blob: 8171ad67f2a27e2e5c756756e9174197ba71d8ee [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;
Christine Franksd154fe52019-01-04 17:17:45 -080022import static android.hardware.display.ColorDisplayManager.COLOR_MODE_AUTOMATIC;
23import static android.hardware.display.ColorDisplayManager.COLOR_MODE_BOOSTED;
24import static android.hardware.display.ColorDisplayManager.COLOR_MODE_NATURAL;
25import static android.hardware.display.ColorDisplayManager.COLOR_MODE_SATURATED;
Christine Franksa4ed3762019-01-24 15:37:04 -080026
Christine Franks245ffd42018-11-16 13:45:14 -080027import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;
Christine Franks39b03112018-07-03 14:46:07 -070028import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
Christine Franks09c229e2018-12-14 10:37:40 -080029import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_SATURATION;
Christine Franks39b03112018-07-03 14:46:07 -070030
Christine Franks09c229e2018-12-14 10:37:40 -080031import android.Manifest;
Justin Klaassen639214e2016-07-14 21:00:06 -070032import android.animation.Animator;
33import android.animation.AnimatorListenerAdapter;
34import android.animation.TypeEvaluator;
35import android.animation.ValueAnimator;
Justin Klaassen4346f632016-08-08 15:01:47 -070036import android.annotation.NonNull;
Justin Klaassen908b86c2016-08-08 09:18:42 -070037import android.annotation.Nullable;
Christine Franksf3529b22019-01-03 13:20:17 -080038import android.annotation.Size;
Christine Franks55194dc2019-01-15 13:47:06 -080039import android.annotation.UserIdInt;
Justin Klaassen911e8892016-06-21 18:24:24 -070040import android.app.AlarmManager;
41import android.content.BroadcastReceiver;
42import android.content.ContentResolver;
43import android.content.Context;
44import android.content.Intent;
45import android.content.IntentFilter;
Christine Franks09c229e2018-12-14 10:37:40 -080046import android.content.pm.PackageManager;
Daniel Solomon8b72c5b2018-11-25 11:07:13 -080047import android.content.res.Resources;
Justin Klaassen2696d992016-07-11 21:26:46 -070048import android.database.ContentObserver;
Daniel Solomon8b72c5b2018-11-25 11:07:13 -080049import android.graphics.ColorSpace;
Christine Franksf3529b22019-01-03 13:20:17 -080050import android.hardware.display.ColorDisplayManager;
Christine Franks83cc5412018-07-03 14:46:07 -070051import android.hardware.display.ColorDisplayManager.AutoMode;
Christine Franksd154fe52019-01-04 17:17:45 -080052import android.hardware.display.ColorDisplayManager.ColorMode;
Christine Franks39b03112018-07-03 14:46:07 -070053import android.hardware.display.IColorDisplayManager;
Christine Franks83cc5412018-07-03 14:46:07 -070054import android.hardware.display.Time;
Justin Klaassen2696d992016-07-11 21:26:46 -070055import android.net.Uri;
Justin Klaassen639214e2016-07-14 21:00:06 -070056import android.opengl.Matrix;
Christine Franks39b03112018-07-03 14:46:07 -070057import android.os.Binder;
Justin Klaassen911e8892016-06-21 18:24:24 -070058import android.os.Handler;
Daniel Solomona4ab5672019-01-22 19:35:55 -080059import android.os.IBinder;
Justin Klaassen911e8892016-06-21 18:24:24 -070060import android.os.Looper;
Christine Franks09c229e2018-12-14 10:37:40 -080061import android.os.Message;
Christine Franksd154fe52019-01-04 17:17:45 -080062import android.os.SystemProperties;
Justin Klaassen911e8892016-06-21 18:24:24 -070063import android.os.UserHandle;
64import android.provider.Settings.Secure;
Christine Franks57fdde82018-07-03 14:46:07 -070065import android.provider.Settings.System;
Justin Klaassen639214e2016-07-14 21:00:06 -070066import android.util.MathUtils;
Justin Klaassen911e8892016-06-21 18:24:24 -070067import android.util.Slog;
Christine Franks55194dc2019-01-15 13:47:06 -080068import android.view.SurfaceControl;
Christine Franksa4ed3762019-01-24 15:37:04 -080069import android.view.SurfaceControl.DisplayPrimaries;
Christine Franks9114f462019-01-04 11:27:30 -080070import android.view.accessibility.AccessibilityManager;
Justin Klaassen639214e2016-07-14 21:00:06 -070071import android.view.animation.AnimationUtils;
Daniel Solomona4ab5672019-01-22 19:35:55 -080072
Christine Franks39b03112018-07-03 14:46:07 -070073import com.android.internal.R;
Christine Franks57fdde82018-07-03 14:46:07 -070074import com.android.internal.annotations.VisibleForTesting;
Christine Franksf3529b22019-01-03 13:20:17 -080075import com.android.internal.util.DumpUtils;
Christine Franks57fdde82018-07-03 14:46:07 -070076import com.android.server.DisplayThread;
Justin Klaassen911e8892016-06-21 18:24:24 -070077import com.android.server.SystemService;
78import com.android.server.twilight.TwilightListener;
79import com.android.server.twilight.TwilightManager;
80import com.android.server.twilight.TwilightState;
Christine Franksa4ed3762019-01-24 15:37:04 -080081
Christine Franksf3529b22019-01-03 13:20:17 -080082import java.io.FileDescriptor;
Daniel Solomon8b72c5b2018-11-25 11:07:13 -080083import java.io.PrintWriter;
Christine Franksf3529b22019-01-03 13:20:17 -080084import java.lang.ref.WeakReference;
Christine Franks57fdde82018-07-03 14:46:07 -070085import java.time.DateTimeException;
86import java.time.Instant;
Christine Franks03213462017-08-25 13:57:26 -070087import java.time.LocalDateTime;
88import java.time.LocalTime;
89import java.time.ZoneId;
Christine Franks57fdde82018-07-03 14:46:07 -070090import java.time.format.DateTimeParseException;
Christine Franks09c229e2018-12-14 10:37:40 -080091import java.util.Arrays;
Christine Franks8ad71492017-10-24 19:04:22 -070092
Justin Klaassen911e8892016-06-21 18:24:24 -070093/**
Christine Franks39b03112018-07-03 14:46:07 -070094 * Controls the display's color transforms.
Justin Klaassen911e8892016-06-21 18:24:24 -070095 */
Christine Franks57fdde82018-07-03 14:46:07 -070096public final class ColorDisplayService extends SystemService {
Justin Klaassen911e8892016-06-21 18:24:24 -070097
Christine Franks5397f032017-11-01 18:35:16 -070098 private static final String TAG = "ColorDisplayService";
Justin Klaassen911e8892016-06-21 18:24:24 -070099
100 /**
Christine Franks7b83b4282017-01-18 14:55:00 -0800101 * The transition time, in milliseconds, for Night Display to turn on/off.
102 */
103 private static final long TRANSITION_DURATION = 3000L;
104
105 /**
Justin Klaassen639214e2016-07-14 21:00:06 -0700106 * The identity matrix, used if one of the given matrices is {@code null}.
107 */
108 private static final float[] MATRIX_IDENTITY = new float[16];
Christine Franks57fdde82018-07-03 14:46:07 -0700109
Justin Klaassen639214e2016-07-14 21:00:06 -0700110 static {
111 Matrix.setIdentityM(MATRIX_IDENTITY, 0);
112 }
113
Christine Franks09c229e2018-12-14 10:37:40 -0800114 private static final int MSG_APPLY_NIGHT_DISPLAY_IMMEDIATE = 0;
115 private static final int MSG_APPLY_NIGHT_DISPLAY_ANIMATED = 1;
116 private static final int MSG_APPLY_GLOBAL_SATURATION = 2;
Christine Franksc7fb9452019-02-04 08:45:33 -0800117 private static final int MSG_APPLY_DISPLAY_WHITE_BALANCE = 3;
Christine Franks09c229e2018-12-14 10:37:40 -0800118
Justin Klaassen639214e2016-07-14 21:00:06 -0700119 /**
Christine Franks83cc5412018-07-03 14:46:07 -0700120 * Return value if a setting has not been set.
121 */
122 private static final int NOT_SET = -1;
123
124 /**
Justin Klaassen639214e2016-07-14 21:00:06 -0700125 * Evaluator used to animate color matrix transitions.
126 */
127 private static final ColorMatrixEvaluator COLOR_MATRIX_EVALUATOR = new ColorMatrixEvaluator();
128
Christine Franks83cc5412018-07-03 14:46:07 -0700129 private final NightDisplayTintController mNightDisplayTintController =
130 new NightDisplayTintController();
Christine Franks245ffd42018-11-16 13:45:14 -0800131
132 private final TintController mDisplayWhiteBalanceTintController = new TintController() {
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800133 // Three chromaticity coordinates per color: X, Y, and Z
134 private final int NUM_VALUES_PER_PRIMARY = 3;
135 // Four colors: red, green, blue, and white
136 private final int NUM_DISPLAY_PRIMARIES_VALS = 4 * NUM_VALUES_PER_PRIMARY;
Christine Franks245ffd42018-11-16 13:45:14 -0800137
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800138 private final Object mLock = new Object();
139 private int mTemperatureMin;
140 private int mTemperatureMax;
141 private int mTemperatureDefault;
142 private float[] mDisplayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
143 private ColorSpace.Rgb mDisplayColorSpaceRGB;
144 private float[] mChromaticAdaptationMatrix;
145 private int mCurrentColorTemperature;
146 private float[] mCurrentColorTemperatureXYZ;
147 private boolean mSetUp = false;
Christine Franks245ffd42018-11-16 13:45:14 -0800148 private float[] mMatrixDisplayWhiteBalance = new float[16];
Christine Franksa4ed3762019-01-24 15:37:04 -0800149 private Boolean mIsAvailable;
Christine Franks245ffd42018-11-16 13:45:14 -0800150
151 @Override
152 public void setUp(Context context, boolean needsLinear) {
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800153 mSetUp = false;
Daniel Solomona4ab5672019-01-22 19:35:55 -0800154 final Resources res = context.getResources();
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800155
Daniel Solomona4ab5672019-01-22 19:35:55 -0800156 ColorSpace.Rgb displayColorSpaceRGB = getDisplayColorSpaceFromSurfaceControl();
157 if (displayColorSpaceRGB == null) {
158 Slog.w(TAG, "Failed to get display color space from SurfaceControl, trying res");
159 displayColorSpaceRGB = getDisplayColorSpaceFromResources(res);
160 if (displayColorSpaceRGB == null) {
161 Slog.e(TAG, "Failed to get display color space from resources");
162 return;
163 }
164 }
165
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800166 final String[] nominalWhiteValues = res.getStringArray(
167 R.array.config_displayWhiteBalanceDisplayNominalWhite);
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800168 float[] displayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
169 for (int i = 0; i < nominalWhiteValues.length; i++) {
170 displayNominalWhiteXYZ[i] = Float.parseFloat(nominalWhiteValues[i]);
171 }
172
173 final int colorTemperatureMin = res.getInteger(
174 R.integer.config_displayWhiteBalanceColorTemperatureMin);
175 if (colorTemperatureMin <= 0) {
Daniel Solomona4ab5672019-01-22 19:35:55 -0800176 Slog.e(TAG, "Display white balance minimum temperature must be greater than 0");
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800177 return;
178 }
179
180 final int colorTemperatureMax = res.getInteger(
181 R.integer.config_displayWhiteBalanceColorTemperatureMax);
182 if (colorTemperatureMax < colorTemperatureMin) {
Daniel Solomona4ab5672019-01-22 19:35:55 -0800183 Slog.e(TAG, "Display white balance max temp must be greater or equal to min");
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800184 return;
185 }
186
187 final int colorTemperature = res.getInteger(
188 R.integer.config_displayWhiteBalanceColorTemperatureDefault);
189
190 synchronized (mLock) {
191 mDisplayColorSpaceRGB = displayColorSpaceRGB;
192 mDisplayNominalWhiteXYZ = displayNominalWhiteXYZ;
193 mTemperatureMin = colorTemperatureMin;
194 mTemperatureMax = colorTemperatureMax;
195 mTemperatureDefault = colorTemperature;
196 mSetUp = true;
197 }
198
199 setMatrix(mTemperatureDefault);
Christine Franks245ffd42018-11-16 13:45:14 -0800200 }
201
202 @Override
203 public float[] getMatrix() {
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800204 return mSetUp && isActivated() ? mMatrixDisplayWhiteBalance : MATRIX_IDENTITY;
Christine Franks245ffd42018-11-16 13:45:14 -0800205 }
206
207 @Override
208 public void setMatrix(int cct) {
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800209 if (!mSetUp) {
210 Slog.w(TAG, "Can't set display white balance temperature: uninitialized");
211 return;
212 }
213
214 if (cct < mTemperatureMin) {
215 Slog.w(TAG, "Requested display color temperature is below allowed minimum");
216 cct = mTemperatureMin;
217 } else if (cct > mTemperatureMax) {
218 Slog.w(TAG, "Requested display color temperature is above allowed maximum");
219 cct = mTemperatureMax;
220 }
221
222 Slog.d(TAG, "setDisplayWhiteBalanceTemperatureMatrix: cct = " + cct);
223
224 synchronized (mLock) {
225 mCurrentColorTemperature = cct;
226
227 // Adapt the display's nominal white point to match the requested CCT value
228 mCurrentColorTemperatureXYZ = ColorSpace.cctToIlluminantdXyz(cct);
229
230 mChromaticAdaptationMatrix =
Christine Franksd154fe52019-01-04 17:17:45 -0800231 ColorSpace.chromaticAdaptation(ColorSpace.Adaptation.BRADFORD,
232 mDisplayNominalWhiteXYZ, mCurrentColorTemperatureXYZ);
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800233
234 // Convert the adaptation matrix to RGB space
235 float[] result = ColorSpace.mul3x3(mChromaticAdaptationMatrix,
236 mDisplayColorSpaceRGB.getTransform());
237 result = ColorSpace.mul3x3(mDisplayColorSpaceRGB.getInverseTransform(), result);
238
239 // Normalize the transform matrix to peak white value in RGB space
240 final float adaptedMaxR = result[0] + result[3] + result[6];
241 final float adaptedMaxG = result[1] + result[4] + result[7];
242 final float adaptedMaxB = result[2] + result[5] + result[8];
243 final float denum = Math.max(Math.max(adaptedMaxR, adaptedMaxG), adaptedMaxB);
244 for (int i = 0; i < result.length; i++) {
245 result[i] /= denum;
246 }
247
248 Matrix.setIdentityM(mMatrixDisplayWhiteBalance, 0);
249 java.lang.System.arraycopy(result, 0, mMatrixDisplayWhiteBalance, 0, 3);
250 java.lang.System.arraycopy(result, 3, mMatrixDisplayWhiteBalance, 4, 3);
251 java.lang.System.arraycopy(result, 6, mMatrixDisplayWhiteBalance, 8, 3);
252 }
Christine Franks245ffd42018-11-16 13:45:14 -0800253 }
254
255 @Override
256 public int getLevel() {
257 return LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;
258 }
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800259
Christine Franksa4ed3762019-01-24 15:37:04 -0800260 @Override
261 public boolean isAvailable(Context context) {
262 if (mIsAvailable == null) {
263 mIsAvailable = ColorDisplayManager.isDisplayWhiteBalanceAvailable(context);
264 }
265 return mIsAvailable;
266 }
267
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800268 /**
269 * Format a given matrix into a string.
270 *
271 * @param matrix the matrix to format
272 * @param cols number of columns in the matrix
273 */
274 private String matrixToString(float[] matrix, int cols) {
275 if (matrix == null || cols <= 0) {
276 Slog.e(TAG, "Invalid arguments when formatting matrix to string");
277 return "";
278 }
279
280 StringBuilder sb = new StringBuilder("");
281 for (int i = 0; i < matrix.length; i++) {
282 if (i % cols == 0) {
283 sb.append("\n ");
284 }
285 sb.append(String.format("%9.6f ", matrix[i]));
286 }
287 return sb.toString();
288 }
289
290 @Override
291 public void dump(PrintWriter pw) {
292 synchronized (mLock) {
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800293 pw.println(" mSetUp = " + mSetUp);
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800294 if (!mSetUp) {
295 return;
296 }
297
298 pw.println(" isActivated = " + isActivated());
299 pw.println(" mTemperatureMin = " + mTemperatureMin);
300 pw.println(" mTemperatureMax = " + mTemperatureMax);
301 pw.println(" mTemperatureDefault = " + mTemperatureDefault);
302 pw.println(" mCurrentColorTemperature = " + mCurrentColorTemperature);
303 pw.println(" mCurrentColorTemperatureXYZ = " +
304 matrixToString(mCurrentColorTemperatureXYZ, 3));
305 pw.println(" mDisplayColorSpaceRGB RGB-to-XYZ = " +
306 matrixToString(mDisplayColorSpaceRGB.getTransform(), 3));
307 pw.println(" mChromaticAdaptationMatrix = " +
308 matrixToString(mChromaticAdaptationMatrix, 3));
309 pw.println(" mDisplayColorSpaceRGB XYZ-to-RGB = " +
310 matrixToString(mDisplayColorSpaceRGB.getInverseTransform(), 3));
311 pw.println(" mMatrixDisplayWhiteBalance = " +
312 matrixToString(mMatrixDisplayWhiteBalance, 4));
313 }
314 }
Daniel Solomona4ab5672019-01-22 19:35:55 -0800315
316 private ColorSpace.Rgb makeRgbColorSpaceFromXYZ(float[] redGreenBlueXYZ, float[] whiteXYZ) {
317 return new ColorSpace.Rgb(
318 "Display Color Space",
319 redGreenBlueXYZ,
320 whiteXYZ,
321 2.2f // gamma, unused for display white balance
322 );
323 }
324
325 private ColorSpace.Rgb getDisplayColorSpaceFromSurfaceControl() {
Dominik Laskowski3316a0a2019-01-25 02:56:41 -0800326 final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
Daniel Solomona4ab5672019-01-22 19:35:55 -0800327 if (displayToken == null) {
328 return null;
329 }
330
331 DisplayPrimaries primaries = SurfaceControl.getDisplayNativePrimaries(displayToken);
332 if (primaries == null || primaries.red == null || primaries.green == null ||
333 primaries.blue == null || primaries.white == null) {
334 return null;
335 }
336
337 return makeRgbColorSpaceFromXYZ(
338 new float[] {
339 primaries.red.X, primaries.red.Y, primaries.red.Z,
340 primaries.green.X, primaries.green.Y, primaries.green.Z,
341 primaries.blue.X, primaries.blue.Y, primaries.blue.Z,
342 },
343 new float[] { primaries.white.X, primaries.white.Y, primaries.white.Z }
344 );
345 }
346
347 private ColorSpace.Rgb getDisplayColorSpaceFromResources(Resources res) {
348 final String[] displayPrimariesValues = res.getStringArray(
349 R.array.config_displayWhiteBalanceDisplayPrimaries);
350 float[] displayRedGreenBlueXYZ =
351 new float[NUM_DISPLAY_PRIMARIES_VALS - NUM_VALUES_PER_PRIMARY];
352 float[] displayWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
353
354 for (int i = 0; i < displayRedGreenBlueXYZ.length; i++) {
355 displayRedGreenBlueXYZ[i] = Float.parseFloat(displayPrimariesValues[i]);
356 }
357
358 for (int i = 0; i < displayWhiteXYZ.length; i++) {
359 displayWhiteXYZ[i] = Float.parseFloat(
360 displayPrimariesValues[displayRedGreenBlueXYZ.length + i]);
361 }
362
363 return makeRgbColorSpaceFromXYZ(displayRedGreenBlueXYZ, displayWhiteXYZ);
364 }
Christine Franks245ffd42018-11-16 13:45:14 -0800365 };
366
Christine Franks09c229e2018-12-14 10:37:40 -0800367 private final TintController mGlobalSaturationTintController = new TintController() {
368
369 private float[] mMatrixGlobalSaturation = new float[16];
370
371 @Override
372 public void setUp(Context context, boolean needsLinear) {
373 }
374
375 @Override
376 public float[] getMatrix() {
377 return Arrays.copyOf(mMatrixGlobalSaturation, mMatrixGlobalSaturation.length);
378 }
379
380 @Override
381 public void setMatrix(int saturationLevel) {
382 if (saturationLevel < 0) {
383 saturationLevel = 0;
384 } else if (saturationLevel > 100) {
385 saturationLevel = 100;
386 }
387 Slog.d(TAG, "Setting saturation level: " + saturationLevel);
388
389 if (saturationLevel == 100) {
Christine Franks6d21d342019-02-07 15:09:03 -0800390 setActivated(false);
Christine Franks09c229e2018-12-14 10:37:40 -0800391 Matrix.setIdentityM(mMatrixGlobalSaturation, 0);
392 } else {
Christine Franks6d21d342019-02-07 15:09:03 -0800393 setActivated(true);
Christine Franks09c229e2018-12-14 10:37:40 -0800394 float saturation = saturationLevel * 0.1f;
395 float desaturation = 1.0f - saturation;
396 float[] luminance = {0.231f * desaturation, 0.715f * desaturation,
Christine Franks83cc5412018-07-03 14:46:07 -0700397 0.072f * desaturation};
Christine Franks09c229e2018-12-14 10:37:40 -0800398 mMatrixGlobalSaturation[0] = luminance[0] + saturation;
399 mMatrixGlobalSaturation[1] = luminance[0];
400 mMatrixGlobalSaturation[2] = luminance[0];
401 mMatrixGlobalSaturation[4] = luminance[1];
402 mMatrixGlobalSaturation[5] = luminance[1] + saturation;
403 mMatrixGlobalSaturation[6] = luminance[1];
404 mMatrixGlobalSaturation[8] = luminance[2];
405 mMatrixGlobalSaturation[9] = luminance[2];
406 mMatrixGlobalSaturation[10] = luminance[2] + saturation;
407 }
408 }
409
410 @Override
411 public int getLevel() {
412 return LEVEL_COLOR_MATRIX_SATURATION;
413 }
Christine Franksa4ed3762019-01-24 15:37:04 -0800414
415 @Override
416 public boolean isAvailable(Context context) {
417 return ColorDisplayManager.isColorTransformAccelerated(context);
418 }
Christine Franks09c229e2018-12-14 10:37:40 -0800419 };
420
Christine Franks9114f462019-01-04 11:27:30 -0800421 /**
422 * Matrix and offset used for converting color to grayscale.
423 */
424 private static final float[] MATRIX_GRAYSCALE = new float[]{
425 .2126f, .2126f, .2126f, 0f,
426 .7152f, .7152f, .7152f, 0f,
427 .0722f, .0722f, .0722f, 0f,
428 0f, 0f, 0f, 1f
429 };
430
431 /**
432 * Matrix and offset used for luminance inversion. Represents a transform from RGB to YIQ color
433 * space, rotation around the Y axis by 180 degrees, transform back to RGB color space, and
434 * subtraction from 1. The last row represents a non-multiplied addition, see surfaceflinger's
435 * ProgramCache for full implementation details.
436 */
Christine Franksd154fe52019-01-04 17:17:45 -0800437 private static final float[] MATRIX_INVERT_COLOR = new float[]{
Christine Franks9114f462019-01-04 11:27:30 -0800438 0.402f, -0.598f, -0.599f, 0f,
439 -1.174f, -0.174f, -1.175f, 0f,
440 -0.228f, -0.228f, 0.772f, 0f,
441 1f, 1f, 1f, 1f
442 };
443
Justin Klaassen2696d992016-07-11 21:26:46 -0700444 private final Handler mHandler;
445
Christine Franksf3529b22019-01-03 13:20:17 -0800446 private final AppSaturationController mAppSaturationController = new AppSaturationController();
447
Justin Klaassen911e8892016-06-21 18:24:24 -0700448 private int mCurrentUser = UserHandle.USER_NULL;
Justin Klaassen2696d992016-07-11 21:26:46 -0700449 private ContentObserver mUserSetupObserver;
Justin Klaassen911e8892016-06-21 18:24:24 -0700450 private boolean mBootCompleted;
451
Christine Franks57fdde82018-07-03 14:46:07 -0700452 private ContentObserver mContentObserver;
Christine Franks57fdde82018-07-03 14:46:07 -0700453
Christine Franks245ffd42018-11-16 13:45:14 -0800454 private DisplayWhiteBalanceListener mDisplayWhiteBalanceListener;
455
Christine Franks57fdde82018-07-03 14:46:07 -0700456 private NightDisplayAutoMode mNightDisplayAutoMode;
Justin Klaassen911e8892016-06-21 18:24:24 -0700457
Christine Franks5397f032017-11-01 18:35:16 -0700458 public ColorDisplayService(Context context) {
Justin Klaassen911e8892016-06-21 18:24:24 -0700459 super(context);
Christine Franks83cc5412018-07-03 14:46:07 -0700460 mHandler = new TintHandler(DisplayThread.get().getLooper());
Justin Klaassen911e8892016-06-21 18:24:24 -0700461 }
462
463 @Override
464 public void onStart() {
Christine Franks39b03112018-07-03 14:46:07 -0700465 publishBinderService(Context.COLOR_DISPLAY_SERVICE, new BinderService());
Christine Franks245ffd42018-11-16 13:45:14 -0800466 publishLocalService(ColorDisplayServiceInternal.class, new ColorDisplayServiceInternal());
Justin Klaassen911e8892016-06-21 18:24:24 -0700467 }
468
469 @Override
Justin Klaassen2696d992016-07-11 21:26:46 -0700470 public void onBootPhase(int phase) {
Christine Frankse5bb03e2017-02-10 17:36:10 -0800471 if (phase >= PHASE_BOOT_COMPLETED) {
Justin Klaassen2696d992016-07-11 21:26:46 -0700472 mBootCompleted = true;
473
474 // Register listeners now that boot is complete.
475 if (mCurrentUser != UserHandle.USER_NULL && mUserSetupObserver == null) {
476 setUp();
477 }
478 }
479 }
480
481 @Override
Justin Klaassen911e8892016-06-21 18:24:24 -0700482 public void onStartUser(int userHandle) {
483 super.onStartUser(userHandle);
484
Justin Klaassen911e8892016-06-21 18:24:24 -0700485 if (mCurrentUser == UserHandle.USER_NULL) {
Justin Klaassen2696d992016-07-11 21:26:46 -0700486 onUserChanged(userHandle);
Justin Klaassen911e8892016-06-21 18:24:24 -0700487 }
488 }
489
490 @Override
491 public void onSwitchUser(int userHandle) {
492 super.onSwitchUser(userHandle);
493
Justin Klaassen2696d992016-07-11 21:26:46 -0700494 onUserChanged(userHandle);
Justin Klaassen911e8892016-06-21 18:24:24 -0700495 }
496
497 @Override
498 public void onStopUser(int userHandle) {
499 super.onStopUser(userHandle);
500
Justin Klaassen911e8892016-06-21 18:24:24 -0700501 if (mCurrentUser == userHandle) {
Justin Klaassen2696d992016-07-11 21:26:46 -0700502 onUserChanged(UserHandle.USER_NULL);
Justin Klaassen911e8892016-06-21 18:24:24 -0700503 }
504 }
505
Justin Klaassen2696d992016-07-11 21:26:46 -0700506 private void onUserChanged(int userHandle) {
507 final ContentResolver cr = getContext().getContentResolver();
Justin Klaassen911e8892016-06-21 18:24:24 -0700508
Justin Klaassen2696d992016-07-11 21:26:46 -0700509 if (mCurrentUser != UserHandle.USER_NULL) {
510 if (mUserSetupObserver != null) {
511 cr.unregisterContentObserver(mUserSetupObserver);
512 mUserSetupObserver = null;
513 } else if (mBootCompleted) {
514 tearDown();
515 }
516 }
517
518 mCurrentUser = userHandle;
519
520 if (mCurrentUser != UserHandle.USER_NULL) {
521 if (!isUserSetupCompleted(cr, mCurrentUser)) {
522 mUserSetupObserver = new ContentObserver(mHandler) {
523 @Override
524 public void onChange(boolean selfChange, Uri uri) {
525 if (isUserSetupCompleted(cr, mCurrentUser)) {
526 cr.unregisterContentObserver(this);
527 mUserSetupObserver = null;
528
529 if (mBootCompleted) {
530 setUp();
531 }
532 }
533 }
534 };
535 cr.registerContentObserver(Secure.getUriFor(Secure.USER_SETUP_COMPLETE),
Christine Franks39b03112018-07-03 14:46:07 -0700536 false /* notifyForDescendants */, mUserSetupObserver, mCurrentUser);
Justin Klaassen2696d992016-07-11 21:26:46 -0700537 } else if (mBootCompleted) {
538 setUp();
Justin Klaassen911e8892016-06-21 18:24:24 -0700539 }
540 }
541 }
542
Justin Klaassen2696d992016-07-11 21:26:46 -0700543 private static boolean isUserSetupCompleted(ContentResolver cr, int userHandle) {
544 return Secure.getIntForUser(cr, Secure.USER_SETUP_COMPLETE, 0, userHandle) == 1;
545 }
546
547 private void setUp() {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700548 Slog.d(TAG, "setUp: currentUser=" + mCurrentUser);
549
Christine Franks57fdde82018-07-03 14:46:07 -0700550 // Listen for external changes to any of the settings.
551 if (mContentObserver == null) {
552 mContentObserver = new ContentObserver(new Handler(DisplayThread.get().getLooper())) {
553 @Override
554 public void onChange(boolean selfChange, Uri uri) {
555 super.onChange(selfChange, uri);
556
557 final String setting = uri == null ? null : uri.getLastPathSegment();
558 if (setting != null) {
559 switch (setting) {
560 case Secure.NIGHT_DISPLAY_ACTIVATED:
Christine Franks83cc5412018-07-03 14:46:07 -0700561 final boolean activated = isNightDisplayActivatedSetting();
562 if (mNightDisplayTintController.isActivatedStateNotSet()
563 || mNightDisplayTintController.isActivated() != activated) {
564 mNightDisplayTintController.onActivated(activated);
565 }
Christine Franks57fdde82018-07-03 14:46:07 -0700566 break;
567 case Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE:
Christine Franks83cc5412018-07-03 14:46:07 -0700568 final int temperature = getNightDisplayColorTemperatureSetting();
569 if (mNightDisplayTintController.getColorTemperature()
570 != temperature) {
571 mNightDisplayTintController
572 .onColorTemperatureChanged(temperature);
573 }
Christine Franks57fdde82018-07-03 14:46:07 -0700574 break;
575 case Secure.NIGHT_DISPLAY_AUTO_MODE:
Christine Franks83cc5412018-07-03 14:46:07 -0700576 onNightDisplayAutoModeChanged(getNightDisplayAutoModeInternal());
Christine Franks57fdde82018-07-03 14:46:07 -0700577 break;
578 case Secure.NIGHT_DISPLAY_CUSTOM_START_TIME:
579 onNightDisplayCustomStartTimeChanged(
Christine Franks83cc5412018-07-03 14:46:07 -0700580 getNightDisplayCustomStartTimeInternal().getLocalTime());
Christine Franks57fdde82018-07-03 14:46:07 -0700581 break;
582 case Secure.NIGHT_DISPLAY_CUSTOM_END_TIME:
583 onNightDisplayCustomEndTimeChanged(
Christine Franks83cc5412018-07-03 14:46:07 -0700584 getNightDisplayCustomEndTimeInternal().getLocalTime());
Christine Franks57fdde82018-07-03 14:46:07 -0700585 break;
586 case System.DISPLAY_COLOR_MODE:
Christine Franks71e003e2019-01-24 14:40:20 -0800587 onDisplayColorModeChanged(getColorModeInternal());
Christine Franks57fdde82018-07-03 14:46:07 -0700588 break;
Christine Franks57fdde82018-07-03 14:46:07 -0700589 case Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED:
Christine Franks9114f462019-01-04 11:27:30 -0800590 onAccessibilityInversionChanged();
591 onAccessibilityActivated();
592 break;
593 case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED:
594 onAccessibilityDaltonizerChanged();
595 onAccessibilityActivated();
596 break;
597 case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER:
598 onAccessibilityDaltonizerChanged();
Christine Franks57fdde82018-07-03 14:46:07 -0700599 break;
Christine Franks245ffd42018-11-16 13:45:14 -0800600 case Secure.DISPLAY_WHITE_BALANCE_ENABLED:
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800601 updateDisplayWhiteBalanceStatus();
Christine Franks245ffd42018-11-16 13:45:14 -0800602 break;
Christine Franks57fdde82018-07-03 14:46:07 -0700603 }
604 }
605 }
606 };
607 }
608 final ContentResolver cr = getContext().getContentResolver();
609 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_ACTIVATED),
610 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
611 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE),
612 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
613 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_AUTO_MODE),
614 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
615 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_START_TIME),
616 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
617 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_END_TIME),
618 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
619 cr.registerContentObserver(System.getUriFor(System.DISPLAY_COLOR_MODE),
620 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
621 cr.registerContentObserver(
622 Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED),
623 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
624 cr.registerContentObserver(
625 Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED),
626 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
Christine Franks9114f462019-01-04 11:27:30 -0800627 cr.registerContentObserver(
628 Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER),
629 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
Christine Franks245ffd42018-11-16 13:45:14 -0800630 cr.registerContentObserver(Secure.getUriFor(Secure.DISPLAY_WHITE_BALANCE_ENABLED),
631 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
Justin Klaassen911e8892016-06-21 18:24:24 -0700632
Christine Frankscf388c22018-05-15 15:48:10 -0700633 // Set the color mode, if valid, and immediately apply the updated tint matrix based on the
634 // existing activated state. This ensures consistency of tint across the color mode change.
Christine Franks71e003e2019-01-24 14:40:20 -0800635 onDisplayColorModeChanged(getColorModeInternal());
Christine Frankscf388c22018-05-15 15:48:10 -0700636
Christine Franksa4ed3762019-01-24 15:37:04 -0800637 if (mNightDisplayTintController.isAvailable(getContext())) {
Christine Franks245ffd42018-11-16 13:45:14 -0800638 // Reset the activated state.
639 mNightDisplayTintController.setActivated(null);
Christine Frankscf388c22018-05-15 15:48:10 -0700640
Christine Franks245ffd42018-11-16 13:45:14 -0800641 // Prepare the night display color transformation matrix.
642 mNightDisplayTintController
643 .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix());
Christine Franks83cc5412018-07-03 14:46:07 -0700644 mNightDisplayTintController.setMatrix(getNightDisplayColorTemperatureSetting());
Christine Franks8ad71492017-10-24 19:04:22 -0700645
Christine Franks245ffd42018-11-16 13:45:14 -0800646 // Initialize the current auto mode.
Christine Franks83cc5412018-07-03 14:46:07 -0700647 onNightDisplayAutoModeChanged(getNightDisplayAutoModeInternal());
Christine Franks6418d0b2017-02-13 09:48:00 -0800648
Christine Franks83cc5412018-07-03 14:46:07 -0700649 // Force the initialization of the current saved activation state.
Christine Franks245ffd42018-11-16 13:45:14 -0800650 if (mNightDisplayTintController.isActivatedStateNotSet()) {
Christine Franks83cc5412018-07-03 14:46:07 -0700651 mNightDisplayTintController.onActivated(isNightDisplayActivatedSetting());
Christine Franks245ffd42018-11-16 13:45:14 -0800652 }
653 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700654
Christine Franksa4ed3762019-01-24 15:37:04 -0800655 if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) {
Christine Franks245ffd42018-11-16 13:45:14 -0800656 // Prepare the display white balance transform matrix.
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800657 mDisplayWhiteBalanceTintController.setUp(getContext(), true /* needsLinear */);
Christine Franks245ffd42018-11-16 13:45:14 -0800658
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800659 updateDisplayWhiteBalanceStatus();
Justin Klaassen911e8892016-06-21 18:24:24 -0700660 }
661 }
662
Justin Klaassen2696d992016-07-11 21:26:46 -0700663 private void tearDown() {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700664 Slog.d(TAG, "tearDown: currentUser=" + mCurrentUser);
665
Christine Franks57fdde82018-07-03 14:46:07 -0700666 getContext().getContentResolver().unregisterContentObserver(mContentObserver);
667
Christine Franksa4ed3762019-01-24 15:37:04 -0800668 if (mNightDisplayTintController.isAvailable(getContext())) {
Christine Franks245ffd42018-11-16 13:45:14 -0800669 if (mNightDisplayAutoMode != null) {
670 mNightDisplayAutoMode.onStop();
671 mNightDisplayAutoMode = null;
672 }
673 mNightDisplayTintController.endAnimator();
Justin Klaassen911e8892016-06-21 18:24:24 -0700674 }
675
Christine Franksa4ed3762019-01-24 15:37:04 -0800676 if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) {
Christine Franks245ffd42018-11-16 13:45:14 -0800677 mDisplayWhiteBalanceTintController.endAnimator();
Justin Klaassen639214e2016-07-14 21:00:06 -0700678 }
Christine Franks6d21d342019-02-07 15:09:03 -0800679
680 if (mGlobalSaturationTintController.isAvailable(getContext())) {
681 mGlobalSaturationTintController.setActivated(null);
682 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700683 }
684
Christine Franks57fdde82018-07-03 14:46:07 -0700685 private void onNightDisplayAutoModeChanged(int autoMode) {
686 Slog.d(TAG, "onNightDisplayAutoModeChanged: autoMode=" + autoMode);
Justin Klaassenec8837a2016-08-23 12:04:42 -0700687
Christine Franks57fdde82018-07-03 14:46:07 -0700688 if (mNightDisplayAutoMode != null) {
689 mNightDisplayAutoMode.onStop();
690 mNightDisplayAutoMode = null;
Justin Klaassen911e8892016-06-21 18:24:24 -0700691 }
692
Christine Franks83cc5412018-07-03 14:46:07 -0700693 if (autoMode == AUTO_MODE_CUSTOM_TIME) {
Christine Franks57fdde82018-07-03 14:46:07 -0700694 mNightDisplayAutoMode = new CustomNightDisplayAutoMode();
Christine Franks83cc5412018-07-03 14:46:07 -0700695 } else if (autoMode == AUTO_MODE_TWILIGHT) {
Christine Franks57fdde82018-07-03 14:46:07 -0700696 mNightDisplayAutoMode = new TwilightNightDisplayAutoMode();
Justin Klaassen911e8892016-06-21 18:24:24 -0700697 }
698
Christine Franks57fdde82018-07-03 14:46:07 -0700699 if (mNightDisplayAutoMode != null) {
700 mNightDisplayAutoMode.onStart();
Justin Klaassen911e8892016-06-21 18:24:24 -0700701 }
702 }
703
Christine Franks57fdde82018-07-03 14:46:07 -0700704 private void onNightDisplayCustomStartTimeChanged(LocalTime startTime) {
705 Slog.d(TAG, "onNightDisplayCustomStartTimeChanged: startTime=" + startTime);
Justin Klaassenec8837a2016-08-23 12:04:42 -0700706
Christine Franks57fdde82018-07-03 14:46:07 -0700707 if (mNightDisplayAutoMode != null) {
708 mNightDisplayAutoMode.onCustomStartTimeChanged(startTime);
Justin Klaassen911e8892016-06-21 18:24:24 -0700709 }
710 }
711
Christine Franks57fdde82018-07-03 14:46:07 -0700712 private void onNightDisplayCustomEndTimeChanged(LocalTime endTime) {
713 Slog.d(TAG, "onNightDisplayCustomEndTimeChanged: endTime=" + endTime);
Justin Klaassenec8837a2016-08-23 12:04:42 -0700714
Christine Franks57fdde82018-07-03 14:46:07 -0700715 if (mNightDisplayAutoMode != null) {
716 mNightDisplayAutoMode.onCustomEndTimeChanged(endTime);
Justin Klaassen911e8892016-06-21 18:24:24 -0700717 }
718 }
719
Christine Franks57fdde82018-07-03 14:46:07 -0700720 private void onDisplayColorModeChanged(int mode) {
Christine Franks83cc5412018-07-03 14:46:07 -0700721 if (mode == NOT_SET) {
Christine Frankscf388c22018-05-15 15:48:10 -0700722 return;
723 }
724
Christine Franks245ffd42018-11-16 13:45:14 -0800725 mNightDisplayTintController.cancelAnimator();
726 mDisplayWhiteBalanceTintController.cancelAnimator();
727
Christine Franksa4ed3762019-01-24 15:37:04 -0800728 if (mNightDisplayTintController.isAvailable(getContext())) {
729 mNightDisplayTintController
730 .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
731 mNightDisplayTintController.setMatrix(getNightDisplayColorTemperatureSetting());
732 }
Christine Franks245ffd42018-11-16 13:45:14 -0800733
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800734 updateDisplayWhiteBalanceStatus();
Christine Franks8ad71492017-10-24 19:04:22 -0700735
Christine Franks218e6562017-11-27 10:20:14 -0800736 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
Christine Franks245ffd42018-11-16 13:45:14 -0800737 dtm.setColorMode(mode, mNightDisplayTintController.getMatrix());
Christine Franks8ad71492017-10-24 19:04:22 -0700738 }
739
Christine Franks9114f462019-01-04 11:27:30 -0800740 private void onAccessibilityActivated() {
Christine Franks71e003e2019-01-24 14:40:20 -0800741 onDisplayColorModeChanged(getColorModeInternal());
Daniel Solomon317a3572018-03-30 18:36:37 -0700742 }
743
Christine Franks9114f462019-01-04 11:27:30 -0800744 /**
745 * Apply the accessibility daltonizer transform based on the settings value.
746 */
747 private void onAccessibilityDaltonizerChanged() {
748 final boolean enabled = Secure.getIntForUser(getContext().getContentResolver(),
749 Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, mCurrentUser) != 0;
750 final int daltonizerMode = enabled ? Secure.getIntForUser(getContext().getContentResolver(),
751 Secure.ACCESSIBILITY_DISPLAY_DALTONIZER,
752 AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY, mCurrentUser)
753 : AccessibilityManager.DALTONIZER_DISABLED;
754
755 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
756 if (daltonizerMode == AccessibilityManager.DALTONIZER_SIMULATE_MONOCHROMACY) {
757 // Monochromacy isn't supported by the native Daltonizer implementation; use grayscale.
758 dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_GRAYSCALE,
759 MATRIX_GRAYSCALE);
760 dtm.setDaltonizerMode(AccessibilityManager.DALTONIZER_DISABLED);
761 } else {
762 dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_GRAYSCALE, null);
763 dtm.setDaltonizerMode(daltonizerMode);
764 }
765 }
766
767 /**
768 * Apply the accessibility inversion transform based on the settings value.
769 */
770 private void onAccessibilityInversionChanged() {
771 final boolean enabled = Secure.getIntForUser(getContext().getContentResolver(),
772 Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, mCurrentUser) != 0;
773 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
774 dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_INVERT_COLOR,
775 enabled ? MATRIX_INVERT_COLOR : null);
776 }
Christine Franks8ad71492017-10-24 19:04:22 -0700777
Christine Franks6418d0b2017-02-13 09:48:00 -0800778 /**
779 * Applies current color temperature matrix, or removes it if deactivated.
780 *
781 * @param immediate {@code true} skips transition animation
782 */
Christine Franks245ffd42018-11-16 13:45:14 -0800783 private void applyTint(TintController tintController, boolean immediate) {
784 tintController.cancelAnimator();
Christine Franks6418d0b2017-02-13 09:48:00 -0800785
Christine Franks6418d0b2017-02-13 09:48:00 -0800786 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
Christine Franks245ffd42018-11-16 13:45:14 -0800787 final float[] from = dtm.getColorMatrix(tintController.getLevel());
788 final float[] to = tintController.getMatrix();
Christine Franks6418d0b2017-02-13 09:48:00 -0800789
790 if (immediate) {
Christine Franks245ffd42018-11-16 13:45:14 -0800791 dtm.setColorMatrix(tintController.getLevel(), to);
Christine Franks6418d0b2017-02-13 09:48:00 -0800792 } else {
Christine Franks245ffd42018-11-16 13:45:14 -0800793 tintController.setAnimator(ValueAnimator.ofObject(COLOR_MATRIX_EVALUATOR,
794 from == null ? MATRIX_IDENTITY : from, to));
795 tintController.getAnimator().setDuration(TRANSITION_DURATION);
796 tintController.getAnimator().setInterpolator(AnimationUtils.loadInterpolator(
Christine Franks6418d0b2017-02-13 09:48:00 -0800797 getContext(), android.R.interpolator.fast_out_slow_in));
Christine Franks245ffd42018-11-16 13:45:14 -0800798 tintController.getAnimator().addUpdateListener((ValueAnimator animator) -> {
799 final float[] value = (float[]) animator.getAnimatedValue();
800 dtm.setColorMatrix(tintController.getLevel(), value);
Christine Franks6418d0b2017-02-13 09:48:00 -0800801 });
Christine Franks245ffd42018-11-16 13:45:14 -0800802 tintController.getAnimator().addListener(new AnimatorListenerAdapter() {
Christine Franks6418d0b2017-02-13 09:48:00 -0800803
804 private boolean mIsCancelled;
805
806 @Override
807 public void onAnimationCancel(Animator animator) {
808 mIsCancelled = true;
809 }
810
811 @Override
812 public void onAnimationEnd(Animator animator) {
813 if (!mIsCancelled) {
814 // Ensure final color matrix is set at the end of the animation. If the
815 // animation is cancelled then don't set the final color matrix so the new
816 // animator can pick up from where this one left off.
Christine Franks245ffd42018-11-16 13:45:14 -0800817 dtm.setColorMatrix(tintController.getLevel(), to);
Christine Franks6418d0b2017-02-13 09:48:00 -0800818 }
Christine Franks245ffd42018-11-16 13:45:14 -0800819 tintController.setAnimator(null);
Christine Franks6418d0b2017-02-13 09:48:00 -0800820 }
821 });
Christine Franks245ffd42018-11-16 13:45:14 -0800822 tintController.getAnimator().start();
Christine Franks6418d0b2017-02-13 09:48:00 -0800823 }
824 }
825
826 /**
Christine Franks39b03112018-07-03 14:46:07 -0700827 * Returns the first date time corresponding to the local time that occurs before the provided
828 * date time.
Christine Franks03213462017-08-25 13:57:26 -0700829 *
830 * @param compareTime the LocalDateTime to compare against
831 * @return the prior LocalDateTime corresponding to this local time
832 */
Christine Franks57fdde82018-07-03 14:46:07 -0700833 @VisibleForTesting
834 static LocalDateTime getDateTimeBefore(LocalTime localTime, LocalDateTime compareTime) {
Christine Franks03213462017-08-25 13:57:26 -0700835 final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
836 compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
837
838 // Check if the local time has passed, if so return the same time yesterday.
839 return ldt.isAfter(compareTime) ? ldt.minusDays(1) : ldt;
840 }
841
842 /**
Christine Franks39b03112018-07-03 14:46:07 -0700843 * Returns the first date time corresponding to this local time that occurs after the provided
844 * date time.
Christine Franks03213462017-08-25 13:57:26 -0700845 *
846 * @param compareTime the LocalDateTime to compare against
847 * @return the next LocalDateTime corresponding to this local time
848 */
Christine Franks57fdde82018-07-03 14:46:07 -0700849 @VisibleForTesting
850 static LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) {
Christine Franks03213462017-08-25 13:57:26 -0700851 final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
852 compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
853
854 // Check if the local time has passed, if so return the same time tomorrow.
855 return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
856 }
857
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800858 private void updateDisplayWhiteBalanceStatus() {
859 boolean oldActivated = mDisplayWhiteBalanceTintController.isActivated();
860 mDisplayWhiteBalanceTintController.setActivated(isDisplayWhiteBalanceSettingEnabled() &&
861 !mNightDisplayTintController.isActivated() &&
862 DisplayTransformManager.needsLinearColorMatrix());
863 boolean activated = mDisplayWhiteBalanceTintController.isActivated();
864
865 if (mDisplayWhiteBalanceListener != null && oldActivated != activated) {
866 mDisplayWhiteBalanceListener.onDisplayWhiteBalanceStatusChanged(activated);
Christine Franks245ffd42018-11-16 13:45:14 -0800867 }
Daniel Solomon86508f82019-01-18 19:01:02 -0800868
869 // If disabled, clear the tint. If enabled, do nothing more here and let the next
870 // temperature update set the correct tint.
871 if (!activated) {
Christine Franksc7fb9452019-02-04 08:45:33 -0800872 mHandler.sendEmptyMessage(MSG_APPLY_DISPLAY_WHITE_BALANCE);
Daniel Solomon86508f82019-01-18 19:01:02 -0800873 }
Christine Franks245ffd42018-11-16 13:45:14 -0800874 }
875
876 private boolean isDisplayWhiteBalanceSettingEnabled() {
877 return Secure.getIntForUser(getContext().getContentResolver(),
878 Secure.DISPLAY_WHITE_BALANCE_ENABLED, 0, mCurrentUser) == 1;
879 }
880
Christine Franks39b03112018-07-03 14:46:07 -0700881 private boolean isDeviceColorManagedInternal() {
882 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
883 return dtm.isDeviceColorManaged();
884 }
885
Christine Franks55194dc2019-01-15 13:47:06 -0800886 private int getTransformCapabilitiesInternal() {
887 int availabilityFlags = ColorDisplayManager.CAPABILITY_NONE;
888 if (SurfaceControl.getProtectedContentSupport()) {
889 availabilityFlags |= ColorDisplayManager.CAPABILITY_PROTECTED_CONTENT;
890 }
891 final Resources res = getContext().getResources();
892 if (res.getBoolean(R.bool.config_setColorTransformAccelerated)) {
893 availabilityFlags |= ColorDisplayManager.CAPABILITY_HARDWARE_ACCELERATION_GLOBAL;
894 }
895 if (res.getBoolean(R.bool.config_setColorTransformAcceleratedPerLayer)) {
896 availabilityFlags |= ColorDisplayManager.CAPABILITY_HARDWARE_ACCELERATION_PER_APP;
897 }
898 return availabilityFlags;
899 }
900
Christine Franks83cc5412018-07-03 14:46:07 -0700901 private boolean setNightDisplayAutoModeInternal(@AutoMode int autoMode) {
902 if (getNightDisplayAutoModeInternal() != autoMode) {
903 Secure.putStringForUser(getContext().getContentResolver(),
904 Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
905 null,
906 mCurrentUser);
907 }
908 return Secure.putIntForUser(getContext().getContentResolver(),
909 Secure.NIGHT_DISPLAY_AUTO_MODE, autoMode, mCurrentUser);
910 }
911
912 private int getNightDisplayAutoModeInternal() {
913 int autoMode = getNightDisplayAutoModeRawInternal();
914 if (autoMode == NOT_SET) {
915 autoMode = getContext().getResources().getInteger(
916 R.integer.config_defaultNightDisplayAutoMode);
917 }
918 if (autoMode != AUTO_MODE_DISABLED
919 && autoMode != AUTO_MODE_CUSTOM_TIME
920 && autoMode != AUTO_MODE_TWILIGHT) {
921 Slog.e(TAG, "Invalid autoMode: " + autoMode);
922 autoMode = AUTO_MODE_DISABLED;
923 }
924 return autoMode;
925 }
926
927 private int getNightDisplayAutoModeRawInternal() {
928 return Secure
929 .getIntForUser(getContext().getContentResolver(), Secure.NIGHT_DISPLAY_AUTO_MODE,
930 NOT_SET, mCurrentUser);
931 }
932
933 private Time getNightDisplayCustomStartTimeInternal() {
934 int startTimeValue = Secure.getIntForUser(getContext().getContentResolver(),
935 Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, NOT_SET, mCurrentUser);
936 if (startTimeValue == NOT_SET) {
937 startTimeValue = getContext().getResources().getInteger(
938 R.integer.config_defaultNightDisplayCustomStartTime);
939 }
940 return new Time(LocalTime.ofSecondOfDay(startTimeValue / 1000));
941 }
942
943 private boolean setNightDisplayCustomStartTimeInternal(Time startTime) {
944 return Secure.putIntForUser(getContext().getContentResolver(),
945 Secure.NIGHT_DISPLAY_CUSTOM_START_TIME,
946 startTime.getLocalTime().toSecondOfDay() * 1000,
947 mCurrentUser);
948 }
949
950 private Time getNightDisplayCustomEndTimeInternal() {
951 int endTimeValue = Secure.getIntForUser(getContext().getContentResolver(),
952 Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, NOT_SET, mCurrentUser);
953 if (endTimeValue == NOT_SET) {
954 endTimeValue = getContext().getResources().getInteger(
955 R.integer.config_defaultNightDisplayCustomEndTime);
956 }
957 return new Time(LocalTime.ofSecondOfDay(endTimeValue / 1000));
958 }
959
960 private boolean setNightDisplayCustomEndTimeInternal(Time endTime) {
961 return Secure.putIntForUser(getContext().getContentResolver(),
962 Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, endTime.getLocalTime().toSecondOfDay() * 1000,
963 mCurrentUser);
964 }
965
Christine Franks57fdde82018-07-03 14:46:07 -0700966 /**
967 * Returns the last time the night display transform activation state was changed, or {@link
968 * LocalDateTime#MIN} if night display has never been activated.
969 */
Christine Franks245ffd42018-11-16 13:45:14 -0800970 private LocalDateTime getNightDisplayLastActivatedTimeSetting() {
Christine Franks57fdde82018-07-03 14:46:07 -0700971 final ContentResolver cr = getContext().getContentResolver();
972 final String lastActivatedTime = Secure.getStringForUser(
973 cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, getContext().getUserId());
974 if (lastActivatedTime != null) {
975 try {
976 return LocalDateTime.parse(lastActivatedTime);
977 } catch (DateTimeParseException ignored) {
978 }
979 // Uses the old epoch time.
980 try {
981 return LocalDateTime.ofInstant(
982 Instant.ofEpochMilli(Long.parseLong(lastActivatedTime)),
983 ZoneId.systemDefault());
984 } catch (DateTimeException | NumberFormatException ignored) {
985 }
986 }
987 return LocalDateTime.MIN;
988 }
989
Christine Franksf3529b22019-01-03 13:20:17 -0800990 private boolean setAppSaturationLevelInternal(String packageName, int saturationLevel) {
991 return mAppSaturationController
992 .setSaturationLevel(packageName, mCurrentUser, saturationLevel);
993 }
994
Christine Franksd154fe52019-01-04 17:17:45 -0800995 private void setColorModeInternal(@ColorMode int colorMode) {
996 if (!isColorModeAvailable(colorMode)) {
997 throw new IllegalArgumentException("Invalid colorMode: " + colorMode);
998 }
999 System.putIntForUser(getContext().getContentResolver(), System.DISPLAY_COLOR_MODE,
1000 colorMode,
1001 mCurrentUser);
1002 }
1003
Christine Franks71e003e2019-01-24 14:40:20 -08001004 private @ColorMode int getColorModeInternal() {
Christine Franksd154fe52019-01-04 17:17:45 -08001005 final ContentResolver cr = getContext().getContentResolver();
1006 if (Secure.getIntForUser(cr, Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED,
1007 0, mCurrentUser) == 1
1008 || Secure.getIntForUser(cr, Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
1009 0, mCurrentUser) == 1) {
1010 // There are restrictions on the available color modes combined with a11y transforms.
1011 if (isColorModeAvailable(COLOR_MODE_SATURATED)) {
1012 return COLOR_MODE_SATURATED;
1013 } else if (isColorModeAvailable(COLOR_MODE_AUTOMATIC)) {
1014 return COLOR_MODE_AUTOMATIC;
1015 }
1016 }
1017
1018 int colorMode = System.getIntForUser(cr, System.DISPLAY_COLOR_MODE, -1, mCurrentUser);
1019 if (colorMode == -1) {
1020 // There might be a system property controlling color mode that we need to respect; if
1021 // not, this will set a suitable default.
1022 colorMode = getCurrentColorModeFromSystemProperties();
1023 }
1024
1025 // This happens when a color mode is no longer available (e.g., after system update or B&R)
1026 // or the device does not support any color mode.
1027 if (!isColorModeAvailable(colorMode)) {
1028 if (colorMode == COLOR_MODE_BOOSTED && isColorModeAvailable(COLOR_MODE_NATURAL)) {
1029 colorMode = COLOR_MODE_NATURAL;
1030 } else if (colorMode == COLOR_MODE_SATURATED
1031 && isColorModeAvailable(COLOR_MODE_AUTOMATIC)) {
1032 colorMode = COLOR_MODE_AUTOMATIC;
1033 } else if (colorMode == COLOR_MODE_AUTOMATIC
1034 && isColorModeAvailable(COLOR_MODE_SATURATED)) {
1035 colorMode = COLOR_MODE_SATURATED;
1036 } else {
1037 colorMode = -1;
1038 }
1039 }
1040
1041 return colorMode;
1042 }
1043
1044 /**
1045 * Get the current color mode from system properties, or return -1 if invalid.
1046 *
1047 * See {@link com.android.server.display.DisplayTransformManager}
1048 */
1049 private @ColorMode
1050 int getCurrentColorModeFromSystemProperties() {
1051 final int displayColorSetting = SystemProperties.getInt("persist.sys.sf.native_mode", 0);
1052 if (displayColorSetting == 0) {
1053 return "1.0".equals(SystemProperties.get("persist.sys.sf.color_saturation"))
1054 ? COLOR_MODE_NATURAL : COLOR_MODE_BOOSTED;
1055 } else if (displayColorSetting == 1) {
1056 return COLOR_MODE_SATURATED;
1057 } else if (displayColorSetting == 2) {
1058 return COLOR_MODE_AUTOMATIC;
1059 } else {
1060 return -1;
1061 }
1062 }
1063
1064 private boolean isColorModeAvailable(@ColorMode int colorMode) {
1065 final int[] availableColorModes = getContext().getResources().getIntArray(
1066 R.array.config_availableColorModes);
1067 if (availableColorModes != null) {
1068 for (int mode : availableColorModes) {
1069 if (mode == colorMode) {
1070 return true;
1071 }
1072 }
1073 }
1074 return false;
1075 }
1076
Christine Franksf3529b22019-01-03 13:20:17 -08001077 private void dumpInternal(PrintWriter pw) {
1078 pw.println("COLOR DISPLAY MANAGER dumpsys (color_display)");
Christine Franksa4ed3762019-01-24 15:37:04 -08001079
Christine Franksf3529b22019-01-03 13:20:17 -08001080 pw.println("Night Display:");
Christine Franksa4ed3762019-01-24 15:37:04 -08001081 if (mNightDisplayTintController.isAvailable(getContext())) {
Christine Franksf3529b22019-01-03 13:20:17 -08001082 pw.println(" Activated: " + mNightDisplayTintController.isActivated());
Christine Franksa4ed3762019-01-24 15:37:04 -08001083 pw.println(" Color temp: " + mNightDisplayTintController.getColorTemperature());
Christine Franksf3529b22019-01-03 13:20:17 -08001084 } else {
1085 pw.println(" Not available");
1086 }
Christine Franksa4ed3762019-01-24 15:37:04 -08001087
1088 pw.println("Global saturation:");
1089 if (mGlobalSaturationTintController.isAvailable(getContext())) {
1090 pw.println(" Activated: " + mGlobalSaturationTintController.isActivated());
1091 } else {
1092 pw.println(" Not available");
1093 }
1094
Christine Franksf3529b22019-01-03 13:20:17 -08001095 mAppSaturationController.dump(pw);
Christine Franksa4ed3762019-01-24 15:37:04 -08001096
1097 pw.println("Display white balance:");
1098 if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) {
1099 pw.println(" Activated: " + mDisplayWhiteBalanceTintController.isActivated());
1100 } else {
1101 pw.println(" Not available");
1102 }
1103
1104 pw.println("Color mode: " + getColorModeInternal());
Christine Franksf3529b22019-01-03 13:20:17 -08001105 }
1106
Christine Franks83cc5412018-07-03 14:46:07 -07001107 private boolean isNightDisplayActivatedSetting() {
1108 return Secure.getIntForUser(getContext().getContentResolver(),
1109 Secure.NIGHT_DISPLAY_ACTIVATED, 0, mCurrentUser) == 1;
1110 }
1111
1112 private int getNightDisplayColorTemperatureSetting() {
1113 return clampNightDisplayColorTemperature(Secure.getIntForUser(
1114 getContext().getContentResolver(), Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, NOT_SET,
1115 mCurrentUser));
1116 }
1117
1118 private int clampNightDisplayColorTemperature(int colorTemperature) {
1119 if (colorTemperature == NOT_SET) {
1120 colorTemperature = getContext().getResources().getInteger(
1121 R.integer.config_nightDisplayColorTemperatureDefault);
1122 }
1123 final int minimumTemperature = ColorDisplayManager.getMinimumColorTemperature(getContext());
1124 final int maximumTemperature = ColorDisplayManager.getMaximumColorTemperature(getContext());
1125 if (colorTemperature < minimumTemperature) {
1126 colorTemperature = minimumTemperature;
1127 } else if (colorTemperature > maximumTemperature) {
1128 colorTemperature = maximumTemperature;
1129 }
1130
1131 return colorTemperature;
1132 }
1133
Christine Franks57fdde82018-07-03 14:46:07 -07001134 private abstract class NightDisplayAutoMode {
1135
1136 public abstract void onActivated(boolean activated);
1137
Justin Klaassen911e8892016-06-21 18:24:24 -07001138 public abstract void onStart();
Christine Frankse5bb03e2017-02-10 17:36:10 -08001139
Justin Klaassen911e8892016-06-21 18:24:24 -07001140 public abstract void onStop();
Christine Franks57fdde82018-07-03 14:46:07 -07001141
1142 public void onCustomStartTimeChanged(LocalTime startTime) {
1143 }
1144
1145 public void onCustomEndTimeChanged(LocalTime endTime) {
1146 }
Justin Klaassen911e8892016-06-21 18:24:24 -07001147 }
1148
Christine Franks57fdde82018-07-03 14:46:07 -07001149 private final class CustomNightDisplayAutoMode extends NightDisplayAutoMode implements
1150 AlarmManager.OnAlarmListener {
Justin Klaassen911e8892016-06-21 18:24:24 -07001151
1152 private final AlarmManager mAlarmManager;
1153 private final BroadcastReceiver mTimeChangedReceiver;
1154
Christine Franks03213462017-08-25 13:57:26 -07001155 private LocalTime mStartTime;
1156 private LocalTime mEndTime;
Justin Klaassen911e8892016-06-21 18:24:24 -07001157
Christine Franks03213462017-08-25 13:57:26 -07001158 private LocalDateTime mLastActivatedTime;
Justin Klaassen911e8892016-06-21 18:24:24 -07001159
Christine Franks57fdde82018-07-03 14:46:07 -07001160 CustomNightDisplayAutoMode() {
Justin Klaassen911e8892016-06-21 18:24:24 -07001161 mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
1162 mTimeChangedReceiver = new BroadcastReceiver() {
1163 @Override
1164 public void onReceive(Context context, Intent intent) {
1165 updateActivated();
1166 }
1167 };
1168 }
1169
1170 private void updateActivated() {
Christine Franks03213462017-08-25 13:57:26 -07001171 final LocalDateTime now = LocalDateTime.now();
1172 final LocalDateTime start = getDateTimeBefore(mStartTime, now);
1173 final LocalDateTime end = getDateTimeAfter(mEndTime, start);
1174 boolean activate = now.isBefore(end);
Justin Klaassen911e8892016-06-21 18:24:24 -07001175
Christine Frankse5bb03e2017-02-10 17:36:10 -08001176 if (mLastActivatedTime != null) {
Christine Frankse5bb03e2017-02-10 17:36:10 -08001177 // Maintain the existing activated state if within the current period.
Christine Franks03213462017-08-25 13:57:26 -07001178 if (mLastActivatedTime.isBefore(now) && mLastActivatedTime.isAfter(start)
1179 && (mLastActivatedTime.isAfter(end) || now.isBefore(end))) {
Christine Franks83cc5412018-07-03 14:46:07 -07001180 activate = isNightDisplayActivatedSetting();
Justin Klaassen911e8892016-06-21 18:24:24 -07001181 }
1182 }
1183
Christine Franks245ffd42018-11-16 13:45:14 -08001184 if (mNightDisplayTintController.isActivatedStateNotSet() || (
1185 mNightDisplayTintController.isActivated() != activate)) {
Christine Franks83cc5412018-07-03 14:46:07 -07001186 mNightDisplayTintController.setActivated(activate);
Justin Klaassen911e8892016-06-21 18:24:24 -07001187 }
Christine Franks03213462017-08-25 13:57:26 -07001188
Christine Franks245ffd42018-11-16 13:45:14 -08001189 updateNextAlarm(mNightDisplayTintController.isActivated(), now);
Justin Klaassen911e8892016-06-21 18:24:24 -07001190 }
1191
Christine Franks03213462017-08-25 13:57:26 -07001192 private void updateNextAlarm(@Nullable Boolean activated, @NonNull LocalDateTime now) {
Justin Klaassen4346f632016-08-08 15:01:47 -07001193 if (activated != null) {
Christine Franks03213462017-08-25 13:57:26 -07001194 final LocalDateTime next = activated ? getDateTimeAfter(mEndTime, now)
1195 : getDateTimeAfter(mStartTime, now);
1196 final long millis = next.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
1197 mAlarmManager.setExact(AlarmManager.RTC, millis, TAG, this, null);
Justin Klaassen911e8892016-06-21 18:24:24 -07001198 }
1199 }
1200
1201 @Override
1202 public void onStart() {
1203 final IntentFilter intentFilter = new IntentFilter(Intent.ACTION_TIME_CHANGED);
1204 intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
1205 getContext().registerReceiver(mTimeChangedReceiver, intentFilter);
1206
Christine Franks83cc5412018-07-03 14:46:07 -07001207 mStartTime = getNightDisplayCustomStartTimeInternal().getLocalTime();
1208 mEndTime = getNightDisplayCustomEndTimeInternal().getLocalTime();
Justin Klaassen911e8892016-06-21 18:24:24 -07001209
Christine Franks57fdde82018-07-03 14:46:07 -07001210 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
Christine Frankse5bb03e2017-02-10 17:36:10 -08001211
Justin Klaassen911e8892016-06-21 18:24:24 -07001212 // Force an update to initialize state.
1213 updateActivated();
1214 }
1215
1216 @Override
1217 public void onStop() {
1218 getContext().unregisterReceiver(mTimeChangedReceiver);
1219
1220 mAlarmManager.cancel(this);
1221 mLastActivatedTime = null;
1222 }
1223
1224 @Override
1225 public void onActivated(boolean activated) {
Christine Franks57fdde82018-07-03 14:46:07 -07001226 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
Christine Franks03213462017-08-25 13:57:26 -07001227 updateNextAlarm(activated, LocalDateTime.now());
Justin Klaassen911e8892016-06-21 18:24:24 -07001228 }
1229
1230 @Override
Christine Franks03213462017-08-25 13:57:26 -07001231 public void onCustomStartTimeChanged(LocalTime startTime) {
Justin Klaassen911e8892016-06-21 18:24:24 -07001232 mStartTime = startTime;
1233 mLastActivatedTime = null;
1234 updateActivated();
1235 }
1236
1237 @Override
Christine Franks03213462017-08-25 13:57:26 -07001238 public void onCustomEndTimeChanged(LocalTime endTime) {
Justin Klaassen911e8892016-06-21 18:24:24 -07001239 mEndTime = endTime;
1240 mLastActivatedTime = null;
1241 updateActivated();
1242 }
1243
1244 @Override
1245 public void onAlarm() {
Justin Klaassenec8837a2016-08-23 12:04:42 -07001246 Slog.d(TAG, "onAlarm");
Justin Klaassen911e8892016-06-21 18:24:24 -07001247 updateActivated();
1248 }
1249 }
1250
Christine Franks57fdde82018-07-03 14:46:07 -07001251 private final class TwilightNightDisplayAutoMode extends NightDisplayAutoMode implements
1252 TwilightListener {
Justin Klaassen911e8892016-06-21 18:24:24 -07001253
1254 private final TwilightManager mTwilightManager;
Christine Franks57fdde82018-07-03 14:46:07 -07001255 private LocalDateTime mLastActivatedTime;
Justin Klaassen911e8892016-06-21 18:24:24 -07001256
Christine Franks57fdde82018-07-03 14:46:07 -07001257 TwilightNightDisplayAutoMode() {
Justin Klaassen911e8892016-06-21 18:24:24 -07001258 mTwilightManager = getLocalService(TwilightManager.class);
Justin Klaassen911e8892016-06-21 18:24:24 -07001259 }
1260
Justin Klaassen908b86c2016-08-08 09:18:42 -07001261 private void updateActivated(TwilightState state) {
Justin Klaassen3da4c442017-05-05 15:19:33 -07001262 if (state == null) {
1263 // If there isn't a valid TwilightState then just keep the current activated
1264 // state.
1265 return;
1266 }
1267
1268 boolean activate = state.isNight();
Christine Franks57fdde82018-07-03 14:46:07 -07001269 if (mLastActivatedTime != null) {
Christine Franks03213462017-08-25 13:57:26 -07001270 final LocalDateTime now = LocalDateTime.now();
1271 final LocalDateTime sunrise = state.sunrise();
1272 final LocalDateTime sunset = state.sunset();
Christine Frankse5bb03e2017-02-10 17:36:10 -08001273 // Maintain the existing activated state if within the current period.
Christine Franks57fdde82018-07-03 14:46:07 -07001274 if (mLastActivatedTime.isBefore(now) && (mLastActivatedTime.isBefore(sunrise)
1275 ^ mLastActivatedTime.isBefore(sunset))) {
Christine Franks83cc5412018-07-03 14:46:07 -07001276 activate = isNightDisplayActivatedSetting();
Justin Klaassen911e8892016-06-21 18:24:24 -07001277 }
1278 }
Justin Klaassen908b86c2016-08-08 09:18:42 -07001279
Christine Franks245ffd42018-11-16 13:45:14 -08001280 if (mNightDisplayTintController.isActivatedStateNotSet() || (
1281 mNightDisplayTintController.isActivated() != activate)) {
Christine Franks83cc5412018-07-03 14:46:07 -07001282 mNightDisplayTintController.setActivated(activate);
Justin Klaassen908b86c2016-08-08 09:18:42 -07001283 }
Justin Klaassen911e8892016-06-21 18:24:24 -07001284 }
1285
1286 @Override
Christine Franks57fdde82018-07-03 14:46:07 -07001287 public void onActivated(boolean activated) {
1288 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
1289 }
1290
1291 @Override
Justin Klaassen911e8892016-06-21 18:24:24 -07001292 public void onStart() {
1293 mTwilightManager.registerListener(this, mHandler);
Christine Franks57fdde82018-07-03 14:46:07 -07001294 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
Justin Klaassen911e8892016-06-21 18:24:24 -07001295
1296 // Force an update to initialize state.
Justin Klaassen908b86c2016-08-08 09:18:42 -07001297 updateActivated(mTwilightManager.getLastTwilightState());
Justin Klaassen911e8892016-06-21 18:24:24 -07001298 }
1299
1300 @Override
1301 public void onStop() {
1302 mTwilightManager.unregisterListener(this);
Christine Franks57fdde82018-07-03 14:46:07 -07001303 mLastActivatedTime = null;
Justin Klaassen908b86c2016-08-08 09:18:42 -07001304 }
1305
1306 @Override
1307 public void onTwilightStateChanged(@Nullable TwilightState state) {
Justin Klaassenec8837a2016-08-23 12:04:42 -07001308 Slog.d(TAG, "onTwilightStateChanged: isNight="
1309 + (state == null ? null : state.isNight()));
Justin Klaassen908b86c2016-08-08 09:18:42 -07001310 updateActivated(state);
Justin Klaassen911e8892016-06-21 18:24:24 -07001311 }
1312 }
Justin Klaassen639214e2016-07-14 21:00:06 -07001313
1314 /**
1315 * Interpolates between two 4x4 color transform matrices (in column-major order).
1316 */
1317 private static class ColorMatrixEvaluator implements TypeEvaluator<float[]> {
1318
1319 /**
1320 * Result matrix returned by {@link #evaluate(float, float[], float[])}.
1321 */
1322 private final float[] mResultMatrix = new float[16];
1323
1324 @Override
1325 public float[] evaluate(float fraction, float[] startValue, float[] endValue) {
1326 for (int i = 0; i < mResultMatrix.length; i++) {
1327 mResultMatrix[i] = MathUtils.lerp(startValue[i], endValue[i], fraction);
1328 }
1329 return mResultMatrix;
1330 }
1331 }
Christine Franks39b03112018-07-03 14:46:07 -07001332
Christine Franks245ffd42018-11-16 13:45:14 -08001333 private abstract static class TintController {
1334
1335 private ValueAnimator mAnimator;
1336 private Boolean mIsActivated;
1337
1338 public ValueAnimator getAnimator() {
1339 return mAnimator;
1340 }
1341
1342 public void setAnimator(ValueAnimator animator) {
1343 mAnimator = animator;
1344 }
1345
1346 /**
1347 * Cancel the animator if it's still running.
1348 */
1349 public void cancelAnimator() {
1350 if (mAnimator != null) {
1351 mAnimator.cancel();
1352 }
1353 }
1354
1355 /**
1356 * End the animator if it's still running, jumping to the end state.
1357 */
1358 public void endAnimator() {
1359 if (mAnimator != null) {
1360 mAnimator.end();
1361 mAnimator = null;
1362 }
1363 }
1364
1365 public void setActivated(Boolean isActivated) {
1366 mIsActivated = isActivated;
1367 }
1368
1369 public boolean isActivated() {
1370 return mIsActivated != null && mIsActivated;
1371 }
1372
1373 public boolean isActivatedStateNotSet() {
1374 return mIsActivated == null;
1375 }
1376
1377 /**
Daniel Solomon8b72c5b2018-11-25 11:07:13 -08001378 * Dump debug information.
1379 */
1380 public void dump(PrintWriter pw) {
1381 }
1382
1383 /**
Christine Franks245ffd42018-11-16 13:45:14 -08001384 * Set up any constants needed for computing the matrix.
1385 */
1386 public abstract void setUp(Context context, boolean needsLinear);
1387
1388 /**
1389 * Sets the 4x4 matrix to apply.
1390 */
1391 public abstract void setMatrix(int value);
1392
1393 /**
1394 * Get the 4x4 matrix to apply.
1395 */
1396 public abstract float[] getMatrix();
1397
1398 /**
1399 * Get the color transform level to apply the matrix.
1400 */
1401 public abstract int getLevel();
Christine Franksa4ed3762019-01-24 15:37:04 -08001402
1403 /**
1404 * Returns whether or not this transform type is available on this device.
1405 */
1406 public abstract boolean isAvailable(Context context);
Christine Franks245ffd42018-11-16 13:45:14 -08001407 }
1408
Christine Franks83cc5412018-07-03 14:46:07 -07001409 private final class NightDisplayTintController extends TintController {
1410
Christine Franksa4ed3762019-01-24 15:37:04 -08001411 private final float[] mMatrix = new float[16];
Christine Franks83cc5412018-07-03 14:46:07 -07001412 private final float[] mColorTempCoefficients = new float[9];
Christine Franksa4ed3762019-01-24 15:37:04 -08001413
1414 private Boolean mIsAvailable;
Christine Franks83cc5412018-07-03 14:46:07 -07001415 private Integer mColorTemp;
1416
1417 /**
1418 * Set coefficients based on whether the color matrix is linear or not.
1419 */
1420 @Override
1421 public void setUp(Context context, boolean needsLinear) {
1422 final String[] coefficients = context.getResources().getStringArray(needsLinear
1423 ? R.array.config_nightDisplayColorTemperatureCoefficients
1424 : R.array.config_nightDisplayColorTemperatureCoefficientsNative);
1425 for (int i = 0; i < 9 && i < coefficients.length; i++) {
1426 mColorTempCoefficients[i] = Float.parseFloat(coefficients[i]);
1427 }
1428 }
1429
1430 @Override
1431 public void setMatrix(int cct) {
1432 if (mMatrix.length != 16) {
1433 Slog.d(TAG, "The display transformation matrix must be 4x4");
1434 return;
1435 }
1436
1437 Matrix.setIdentityM(mMatrix, 0);
1438
1439 final float squareTemperature = cct * cct;
1440 final float red = squareTemperature * mColorTempCoefficients[0]
1441 + cct * mColorTempCoefficients[1] + mColorTempCoefficients[2];
1442 final float green = squareTemperature * mColorTempCoefficients[3]
1443 + cct * mColorTempCoefficients[4] + mColorTempCoefficients[5];
1444 final float blue = squareTemperature * mColorTempCoefficients[6]
1445 + cct * mColorTempCoefficients[7] + mColorTempCoefficients[8];
1446 mMatrix[0] = red;
1447 mMatrix[5] = green;
1448 mMatrix[10] = blue;
1449 }
1450
1451 @Override
1452 public float[] getMatrix() {
1453 return isActivated() ? mMatrix : MATRIX_IDENTITY;
1454 }
1455
1456 @Override
1457 public void setActivated(Boolean activated) {
1458 if (activated == null) {
1459 super.setActivated(null);
1460 return;
1461 }
1462
1463 boolean activationStateChanged = activated != isActivated();
1464
1465 if (!isActivatedStateNotSet() && activationStateChanged) {
1466 // This is a true state change, so set this as the last activation time.
1467 Secure.putStringForUser(getContext().getContentResolver(),
1468 Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
1469 LocalDateTime.now().toString(),
1470 mCurrentUser);
1471 }
1472
1473 if (isActivatedStateNotSet() || activationStateChanged) {
1474 super.setActivated(activated);
1475 Secure.putIntForUser(getContext().getContentResolver(),
1476 Secure.NIGHT_DISPLAY_ACTIVATED,
1477 activated ? 1 : 0, mCurrentUser);
1478 onActivated(activated);
1479 }
1480 }
1481
1482 @Override
1483 public int getLevel() {
1484 return LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
1485 }
1486
Christine Franksa4ed3762019-01-24 15:37:04 -08001487 @Override
1488 public boolean isAvailable(Context context) {
1489 if (mIsAvailable == null) {
1490 mIsAvailable = ColorDisplayManager.isNightDisplayAvailable(context);
1491 }
1492 return mIsAvailable;
1493 }
1494
Christine Franks83cc5412018-07-03 14:46:07 -07001495 void onActivated(boolean activated) {
1496 Slog.i(TAG, activated ? "Turning on night display" : "Turning off night display");
1497 if (mNightDisplayAutoMode != null) {
1498 mNightDisplayAutoMode.onActivated(activated);
1499 }
1500
Christine Franksa4ed3762019-01-24 15:37:04 -08001501 if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) {
Christine Franks83cc5412018-07-03 14:46:07 -07001502 updateDisplayWhiteBalanceStatus();
1503 }
1504
1505 mHandler.sendEmptyMessage(MSG_APPLY_NIGHT_DISPLAY_ANIMATED);
1506 }
1507
1508 int getColorTemperature() {
1509 return mColorTemp != null ? clampNightDisplayColorTemperature(mColorTemp)
1510 : getNightDisplayColorTemperatureSetting();
1511 }
1512
1513 boolean setColorTemperature(int temperature) {
1514 mColorTemp = temperature;
1515 final boolean success = Secure.putIntForUser(getContext().getContentResolver(),
1516 Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, temperature, mCurrentUser);
1517 onColorTemperatureChanged(temperature);
1518 return success;
1519 }
1520
1521 void onColorTemperatureChanged(int temperature) {
1522 setMatrix(temperature);
1523 mHandler.sendEmptyMessage(MSG_APPLY_NIGHT_DISPLAY_IMMEDIATE);
1524 }
1525 }
1526
Christine Franks245ffd42018-11-16 13:45:14 -08001527 /**
1528 * Local service that allows color transforms to be enabled from other system services.
1529 */
1530 public final class ColorDisplayServiceInternal {
1531
1532 /**
1533 * Set the current CCT value for the display white balance transform, and if the transform
1534 * is enabled, apply it.
1535 *
1536 * @param cct the color temperature in Kelvin.
1537 */
1538 public boolean setDisplayWhiteBalanceColorTemperature(int cct) {
1539 // Update the transform matrix even if it can't be applied.
Christine Franks245ffd42018-11-16 13:45:14 -08001540 mDisplayWhiteBalanceTintController.setMatrix(cct);
1541
1542 if (mDisplayWhiteBalanceTintController.isActivated()) {
Christine Franksc7fb9452019-02-04 08:45:33 -08001543 mHandler.sendEmptyMessage(MSG_APPLY_DISPLAY_WHITE_BALANCE);
Christine Franks245ffd42018-11-16 13:45:14 -08001544 return true;
1545 }
1546 return false;
1547 }
1548
1549 /**
1550 * Sets the listener and returns whether display white balance is currently enabled.
1551 */
1552 public boolean setDisplayWhiteBalanceListener(DisplayWhiteBalanceListener listener) {
1553 mDisplayWhiteBalanceListener = listener;
1554 return mDisplayWhiteBalanceTintController.isActivated();
1555 }
Daniel Solomon8b72c5b2018-11-25 11:07:13 -08001556
1557 public void dump(PrintWriter pw) {
1558 mDisplayWhiteBalanceTintController.dump(pw);
1559 }
Christine Franksf3529b22019-01-03 13:20:17 -08001560
1561 /**
1562 * Adds a {@link WeakReference<ColorTransformController>} for a newly started activity, and
1563 * invokes {@link ColorTransformController#applyAppSaturation(float[], float[])} if needed.
1564 */
Christine Franks55194dc2019-01-15 13:47:06 -08001565 public boolean attachColorTransformController(String packageName, @UserIdInt int userId,
Christine Franksf3529b22019-01-03 13:20:17 -08001566 WeakReference<ColorTransformController> controller) {
1567 return mAppSaturationController
Christine Franks55194dc2019-01-15 13:47:06 -08001568 .addColorTransformController(packageName, userId, controller);
Christine Franksf3529b22019-01-03 13:20:17 -08001569 }
Christine Franks245ffd42018-11-16 13:45:14 -08001570 }
1571
1572 /**
1573 * Listener for changes in display white balance status.
1574 */
1575 public interface DisplayWhiteBalanceListener {
1576
1577 /**
1578 * Notify that the display white balance status has changed, either due to preemption by
1579 * another transform or the feature being turned off.
1580 */
1581 void onDisplayWhiteBalanceStatusChanged(boolean enabled);
1582 }
1583
Christine Franks09c229e2018-12-14 10:37:40 -08001584 private final class TintHandler extends Handler {
1585
Christine Franks83cc5412018-07-03 14:46:07 -07001586 private TintHandler(Looper looper) {
Christine Franks09c229e2018-12-14 10:37:40 -08001587 super(looper, null, true /* async */);
1588 }
1589
1590 @Override
1591 public void handleMessage(Message msg) {
1592 switch (msg.what) {
1593 case MSG_APPLY_GLOBAL_SATURATION:
1594 mGlobalSaturationTintController.setMatrix(msg.arg1);
1595 applyTint(mGlobalSaturationTintController, false);
1596 break;
Christine Franks83cc5412018-07-03 14:46:07 -07001597 case MSG_APPLY_NIGHT_DISPLAY_IMMEDIATE:
1598 applyTint(mNightDisplayTintController, true);
1599 break;
1600 case MSG_APPLY_NIGHT_DISPLAY_ANIMATED:
1601 applyTint(mNightDisplayTintController, false);
1602 break;
Christine Franksc7fb9452019-02-04 08:45:33 -08001603 case MSG_APPLY_DISPLAY_WHITE_BALANCE:
1604 applyTint(mDisplayWhiteBalanceTintController, false);
1605 break;
Christine Franks09c229e2018-12-14 10:37:40 -08001606 }
1607 }
1608 }
1609
Christine Franksf3529b22019-01-03 13:20:17 -08001610 /**
1611 * Interface for applying transforms to a given AppWindow.
1612 */
1613 public interface ColorTransformController {
1614
Christine Franksd154fe52019-01-04 17:17:45 -08001615 /**
1616 * Apply the given saturation (grayscale) matrix to the associated AppWindow.
1617 */
Christine Franksf3529b22019-01-03 13:20:17 -08001618 void applyAppSaturation(@Size(9) float[] matrix, @Size(3) float[] translation);
1619 }
1620
Christine Franks83cc5412018-07-03 14:46:07 -07001621 @VisibleForTesting
1622 final class BinderService extends IColorDisplayManager.Stub {
Christine Franks57fdde82018-07-03 14:46:07 -07001623
Christine Franks39b03112018-07-03 14:46:07 -07001624 @Override
Christine Franksd154fe52019-01-04 17:17:45 -08001625 public void setColorMode(int colorMode) {
1626 getContext().enforceCallingOrSelfPermission(
1627 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1628 "Permission required to set display color mode");
1629 final long token = Binder.clearCallingIdentity();
1630 try {
1631 setColorModeInternal(colorMode);
1632 } finally {
1633 Binder.restoreCallingIdentity(token);
1634 }
1635 }
1636
1637 @Override
1638 public int getColorMode() {
1639 final long token = Binder.clearCallingIdentity();
1640 try {
1641 return getColorModeInternal();
1642 } finally {
1643 Binder.restoreCallingIdentity(token);
1644 }
1645 }
1646
1647 @Override
Christine Franks39b03112018-07-03 14:46:07 -07001648 public boolean isDeviceColorManaged() {
1649 final long token = Binder.clearCallingIdentity();
1650 try {
1651 return isDeviceColorManagedInternal();
1652 } finally {
1653 Binder.restoreCallingIdentity(token);
1654 }
1655 }
Christine Franks09c229e2018-12-14 10:37:40 -08001656
1657 @Override
1658 public boolean setSaturationLevel(int level) {
1659 final boolean hasTransformsPermission = getContext()
1660 .checkCallingPermission(Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
1661 == PackageManager.PERMISSION_GRANTED;
1662 final boolean hasLegacyPermission = getContext()
1663 .checkCallingPermission(Manifest.permission.CONTROL_DISPLAY_SATURATION)
1664 == PackageManager.PERMISSION_GRANTED;
1665 if (!hasTransformsPermission && !hasLegacyPermission) {
1666 throw new SecurityException("Permission required to set display saturation level");
1667 }
1668 final long token = Binder.clearCallingIdentity();
1669 try {
1670 final Message message = mHandler.obtainMessage(MSG_APPLY_GLOBAL_SATURATION);
1671 message.arg1 = level;
1672 mHandler.sendMessage(message);
1673 } finally {
1674 Binder.restoreCallingIdentity(token);
1675 }
1676 return true;
1677 }
Christine Franksf3529b22019-01-03 13:20:17 -08001678
1679 @Override
Christine Franks6d21d342019-02-07 15:09:03 -08001680 public boolean isSaturationActivated() {
1681 getContext().enforceCallingPermission(
1682 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1683 "Permission required to get display saturation level");
1684 final long token = Binder.clearCallingIdentity();
1685 try {
1686 return !mGlobalSaturationTintController.isActivatedStateNotSet()
1687 && mGlobalSaturationTintController.isActivated();
1688 } finally {
1689 Binder.restoreCallingIdentity(token);
1690 }
1691 }
1692
1693 @Override
Christine Franksf3529b22019-01-03 13:20:17 -08001694 public boolean setAppSaturationLevel(String packageName, int level) {
1695 getContext().enforceCallingPermission(
1696 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1697 "Permission required to set display saturation level");
1698 final long token = Binder.clearCallingIdentity();
1699 try {
1700 return setAppSaturationLevelInternal(packageName, level);
1701 } finally {
1702 Binder.restoreCallingIdentity(token);
1703 }
1704 }
1705
Christine Franks55194dc2019-01-15 13:47:06 -08001706 public int getTransformCapabilities() {
1707 getContext().enforceCallingPermission(
1708 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1709 "Permission required to query transform capabilities");
1710 final long token = Binder.clearCallingIdentity();
1711 try {
1712 return getTransformCapabilitiesInternal();
1713 } finally {
1714 Binder.restoreCallingIdentity(token);
1715 }
1716 }
1717
Christine Franksf3529b22019-01-03 13:20:17 -08001718 @Override
Christine Franks83cc5412018-07-03 14:46:07 -07001719 public boolean setNightDisplayActivated(boolean activated) {
1720 getContext().enforceCallingOrSelfPermission(
1721 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1722 "Permission required to set night display activated");
1723 final long token = Binder.clearCallingIdentity();
1724 try {
1725 mNightDisplayTintController.setActivated(activated);
1726 return true;
1727 } finally {
1728 Binder.restoreCallingIdentity(token);
1729 }
1730 }
1731
1732 @Override
1733 public boolean isNightDisplayActivated() {
1734 final long token = Binder.clearCallingIdentity();
1735 try {
1736 return mNightDisplayTintController.isActivated();
1737 } finally {
1738 Binder.restoreCallingIdentity(token);
1739 }
1740 }
1741
1742 @Override
1743 public boolean setNightDisplayColorTemperature(int temperature) {
1744 getContext().enforceCallingOrSelfPermission(
1745 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1746 "Permission required to set night display temperature");
1747 final long token = Binder.clearCallingIdentity();
1748 try {
1749 return mNightDisplayTintController.setColorTemperature(temperature);
1750 } finally {
1751 Binder.restoreCallingIdentity(token);
1752 }
1753 }
1754
1755 @Override
1756 public int getNightDisplayColorTemperature() {
1757 final long token = Binder.clearCallingIdentity();
1758 try {
1759 return mNightDisplayTintController.getColorTemperature();
1760 } finally {
1761 Binder.restoreCallingIdentity(token);
1762 }
1763 }
1764
1765 @Override
1766 public boolean setNightDisplayAutoMode(int autoMode) {
1767 getContext().enforceCallingOrSelfPermission(
1768 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1769 "Permission required to set night display auto mode");
1770 final long token = Binder.clearCallingIdentity();
1771 try {
1772 return setNightDisplayAutoModeInternal(autoMode);
1773 } finally {
1774 Binder.restoreCallingIdentity(token);
1775 }
1776 }
1777
1778 @Override
1779 public int getNightDisplayAutoMode() {
1780 getContext().enforceCallingOrSelfPermission(
1781 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1782 "Permission required to get night display auto mode");
1783 final long token = Binder.clearCallingIdentity();
1784 try {
1785 return getNightDisplayAutoModeInternal();
1786 } finally {
1787 Binder.restoreCallingIdentity(token);
1788 }
1789 }
1790
1791 @Override
1792 public int getNightDisplayAutoModeRaw() {
1793 final long token = Binder.clearCallingIdentity();
1794 try {
1795 return getNightDisplayAutoModeRawInternal();
1796 } finally {
1797 Binder.restoreCallingIdentity(token);
1798 }
1799 }
1800
1801 @Override
1802 public boolean setNightDisplayCustomStartTime(Time startTime) {
1803 getContext().enforceCallingOrSelfPermission(
1804 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1805 "Permission required to set night display custom start time");
1806 final long token = Binder.clearCallingIdentity();
1807 try {
1808 return setNightDisplayCustomStartTimeInternal(startTime);
1809 } finally {
1810 Binder.restoreCallingIdentity(token);
1811 }
1812 }
1813
1814 @Override
1815 public Time getNightDisplayCustomStartTime() {
1816 final long token = Binder.clearCallingIdentity();
1817 try {
1818 return getNightDisplayCustomStartTimeInternal();
1819 } finally {
1820 Binder.restoreCallingIdentity(token);
1821 }
1822 }
1823
1824 @Override
1825 public boolean setNightDisplayCustomEndTime(Time endTime) {
1826 getContext().enforceCallingOrSelfPermission(
1827 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1828 "Permission required to set night display custom end time");
1829 final long token = Binder.clearCallingIdentity();
1830 try {
1831 return setNightDisplayCustomEndTimeInternal(endTime);
1832 } finally {
1833 Binder.restoreCallingIdentity(token);
1834 }
1835 }
1836
1837 @Override
1838 public Time getNightDisplayCustomEndTime() {
1839 final long token = Binder.clearCallingIdentity();
1840 try {
1841 return getNightDisplayCustomEndTimeInternal();
1842 } finally {
1843 Binder.restoreCallingIdentity(token);
1844 }
1845 }
1846
1847 @Override
Christine Franksf3529b22019-01-03 13:20:17 -08001848 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Christine Franksd154fe52019-01-04 17:17:45 -08001849 if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
1850 return;
1851 }
Christine Franksf3529b22019-01-03 13:20:17 -08001852
1853 final long token = Binder.clearCallingIdentity();
1854 try {
1855 dumpInternal(pw);
1856 } finally {
1857 Binder.restoreCallingIdentity(token);
1858 }
1859 }
Christine Franks39b03112018-07-03 14:46:07 -07001860 }
Justin Klaassen911e8892016-06-21 18:24:24 -07001861}