blob: 0bce544d1c2395a2903cdc29c2f0a50009f954d6 [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;
Christine Franksf3529b22019-01-03 13:20:17 -080030import android.annotation.Size;
Christine Franks55194dc2019-01-15 13:47:06 -080031import android.annotation.UserIdInt;
Justin Klaassen911e8892016-06-21 18:24:24 -070032import android.app.AlarmManager;
33import android.content.BroadcastReceiver;
34import android.content.ContentResolver;
35import android.content.Context;
36import android.content.Intent;
37import android.content.IntentFilter;
Christine Franks09c229e2018-12-14 10:37:40 -080038import android.content.pm.PackageManager;
Daniel Solomon8b72c5b2018-11-25 11:07:13 -080039import android.content.res.Resources;
Justin Klaassen2696d992016-07-11 21:26:46 -070040import android.database.ContentObserver;
Daniel Solomon8b72c5b2018-11-25 11:07:13 -080041import android.graphics.ColorSpace;
Christine Franksf3529b22019-01-03 13:20:17 -080042import android.hardware.display.ColorDisplayManager;
Christine Franks39b03112018-07-03 14:46:07 -070043import android.hardware.display.IColorDisplayManager;
Justin Klaassen2696d992016-07-11 21:26:46 -070044import android.net.Uri;
Justin Klaassen639214e2016-07-14 21:00:06 -070045import android.opengl.Matrix;
Christine Franks39b03112018-07-03 14:46:07 -070046import android.os.Binder;
Justin Klaassen911e8892016-06-21 18:24:24 -070047import android.os.Handler;
48import android.os.Looper;
Christine Franks09c229e2018-12-14 10:37:40 -080049import android.os.Message;
Justin Klaassen911e8892016-06-21 18:24:24 -070050import android.os.UserHandle;
51import android.provider.Settings.Secure;
Christine Franks57fdde82018-07-03 14:46:07 -070052import android.provider.Settings.System;
Justin Klaassen639214e2016-07-14 21:00:06 -070053import android.util.MathUtils;
Justin Klaassen911e8892016-06-21 18:24:24 -070054import android.util.Slog;
Christine Franks55194dc2019-01-15 13:47:06 -080055import android.view.SurfaceControl;
Christine Franks9114f462019-01-04 11:27:30 -080056import android.view.accessibility.AccessibilityManager;
Justin Klaassen639214e2016-07-14 21:00:06 -070057import android.view.animation.AnimationUtils;
Justin Klaassen911e8892016-06-21 18:24:24 -070058
Christine Franks39b03112018-07-03 14:46:07 -070059import com.android.internal.R;
Christine Franks57fdde82018-07-03 14:46:07 -070060import com.android.internal.annotations.VisibleForTesting;
Christine Franks5397f032017-11-01 18:35:16 -070061import com.android.internal.app.ColorDisplayController;
Christine Franksf3529b22019-01-03 13:20:17 -080062import com.android.internal.util.DumpUtils;
Christine Franks57fdde82018-07-03 14:46:07 -070063import com.android.server.DisplayThread;
Justin Klaassen911e8892016-06-21 18:24:24 -070064import com.android.server.SystemService;
65import com.android.server.twilight.TwilightListener;
66import com.android.server.twilight.TwilightManager;
67import com.android.server.twilight.TwilightState;
68
Christine Franksf3529b22019-01-03 13:20:17 -080069import java.io.FileDescriptor;
Daniel Solomon8b72c5b2018-11-25 11:07:13 -080070import java.io.PrintWriter;
Christine Franksf3529b22019-01-03 13:20:17 -080071import java.lang.ref.WeakReference;
Christine Franks57fdde82018-07-03 14:46:07 -070072import java.time.DateTimeException;
73import java.time.Instant;
Christine Franks03213462017-08-25 13:57:26 -070074import java.time.LocalDateTime;
75import java.time.LocalTime;
76import java.time.ZoneId;
Christine Franks57fdde82018-07-03 14:46:07 -070077import java.time.format.DateTimeParseException;
Christine Franks09c229e2018-12-14 10:37:40 -080078import java.util.Arrays;
Christine Franks8ad71492017-10-24 19:04:22 -070079
Justin Klaassen911e8892016-06-21 18:24:24 -070080/**
Christine Franks39b03112018-07-03 14:46:07 -070081 * Controls the display's color transforms.
Justin Klaassen911e8892016-06-21 18:24:24 -070082 */
Christine Franks57fdde82018-07-03 14:46:07 -070083public final class ColorDisplayService extends SystemService {
Justin Klaassen911e8892016-06-21 18:24:24 -070084
Christine Franks5397f032017-11-01 18:35:16 -070085 private static final String TAG = "ColorDisplayService";
Justin Klaassen911e8892016-06-21 18:24:24 -070086
87 /**
Christine Franks7b83b4282017-01-18 14:55:00 -080088 * The transition time, in milliseconds, for Night Display to turn on/off.
89 */
90 private static final long TRANSITION_DURATION = 3000L;
91
92 /**
Justin Klaassen639214e2016-07-14 21:00:06 -070093 * The identity matrix, used if one of the given matrices is {@code null}.
94 */
95 private static final float[] MATRIX_IDENTITY = new float[16];
Christine Franks57fdde82018-07-03 14:46:07 -070096
Justin Klaassen639214e2016-07-14 21:00:06 -070097 static {
98 Matrix.setIdentityM(MATRIX_IDENTITY, 0);
99 }
100
Christine Franks09c229e2018-12-14 10:37:40 -0800101 private static final int MSG_APPLY_NIGHT_DISPLAY_IMMEDIATE = 0;
102 private static final int MSG_APPLY_NIGHT_DISPLAY_ANIMATED = 1;
103 private static final int MSG_APPLY_GLOBAL_SATURATION = 2;
104
Justin Klaassen639214e2016-07-14 21:00:06 -0700105 /**
106 * Evaluator used to animate color matrix transitions.
107 */
108 private static final ColorMatrixEvaluator COLOR_MATRIX_EVALUATOR = new ColorMatrixEvaluator();
109
Christine Franks245ffd42018-11-16 13:45:14 -0800110 private final TintController mNightDisplayTintController = new TintController() {
111
112 private float[] mMatrixNightDisplay = new float[16];
113 private final float[] mColorTempCoefficients = new float[9];
114
115 /**
116 * Set coefficients based on whether the color matrix is linear or not.
117 */
118 @Override
119 public void setUp(Context context, boolean needsLinear) {
120 final String[] coefficients = context.getResources().getStringArray(needsLinear
121 ? R.array.config_nightDisplayColorTemperatureCoefficients
122 : R.array.config_nightDisplayColorTemperatureCoefficientsNative);
123 for (int i = 0; i < 9 && i < coefficients.length; i++) {
124 mColorTempCoefficients[i] = Float.parseFloat(coefficients[i]);
125 }
126 }
127
128 @Override
129 public void setMatrix(int cct) {
130 if (mMatrixNightDisplay.length != 16) {
131 Slog.d(TAG, "The display transformation matrix must be 4x4");
132 return;
133 }
134
135 Matrix.setIdentityM(mMatrixNightDisplay, 0);
136
137 final float squareTemperature = cct * cct;
138 final float red = squareTemperature * mColorTempCoefficients[0]
139 + cct * mColorTempCoefficients[1] + mColorTempCoefficients[2];
140 final float green = squareTemperature * mColorTempCoefficients[3]
141 + cct * mColorTempCoefficients[4] + mColorTempCoefficients[5];
142 final float blue = squareTemperature * mColorTempCoefficients[6]
143 + cct * mColorTempCoefficients[7] + mColorTempCoefficients[8];
144 mMatrixNightDisplay[0] = red;
145 mMatrixNightDisplay[5] = green;
146 mMatrixNightDisplay[10] = blue;
147 }
148
149 @Override
150 public float[] getMatrix() {
151 return isActivated() ? mMatrixNightDisplay : MATRIX_IDENTITY;
152 }
153
154 @Override
155 public int getLevel() {
156 return LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
157 }
158 };
159
160 private final TintController mDisplayWhiteBalanceTintController = new TintController() {
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800161 // Three chromaticity coordinates per color: X, Y, and Z
162 private final int NUM_VALUES_PER_PRIMARY = 3;
163 // Four colors: red, green, blue, and white
164 private final int NUM_DISPLAY_PRIMARIES_VALS = 4 * NUM_VALUES_PER_PRIMARY;
Christine Franks245ffd42018-11-16 13:45:14 -0800165
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800166 private final Object mLock = new Object();
167 private int mTemperatureMin;
168 private int mTemperatureMax;
169 private int mTemperatureDefault;
170 private float[] mDisplayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
171 private ColorSpace.Rgb mDisplayColorSpaceRGB;
172 private float[] mChromaticAdaptationMatrix;
173 private int mCurrentColorTemperature;
174 private float[] mCurrentColorTemperatureXYZ;
175 private boolean mSetUp = false;
Christine Franks245ffd42018-11-16 13:45:14 -0800176 private float[] mMatrixDisplayWhiteBalance = new float[16];
177
178 @Override
179 public void setUp(Context context, boolean needsLinear) {
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800180 mSetUp = false;
181
182 final Resources res = getContext().getResources();
183 final String[] displayPrimariesValues = res.getStringArray(
184 R.array.config_displayWhiteBalanceDisplayPrimaries);
185 final String[] nominalWhiteValues = res.getStringArray(
186 R.array.config_displayWhiteBalanceDisplayNominalWhite);
187
188 if (displayPrimariesValues.length != NUM_DISPLAY_PRIMARIES_VALS) {
189 Slog.e(TAG, "Unexpected display white balance primaries resource length " +
190 displayPrimariesValues.length);
191 return;
192 }
193
194 if (nominalWhiteValues.length != NUM_VALUES_PER_PRIMARY) {
195 Slog.e(TAG, "Unexpected display white balance nominal white resource length " +
196 nominalWhiteValues.length);
197 return;
198 }
199
200 float[] displayRedGreenBlueXYZ =
201 new float[NUM_DISPLAY_PRIMARIES_VALS - NUM_VALUES_PER_PRIMARY];
202 float[] displayWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
203 for (int i = 0; i < displayRedGreenBlueXYZ.length; i++) {
204 displayRedGreenBlueXYZ[i] = Float.parseFloat(displayPrimariesValues[i]);
205 }
206 for (int i = 0; i < displayWhiteXYZ.length; i++) {
207 displayWhiteXYZ[i] = Float.parseFloat(
208 displayPrimariesValues[displayRedGreenBlueXYZ.length + i]);
209 }
210
211 final ColorSpace.Rgb displayColorSpaceRGB = new ColorSpace.Rgb(
212 "Display Color Space",
213 displayRedGreenBlueXYZ,
214 displayWhiteXYZ,
215 2.2f // gamma, unused for display white balance
216 );
217
218 float[] displayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
219 for (int i = 0; i < nominalWhiteValues.length; i++) {
220 displayNominalWhiteXYZ[i] = Float.parseFloat(nominalWhiteValues[i]);
221 }
222
223 final int colorTemperatureMin = res.getInteger(
224 R.integer.config_displayWhiteBalanceColorTemperatureMin);
225 if (colorTemperatureMin <= 0) {
226 Slog.e(TAG, "display white balance minimum temperature must be greater than 0");
227 return;
228 }
229
230 final int colorTemperatureMax = res.getInteger(
231 R.integer.config_displayWhiteBalanceColorTemperatureMax);
232 if (colorTemperatureMax < colorTemperatureMin) {
233 Slog.e(TAG, "display white balance max temp must be greater or equal to min");
234 return;
235 }
236
237 final int colorTemperature = res.getInteger(
238 R.integer.config_displayWhiteBalanceColorTemperatureDefault);
239
240 synchronized (mLock) {
241 mDisplayColorSpaceRGB = displayColorSpaceRGB;
242 mDisplayNominalWhiteXYZ = displayNominalWhiteXYZ;
243 mTemperatureMin = colorTemperatureMin;
244 mTemperatureMax = colorTemperatureMax;
245 mTemperatureDefault = colorTemperature;
246 mSetUp = true;
247 }
248
249 setMatrix(mTemperatureDefault);
Christine Franks245ffd42018-11-16 13:45:14 -0800250 }
251
252 @Override
253 public float[] getMatrix() {
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800254 return mSetUp && isActivated() ? mMatrixDisplayWhiteBalance : MATRIX_IDENTITY;
Christine Franks245ffd42018-11-16 13:45:14 -0800255 }
256
257 @Override
258 public void setMatrix(int cct) {
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800259 if (!mSetUp) {
260 Slog.w(TAG, "Can't set display white balance temperature: uninitialized");
261 return;
262 }
263
264 if (cct < mTemperatureMin) {
265 Slog.w(TAG, "Requested display color temperature is below allowed minimum");
266 cct = mTemperatureMin;
267 } else if (cct > mTemperatureMax) {
268 Slog.w(TAG, "Requested display color temperature is above allowed maximum");
269 cct = mTemperatureMax;
270 }
271
272 Slog.d(TAG, "setDisplayWhiteBalanceTemperatureMatrix: cct = " + cct);
273
274 synchronized (mLock) {
275 mCurrentColorTemperature = cct;
276
277 // Adapt the display's nominal white point to match the requested CCT value
278 mCurrentColorTemperatureXYZ = ColorSpace.cctToIlluminantdXyz(cct);
279
280 mChromaticAdaptationMatrix =
281 ColorSpace.chromaticAdaptation(ColorSpace.Adaptation.BRADFORD,
282 mDisplayNominalWhiteXYZ, mCurrentColorTemperatureXYZ);
283
284 // Convert the adaptation matrix to RGB space
285 float[] result = ColorSpace.mul3x3(mChromaticAdaptationMatrix,
286 mDisplayColorSpaceRGB.getTransform());
287 result = ColorSpace.mul3x3(mDisplayColorSpaceRGB.getInverseTransform(), result);
288
289 // Normalize the transform matrix to peak white value in RGB space
290 final float adaptedMaxR = result[0] + result[3] + result[6];
291 final float adaptedMaxG = result[1] + result[4] + result[7];
292 final float adaptedMaxB = result[2] + result[5] + result[8];
293 final float denum = Math.max(Math.max(adaptedMaxR, adaptedMaxG), adaptedMaxB);
294 for (int i = 0; i < result.length; i++) {
295 result[i] /= denum;
296 }
297
298 Matrix.setIdentityM(mMatrixDisplayWhiteBalance, 0);
299 java.lang.System.arraycopy(result, 0, mMatrixDisplayWhiteBalance, 0, 3);
300 java.lang.System.arraycopy(result, 3, mMatrixDisplayWhiteBalance, 4, 3);
301 java.lang.System.arraycopy(result, 6, mMatrixDisplayWhiteBalance, 8, 3);
302 }
Christine Franks245ffd42018-11-16 13:45:14 -0800303 }
304
305 @Override
306 public int getLevel() {
307 return LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;
308 }
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800309
310 /**
311 * Format a given matrix into a string.
312 *
313 * @param matrix the matrix to format
314 * @param cols number of columns in the matrix
315 */
316 private String matrixToString(float[] matrix, int cols) {
317 if (matrix == null || cols <= 0) {
318 Slog.e(TAG, "Invalid arguments when formatting matrix to string");
319 return "";
320 }
321
322 StringBuilder sb = new StringBuilder("");
323 for (int i = 0; i < matrix.length; i++) {
324 if (i % cols == 0) {
325 sb.append("\n ");
326 }
327 sb.append(String.format("%9.6f ", matrix[i]));
328 }
329 return sb.toString();
330 }
331
332 @Override
333 public void dump(PrintWriter pw) {
334 synchronized (mLock) {
335 pw.println("ColorDisplayService");
336 pw.println(" mSetUp = " + mSetUp);
337
338 if (!mSetUp) {
339 return;
340 }
341
342 pw.println(" isActivated = " + isActivated());
343 pw.println(" mTemperatureMin = " + mTemperatureMin);
344 pw.println(" mTemperatureMax = " + mTemperatureMax);
345 pw.println(" mTemperatureDefault = " + mTemperatureDefault);
346 pw.println(" mCurrentColorTemperature = " + mCurrentColorTemperature);
347 pw.println(" mCurrentColorTemperatureXYZ = " +
348 matrixToString(mCurrentColorTemperatureXYZ, 3));
349 pw.println(" mDisplayColorSpaceRGB RGB-to-XYZ = " +
350 matrixToString(mDisplayColorSpaceRGB.getTransform(), 3));
351 pw.println(" mChromaticAdaptationMatrix = " +
352 matrixToString(mChromaticAdaptationMatrix, 3));
353 pw.println(" mDisplayColorSpaceRGB XYZ-to-RGB = " +
354 matrixToString(mDisplayColorSpaceRGB.getInverseTransform(), 3));
355 pw.println(" mMatrixDisplayWhiteBalance = " +
356 matrixToString(mMatrixDisplayWhiteBalance, 4));
357 }
358 }
Christine Franks245ffd42018-11-16 13:45:14 -0800359 };
360
Christine Franks09c229e2018-12-14 10:37:40 -0800361 private final TintController mGlobalSaturationTintController = new TintController() {
362
363 private float[] mMatrixGlobalSaturation = new float[16];
364
365 @Override
366 public void setUp(Context context, boolean needsLinear) {
367 }
368
369 @Override
370 public float[] getMatrix() {
371 return Arrays.copyOf(mMatrixGlobalSaturation, mMatrixGlobalSaturation.length);
372 }
373
374 @Override
375 public void setMatrix(int saturationLevel) {
376 if (saturationLevel < 0) {
377 saturationLevel = 0;
378 } else if (saturationLevel > 100) {
379 saturationLevel = 100;
380 }
381 Slog.d(TAG, "Setting saturation level: " + saturationLevel);
382
383 if (saturationLevel == 100) {
384 Matrix.setIdentityM(mMatrixGlobalSaturation, 0);
385 } else {
386 float saturation = saturationLevel * 0.1f;
387 float desaturation = 1.0f - saturation;
388 float[] luminance = {0.231f * desaturation, 0.715f * desaturation,
389 0.072f * desaturation};
390 mMatrixGlobalSaturation[0] = luminance[0] + saturation;
391 mMatrixGlobalSaturation[1] = luminance[0];
392 mMatrixGlobalSaturation[2] = luminance[0];
393 mMatrixGlobalSaturation[4] = luminance[1];
394 mMatrixGlobalSaturation[5] = luminance[1] + saturation;
395 mMatrixGlobalSaturation[6] = luminance[1];
396 mMatrixGlobalSaturation[8] = luminance[2];
397 mMatrixGlobalSaturation[9] = luminance[2];
398 mMatrixGlobalSaturation[10] = luminance[2] + saturation;
399 }
400 }
401
402 @Override
403 public int getLevel() {
404 return LEVEL_COLOR_MATRIX_SATURATION;
405 }
406 };
407
Christine Franks9114f462019-01-04 11:27:30 -0800408 /**
409 * Matrix and offset used for converting color to grayscale.
410 */
411 private static final float[] MATRIX_GRAYSCALE = new float[]{
412 .2126f, .2126f, .2126f, 0f,
413 .7152f, .7152f, .7152f, 0f,
414 .0722f, .0722f, .0722f, 0f,
415 0f, 0f, 0f, 1f
416 };
417
418 /**
419 * Matrix and offset used for luminance inversion. Represents a transform from RGB to YIQ color
420 * space, rotation around the Y axis by 180 degrees, transform back to RGB color space, and
421 * subtraction from 1. The last row represents a non-multiplied addition, see surfaceflinger's
422 * ProgramCache for full implementation details.
423 */
424 private static final float[] MATRIX_INVERT_COLOR = new float[] {
425 0.402f, -0.598f, -0.599f, 0f,
426 -1.174f, -0.174f, -1.175f, 0f,
427 -0.228f, -0.228f, 0.772f, 0f,
428 1f, 1f, 1f, 1f
429 };
430
Justin Klaassen2696d992016-07-11 21:26:46 -0700431 private final Handler mHandler;
432
Christine Franksf3529b22019-01-03 13:20:17 -0800433 private final AppSaturationController mAppSaturationController = new AppSaturationController();
434
Justin Klaassen911e8892016-06-21 18:24:24 -0700435 private int mCurrentUser = UserHandle.USER_NULL;
Justin Klaassen2696d992016-07-11 21:26:46 -0700436 private ContentObserver mUserSetupObserver;
Justin Klaassen911e8892016-06-21 18:24:24 -0700437 private boolean mBootCompleted;
438
Christine Franks57fdde82018-07-03 14:46:07 -0700439 private ColorDisplayController mNightDisplayController;
440 private ContentObserver mContentObserver;
Christine Franks57fdde82018-07-03 14:46:07 -0700441
Christine Franks245ffd42018-11-16 13:45:14 -0800442 private DisplayWhiteBalanceListener mDisplayWhiteBalanceListener;
443
Christine Franks57fdde82018-07-03 14:46:07 -0700444 private NightDisplayAutoMode mNightDisplayAutoMode;
Justin Klaassen911e8892016-06-21 18:24:24 -0700445
Christine Franks5397f032017-11-01 18:35:16 -0700446 public ColorDisplayService(Context context) {
Justin Klaassen911e8892016-06-21 18:24:24 -0700447 super(context);
Christine Franks09c229e2018-12-14 10:37:40 -0800448 mHandler = new TintHandler(Looper.getMainLooper());
Justin Klaassen911e8892016-06-21 18:24:24 -0700449 }
450
451 @Override
452 public void onStart() {
Christine Franks39b03112018-07-03 14:46:07 -0700453 publishBinderService(Context.COLOR_DISPLAY_SERVICE, new BinderService());
Christine Franks245ffd42018-11-16 13:45:14 -0800454 publishLocalService(ColorDisplayServiceInternal.class, new ColorDisplayServiceInternal());
Justin Klaassen911e8892016-06-21 18:24:24 -0700455 }
456
457 @Override
Justin Klaassen2696d992016-07-11 21:26:46 -0700458 public void onBootPhase(int phase) {
Christine Frankse5bb03e2017-02-10 17:36:10 -0800459 if (phase >= PHASE_BOOT_COMPLETED) {
Justin Klaassen2696d992016-07-11 21:26:46 -0700460 mBootCompleted = true;
461
462 // Register listeners now that boot is complete.
463 if (mCurrentUser != UserHandle.USER_NULL && mUserSetupObserver == null) {
464 setUp();
465 }
466 }
467 }
468
469 @Override
Justin Klaassen911e8892016-06-21 18:24:24 -0700470 public void onStartUser(int userHandle) {
471 super.onStartUser(userHandle);
472
Justin Klaassen911e8892016-06-21 18:24:24 -0700473 if (mCurrentUser == UserHandle.USER_NULL) {
Justin Klaassen2696d992016-07-11 21:26:46 -0700474 onUserChanged(userHandle);
Justin Klaassen911e8892016-06-21 18:24:24 -0700475 }
476 }
477
478 @Override
479 public void onSwitchUser(int userHandle) {
480 super.onSwitchUser(userHandle);
481
Justin Klaassen2696d992016-07-11 21:26:46 -0700482 onUserChanged(userHandle);
Justin Klaassen911e8892016-06-21 18:24:24 -0700483 }
484
485 @Override
486 public void onStopUser(int userHandle) {
487 super.onStopUser(userHandle);
488
Justin Klaassen911e8892016-06-21 18:24:24 -0700489 if (mCurrentUser == userHandle) {
Justin Klaassen2696d992016-07-11 21:26:46 -0700490 onUserChanged(UserHandle.USER_NULL);
Justin Klaassen911e8892016-06-21 18:24:24 -0700491 }
492 }
493
Justin Klaassen2696d992016-07-11 21:26:46 -0700494 private void onUserChanged(int userHandle) {
495 final ContentResolver cr = getContext().getContentResolver();
Justin Klaassen911e8892016-06-21 18:24:24 -0700496
Justin Klaassen2696d992016-07-11 21:26:46 -0700497 if (mCurrentUser != UserHandle.USER_NULL) {
498 if (mUserSetupObserver != null) {
499 cr.unregisterContentObserver(mUserSetupObserver);
500 mUserSetupObserver = null;
501 } else if (mBootCompleted) {
502 tearDown();
503 }
504 }
505
506 mCurrentUser = userHandle;
507
508 if (mCurrentUser != UserHandle.USER_NULL) {
509 if (!isUserSetupCompleted(cr, mCurrentUser)) {
510 mUserSetupObserver = new ContentObserver(mHandler) {
511 @Override
512 public void onChange(boolean selfChange, Uri uri) {
513 if (isUserSetupCompleted(cr, mCurrentUser)) {
514 cr.unregisterContentObserver(this);
515 mUserSetupObserver = null;
516
517 if (mBootCompleted) {
518 setUp();
519 }
520 }
521 }
522 };
523 cr.registerContentObserver(Secure.getUriFor(Secure.USER_SETUP_COMPLETE),
Christine Franks39b03112018-07-03 14:46:07 -0700524 false /* notifyForDescendants */, mUserSetupObserver, mCurrentUser);
Justin Klaassen2696d992016-07-11 21:26:46 -0700525 } else if (mBootCompleted) {
526 setUp();
Justin Klaassen911e8892016-06-21 18:24:24 -0700527 }
528 }
529 }
530
Justin Klaassen2696d992016-07-11 21:26:46 -0700531 private static boolean isUserSetupCompleted(ContentResolver cr, int userHandle) {
532 return Secure.getIntForUser(cr, Secure.USER_SETUP_COMPLETE, 0, userHandle) == 1;
533 }
534
535 private void setUp() {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700536 Slog.d(TAG, "setUp: currentUser=" + mCurrentUser);
537
Christine Franks57fdde82018-07-03 14:46:07 -0700538 mNightDisplayController = new ColorDisplayController(getContext(), mCurrentUser);
539
540 // Listen for external changes to any of the settings.
541 if (mContentObserver == null) {
542 mContentObserver = new ContentObserver(new Handler(DisplayThread.get().getLooper())) {
543 @Override
544 public void onChange(boolean selfChange, Uri uri) {
545 super.onChange(selfChange, uri);
546
547 final String setting = uri == null ? null : uri.getLastPathSegment();
548 if (setting != null) {
549 switch (setting) {
550 case Secure.NIGHT_DISPLAY_ACTIVATED:
551 onNightDisplayActivated(mNightDisplayController.isActivated());
552 break;
553 case Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE:
554 onNightDisplayColorTemperatureChanged(
555 mNightDisplayController.getColorTemperature());
556 break;
557 case Secure.NIGHT_DISPLAY_AUTO_MODE:
558 onNightDisplayAutoModeChanged(
559 mNightDisplayController.getAutoMode());
560 break;
561 case Secure.NIGHT_DISPLAY_CUSTOM_START_TIME:
562 onNightDisplayCustomStartTimeChanged(
563 mNightDisplayController.getCustomStartTime());
564 break;
565 case Secure.NIGHT_DISPLAY_CUSTOM_END_TIME:
566 onNightDisplayCustomEndTimeChanged(
567 mNightDisplayController.getCustomEndTime());
568 break;
569 case System.DISPLAY_COLOR_MODE:
570 onDisplayColorModeChanged(mNightDisplayController.getColorMode());
571 break;
Christine Franks57fdde82018-07-03 14:46:07 -0700572 case Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED:
Christine Franks9114f462019-01-04 11:27:30 -0800573 onAccessibilityInversionChanged();
574 onAccessibilityActivated();
575 break;
576 case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED:
577 onAccessibilityDaltonizerChanged();
578 onAccessibilityActivated();
579 break;
580 case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER:
581 onAccessibilityDaltonizerChanged();
Christine Franks57fdde82018-07-03 14:46:07 -0700582 break;
Christine Franks245ffd42018-11-16 13:45:14 -0800583 case Secure.DISPLAY_WHITE_BALANCE_ENABLED:
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800584 updateDisplayWhiteBalanceStatus();
Christine Franks245ffd42018-11-16 13:45:14 -0800585 break;
Christine Franks57fdde82018-07-03 14:46:07 -0700586 }
587 }
588 }
589 };
590 }
591 final ContentResolver cr = getContext().getContentResolver();
592 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_ACTIVATED),
593 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
594 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE),
595 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
596 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_AUTO_MODE),
597 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
598 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_START_TIME),
599 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
600 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_END_TIME),
601 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
602 cr.registerContentObserver(System.getUriFor(System.DISPLAY_COLOR_MODE),
603 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
604 cr.registerContentObserver(
605 Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED),
606 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
607 cr.registerContentObserver(
608 Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED),
609 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
Christine Franks9114f462019-01-04 11:27:30 -0800610 cr.registerContentObserver(
611 Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER),
612 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
Christine Franks245ffd42018-11-16 13:45:14 -0800613 cr.registerContentObserver(Secure.getUriFor(Secure.DISPLAY_WHITE_BALANCE_ENABLED),
614 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
Justin Klaassen911e8892016-06-21 18:24:24 -0700615
Christine Frankscf388c22018-05-15 15:48:10 -0700616 // Set the color mode, if valid, and immediately apply the updated tint matrix based on the
617 // existing activated state. This ensures consistency of tint across the color mode change.
Christine Franks57fdde82018-07-03 14:46:07 -0700618 onDisplayColorModeChanged(mNightDisplayController.getColorMode());
Christine Frankscf388c22018-05-15 15:48:10 -0700619
Christine Franks245ffd42018-11-16 13:45:14 -0800620 if (ColorDisplayManager.isNightDisplayAvailable(getContext())) {
621 // Reset the activated state.
622 mNightDisplayTintController.setActivated(null);
Christine Frankscf388c22018-05-15 15:48:10 -0700623
Christine Franks245ffd42018-11-16 13:45:14 -0800624 // Prepare the night display color transformation matrix.
625 mNightDisplayTintController
626 .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix());
627 mNightDisplayTintController.setMatrix(mNightDisplayController.getColorTemperature());
Christine Franks8ad71492017-10-24 19:04:22 -0700628
Christine Franks245ffd42018-11-16 13:45:14 -0800629 // Initialize the current auto mode.
630 onNightDisplayAutoModeChanged(mNightDisplayController.getAutoMode());
Christine Franks6418d0b2017-02-13 09:48:00 -0800631
Christine Franks245ffd42018-11-16 13:45:14 -0800632 // Force the initialization current activated state.
633 if (mNightDisplayTintController.isActivatedStateNotSet()) {
634 onNightDisplayActivated(mNightDisplayController.isActivated());
635 }
636 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700637
Christine Franks245ffd42018-11-16 13:45:14 -0800638 if (ColorDisplayManager.isDisplayWhiteBalanceAvailable(getContext())) {
639 // Prepare the display white balance transform matrix.
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800640 mDisplayWhiteBalanceTintController.setUp(getContext(), true /* needsLinear */);
Christine Franks245ffd42018-11-16 13:45:14 -0800641
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800642 updateDisplayWhiteBalanceStatus();
Justin Klaassen911e8892016-06-21 18:24:24 -0700643 }
644 }
645
Justin Klaassen2696d992016-07-11 21:26:46 -0700646 private void tearDown() {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700647 Slog.d(TAG, "tearDown: currentUser=" + mCurrentUser);
648
Christine Franks57fdde82018-07-03 14:46:07 -0700649 getContext().getContentResolver().unregisterContentObserver(mContentObserver);
650
651 if (mNightDisplayController != null) {
652 mNightDisplayController = null;
Justin Klaassen2696d992016-07-11 21:26:46 -0700653 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700654
Christine Franks245ffd42018-11-16 13:45:14 -0800655 if (ColorDisplayManager.isNightDisplayAvailable(getContext())) {
656 if (mNightDisplayAutoMode != null) {
657 mNightDisplayAutoMode.onStop();
658 mNightDisplayAutoMode = null;
659 }
660 mNightDisplayTintController.endAnimator();
Justin Klaassen911e8892016-06-21 18:24:24 -0700661 }
662
Christine Franks245ffd42018-11-16 13:45:14 -0800663 if (ColorDisplayManager.isDisplayWhiteBalanceAvailable(getContext())) {
664 mDisplayWhiteBalanceTintController.endAnimator();
Justin Klaassen639214e2016-07-14 21:00:06 -0700665 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700666 }
667
Christine Franks57fdde82018-07-03 14:46:07 -0700668 private void onNightDisplayActivated(boolean activated) {
Christine Franks245ffd42018-11-16 13:45:14 -0800669 if (mNightDisplayTintController.isActivatedStateNotSet()
670 || mNightDisplayTintController.isActivated() != activated) {
Justin Klaassen911e8892016-06-21 18:24:24 -0700671 Slog.i(TAG, activated ? "Turning on night display" : "Turning off night display");
672
Christine Franks245ffd42018-11-16 13:45:14 -0800673 mNightDisplayTintController.setActivated(activated);
Justin Klaassen908b86c2016-08-08 09:18:42 -0700674
Christine Franks57fdde82018-07-03 14:46:07 -0700675 if (mNightDisplayAutoMode != null) {
676 mNightDisplayAutoMode.onActivated(activated);
Christine Frankse5bb03e2017-02-10 17:36:10 -0800677 }
678
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800679 updateDisplayWhiteBalanceStatus();
680
Christine Franks245ffd42018-11-16 13:45:14 -0800681 applyTint(mNightDisplayTintController, false);
Justin Klaassen911e8892016-06-21 18:24:24 -0700682 }
683 }
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 Franks5397f032017-11-01 18:35:16 -0700693 if (autoMode == ColorDisplayController.AUTO_MODE_CUSTOM) {
Christine Franks57fdde82018-07-03 14:46:07 -0700694 mNightDisplayAutoMode = new CustomNightDisplayAutoMode();
Christine Franks5397f032017-11-01 18:35:16 -0700695 } else if (autoMode == ColorDisplayController.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 onNightDisplayColorTemperatureChanged(int colorTemperature) {
Christine Franks245ffd42018-11-16 13:45:14 -0800721 mNightDisplayTintController.setMatrix(colorTemperature);
722 applyTint(mNightDisplayTintController, true);
Christine Franks6418d0b2017-02-13 09:48:00 -0800723 }
724
Christine Franks57fdde82018-07-03 14:46:07 -0700725 private void onDisplayColorModeChanged(int mode) {
Christine Frankscf388c22018-05-15 15:48:10 -0700726 if (mode == -1) {
727 return;
728 }
729
Christine Franks245ffd42018-11-16 13:45:14 -0800730 mNightDisplayTintController.cancelAnimator();
731 mDisplayWhiteBalanceTintController.cancelAnimator();
732
733 mNightDisplayTintController
734 .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
735 mNightDisplayTintController.setMatrix(mNightDisplayController.getColorTemperature());
736
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800737 updateDisplayWhiteBalanceStatus();
Christine Franks8ad71492017-10-24 19:04:22 -0700738
Christine Franks218e6562017-11-27 10:20:14 -0800739 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
Christine Franks245ffd42018-11-16 13:45:14 -0800740 dtm.setColorMode(mode, mNightDisplayTintController.getMatrix());
Christine Franks8ad71492017-10-24 19:04:22 -0700741 }
742
Christine Franks9114f462019-01-04 11:27:30 -0800743 private void onAccessibilityActivated() {
Christine Franks57fdde82018-07-03 14:46:07 -0700744 onDisplayColorModeChanged(mNightDisplayController.getColorMode());
Daniel Solomon317a3572018-03-30 18:36:37 -0700745 }
746
Christine Franks9114f462019-01-04 11:27:30 -0800747 /**
748 * Apply the accessibility daltonizer transform based on the settings value.
749 */
750 private void onAccessibilityDaltonizerChanged() {
751 final boolean enabled = Secure.getIntForUser(getContext().getContentResolver(),
752 Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, mCurrentUser) != 0;
753 final int daltonizerMode = enabled ? Secure.getIntForUser(getContext().getContentResolver(),
754 Secure.ACCESSIBILITY_DISPLAY_DALTONIZER,
755 AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY, mCurrentUser)
756 : AccessibilityManager.DALTONIZER_DISABLED;
757
758 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
759 if (daltonizerMode == AccessibilityManager.DALTONIZER_SIMULATE_MONOCHROMACY) {
760 // Monochromacy isn't supported by the native Daltonizer implementation; use grayscale.
761 dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_GRAYSCALE,
762 MATRIX_GRAYSCALE);
763 dtm.setDaltonizerMode(AccessibilityManager.DALTONIZER_DISABLED);
764 } else {
765 dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_GRAYSCALE, null);
766 dtm.setDaltonizerMode(daltonizerMode);
767 }
768 }
769
770 /**
771 * Apply the accessibility inversion transform based on the settings value.
772 */
773 private void onAccessibilityInversionChanged() {
774 final boolean enabled = Secure.getIntForUser(getContext().getContentResolver(),
775 Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, mCurrentUser) != 0;
776 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
777 dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_INVERT_COLOR,
778 enabled ? MATRIX_INVERT_COLOR : null);
779 }
Christine Franks8ad71492017-10-24 19:04:22 -0700780
Christine Franks6418d0b2017-02-13 09:48:00 -0800781 /**
782 * Applies current color temperature matrix, or removes it if deactivated.
783 *
784 * @param immediate {@code true} skips transition animation
785 */
Christine Franks245ffd42018-11-16 13:45:14 -0800786 private void applyTint(TintController tintController, boolean immediate) {
787 tintController.cancelAnimator();
Christine Franks6418d0b2017-02-13 09:48:00 -0800788
Christine Franks6418d0b2017-02-13 09:48:00 -0800789 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
Christine Franks245ffd42018-11-16 13:45:14 -0800790 final float[] from = dtm.getColorMatrix(tintController.getLevel());
791 final float[] to = tintController.getMatrix();
Christine Franks6418d0b2017-02-13 09:48:00 -0800792
793 if (immediate) {
Christine Franks245ffd42018-11-16 13:45:14 -0800794 dtm.setColorMatrix(tintController.getLevel(), to);
Christine Franks6418d0b2017-02-13 09:48:00 -0800795 } else {
Christine Franks245ffd42018-11-16 13:45:14 -0800796 tintController.setAnimator(ValueAnimator.ofObject(COLOR_MATRIX_EVALUATOR,
797 from == null ? MATRIX_IDENTITY : from, to));
798 tintController.getAnimator().setDuration(TRANSITION_DURATION);
799 tintController.getAnimator().setInterpolator(AnimationUtils.loadInterpolator(
Christine Franks6418d0b2017-02-13 09:48:00 -0800800 getContext(), android.R.interpolator.fast_out_slow_in));
Christine Franks245ffd42018-11-16 13:45:14 -0800801 tintController.getAnimator().addUpdateListener((ValueAnimator animator) -> {
802 final float[] value = (float[]) animator.getAnimatedValue();
803 dtm.setColorMatrix(tintController.getLevel(), value);
Christine Franks6418d0b2017-02-13 09:48:00 -0800804 });
Christine Franks245ffd42018-11-16 13:45:14 -0800805 tintController.getAnimator().addListener(new AnimatorListenerAdapter() {
Christine Franks6418d0b2017-02-13 09:48:00 -0800806
807 private boolean mIsCancelled;
808
809 @Override
810 public void onAnimationCancel(Animator animator) {
811 mIsCancelled = true;
812 }
813
814 @Override
815 public void onAnimationEnd(Animator animator) {
816 if (!mIsCancelled) {
817 // Ensure final color matrix is set at the end of the animation. If the
818 // animation is cancelled then don't set the final color matrix so the new
819 // animator can pick up from where this one left off.
Christine Franks245ffd42018-11-16 13:45:14 -0800820 dtm.setColorMatrix(tintController.getLevel(), to);
Christine Franks6418d0b2017-02-13 09:48:00 -0800821 }
Christine Franks245ffd42018-11-16 13:45:14 -0800822 tintController.setAnimator(null);
Christine Franks6418d0b2017-02-13 09:48:00 -0800823 }
824 });
Christine Franks245ffd42018-11-16 13:45:14 -0800825 tintController.getAnimator().start();
Christine Franks6418d0b2017-02-13 09:48:00 -0800826 }
827 }
828
829 /**
Christine Franks39b03112018-07-03 14:46:07 -0700830 * Returns the first date time corresponding to the local time that occurs before the provided
831 * date time.
Christine Franks03213462017-08-25 13:57:26 -0700832 *
833 * @param compareTime the LocalDateTime to compare against
834 * @return the prior LocalDateTime corresponding to this local time
835 */
Christine Franks57fdde82018-07-03 14:46:07 -0700836 @VisibleForTesting
837 static LocalDateTime getDateTimeBefore(LocalTime localTime, LocalDateTime compareTime) {
Christine Franks03213462017-08-25 13:57:26 -0700838 final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
839 compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
840
841 // Check if the local time has passed, if so return the same time yesterday.
842 return ldt.isAfter(compareTime) ? ldt.minusDays(1) : ldt;
843 }
844
845 /**
Christine Franks39b03112018-07-03 14:46:07 -0700846 * Returns the first date time corresponding to this local time that occurs after the provided
847 * date time.
Christine Franks03213462017-08-25 13:57:26 -0700848 *
849 * @param compareTime the LocalDateTime to compare against
850 * @return the next LocalDateTime corresponding to this local time
851 */
Christine Franks57fdde82018-07-03 14:46:07 -0700852 @VisibleForTesting
853 static LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) {
Christine Franks03213462017-08-25 13:57:26 -0700854 final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
855 compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
856
857 // Check if the local time has passed, if so return the same time tomorrow.
858 return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
859 }
860
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800861 private void updateDisplayWhiteBalanceStatus() {
862 boolean oldActivated = mDisplayWhiteBalanceTintController.isActivated();
863 mDisplayWhiteBalanceTintController.setActivated(isDisplayWhiteBalanceSettingEnabled() &&
864 !mNightDisplayTintController.isActivated() &&
865 DisplayTransformManager.needsLinearColorMatrix());
866 boolean activated = mDisplayWhiteBalanceTintController.isActivated();
867
868 if (mDisplayWhiteBalanceListener != null && oldActivated != activated) {
869 mDisplayWhiteBalanceListener.onDisplayWhiteBalanceStatusChanged(activated);
Christine Franks245ffd42018-11-16 13:45:14 -0800870 }
871 }
872
873 private boolean isDisplayWhiteBalanceSettingEnabled() {
874 return Secure.getIntForUser(getContext().getContentResolver(),
875 Secure.DISPLAY_WHITE_BALANCE_ENABLED, 0, mCurrentUser) == 1;
876 }
877
Christine Franks39b03112018-07-03 14:46:07 -0700878 private boolean isDeviceColorManagedInternal() {
879 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
880 return dtm.isDeviceColorManaged();
881 }
882
Christine Franks55194dc2019-01-15 13:47:06 -0800883 private int getTransformCapabilitiesInternal() {
884 int availabilityFlags = ColorDisplayManager.CAPABILITY_NONE;
885 if (SurfaceControl.getProtectedContentSupport()) {
886 availabilityFlags |= ColorDisplayManager.CAPABILITY_PROTECTED_CONTENT;
887 }
888 final Resources res = getContext().getResources();
889 if (res.getBoolean(R.bool.config_setColorTransformAccelerated)) {
890 availabilityFlags |= ColorDisplayManager.CAPABILITY_HARDWARE_ACCELERATION_GLOBAL;
891 }
892 if (res.getBoolean(R.bool.config_setColorTransformAcceleratedPerLayer)) {
893 availabilityFlags |= ColorDisplayManager.CAPABILITY_HARDWARE_ACCELERATION_PER_APP;
894 }
895 return availabilityFlags;
896 }
897
Christine Franks57fdde82018-07-03 14:46:07 -0700898 /**
899 * Returns the last time the night display transform activation state was changed, or {@link
900 * LocalDateTime#MIN} if night display has never been activated.
901 */
Christine Franks245ffd42018-11-16 13:45:14 -0800902 private LocalDateTime getNightDisplayLastActivatedTimeSetting() {
Christine Franks57fdde82018-07-03 14:46:07 -0700903 final ContentResolver cr = getContext().getContentResolver();
904 final String lastActivatedTime = Secure.getStringForUser(
905 cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, getContext().getUserId());
906 if (lastActivatedTime != null) {
907 try {
908 return LocalDateTime.parse(lastActivatedTime);
909 } catch (DateTimeParseException ignored) {
910 }
911 // Uses the old epoch time.
912 try {
913 return LocalDateTime.ofInstant(
914 Instant.ofEpochMilli(Long.parseLong(lastActivatedTime)),
915 ZoneId.systemDefault());
916 } catch (DateTimeException | NumberFormatException ignored) {
917 }
918 }
919 return LocalDateTime.MIN;
920 }
921
Christine Franksf3529b22019-01-03 13:20:17 -0800922 private boolean setAppSaturationLevelInternal(String packageName, int saturationLevel) {
923 return mAppSaturationController
924 .setSaturationLevel(packageName, mCurrentUser, saturationLevel);
925 }
926
927 private void dumpInternal(PrintWriter pw) {
928 pw.println("COLOR DISPLAY MANAGER dumpsys (color_display)");
929 pw.println("Night Display:");
930 if (ColorDisplayManager.isNightDisplayAvailable(getContext())) {
931 pw.println(" Activated: " + mNightDisplayTintController.isActivated());
932 } else {
933 pw.println(" Not available");
934 }
935 mAppSaturationController.dump(pw);
936 }
937
Christine Franks57fdde82018-07-03 14:46:07 -0700938 private abstract class NightDisplayAutoMode {
939
940 public abstract void onActivated(boolean activated);
941
Justin Klaassen911e8892016-06-21 18:24:24 -0700942 public abstract void onStart();
Christine Frankse5bb03e2017-02-10 17:36:10 -0800943
Justin Klaassen911e8892016-06-21 18:24:24 -0700944 public abstract void onStop();
Christine Franks57fdde82018-07-03 14:46:07 -0700945
946 public void onCustomStartTimeChanged(LocalTime startTime) {
947 }
948
949 public void onCustomEndTimeChanged(LocalTime endTime) {
950 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700951 }
952
Christine Franks57fdde82018-07-03 14:46:07 -0700953 private final class CustomNightDisplayAutoMode extends NightDisplayAutoMode implements
954 AlarmManager.OnAlarmListener {
Justin Klaassen911e8892016-06-21 18:24:24 -0700955
956 private final AlarmManager mAlarmManager;
957 private final BroadcastReceiver mTimeChangedReceiver;
958
Christine Franks03213462017-08-25 13:57:26 -0700959 private LocalTime mStartTime;
960 private LocalTime mEndTime;
Justin Klaassen911e8892016-06-21 18:24:24 -0700961
Christine Franks03213462017-08-25 13:57:26 -0700962 private LocalDateTime mLastActivatedTime;
Justin Klaassen911e8892016-06-21 18:24:24 -0700963
Christine Franks57fdde82018-07-03 14:46:07 -0700964 CustomNightDisplayAutoMode() {
Justin Klaassen911e8892016-06-21 18:24:24 -0700965 mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
966 mTimeChangedReceiver = new BroadcastReceiver() {
967 @Override
968 public void onReceive(Context context, Intent intent) {
969 updateActivated();
970 }
971 };
972 }
973
974 private void updateActivated() {
Christine Franks03213462017-08-25 13:57:26 -0700975 final LocalDateTime now = LocalDateTime.now();
976 final LocalDateTime start = getDateTimeBefore(mStartTime, now);
977 final LocalDateTime end = getDateTimeAfter(mEndTime, start);
978 boolean activate = now.isBefore(end);
Justin Klaassen911e8892016-06-21 18:24:24 -0700979
Christine Frankse5bb03e2017-02-10 17:36:10 -0800980 if (mLastActivatedTime != null) {
Christine Frankse5bb03e2017-02-10 17:36:10 -0800981 // Maintain the existing activated state if within the current period.
Christine Franks03213462017-08-25 13:57:26 -0700982 if (mLastActivatedTime.isBefore(now) && mLastActivatedTime.isAfter(start)
983 && (mLastActivatedTime.isAfter(end) || now.isBefore(end))) {
Christine Franks57fdde82018-07-03 14:46:07 -0700984 activate = mNightDisplayController.isActivated();
Justin Klaassen911e8892016-06-21 18:24:24 -0700985 }
986 }
987
Christine Franks245ffd42018-11-16 13:45:14 -0800988 if (mNightDisplayTintController.isActivatedStateNotSet() || (
989 mNightDisplayTintController.isActivated() != activate)) {
Christine Franks57fdde82018-07-03 14:46:07 -0700990 mNightDisplayController.setActivated(activate);
Justin Klaassen911e8892016-06-21 18:24:24 -0700991 }
Christine Franks03213462017-08-25 13:57:26 -0700992
Christine Franks245ffd42018-11-16 13:45:14 -0800993 updateNextAlarm(mNightDisplayTintController.isActivated(), now);
Justin Klaassen911e8892016-06-21 18:24:24 -0700994 }
995
Christine Franks03213462017-08-25 13:57:26 -0700996 private void updateNextAlarm(@Nullable Boolean activated, @NonNull LocalDateTime now) {
Justin Klaassen4346f632016-08-08 15:01:47 -0700997 if (activated != null) {
Christine Franks03213462017-08-25 13:57:26 -0700998 final LocalDateTime next = activated ? getDateTimeAfter(mEndTime, now)
999 : getDateTimeAfter(mStartTime, now);
1000 final long millis = next.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
1001 mAlarmManager.setExact(AlarmManager.RTC, millis, TAG, this, null);
Justin Klaassen911e8892016-06-21 18:24:24 -07001002 }
1003 }
1004
1005 @Override
1006 public void onStart() {
1007 final IntentFilter intentFilter = new IntentFilter(Intent.ACTION_TIME_CHANGED);
1008 intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
1009 getContext().registerReceiver(mTimeChangedReceiver, intentFilter);
1010
Christine Franks57fdde82018-07-03 14:46:07 -07001011 mStartTime = mNightDisplayController.getCustomStartTime();
1012 mEndTime = mNightDisplayController.getCustomEndTime();
Justin Klaassen911e8892016-06-21 18:24:24 -07001013
Christine Franks57fdde82018-07-03 14:46:07 -07001014 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
Christine Frankse5bb03e2017-02-10 17:36:10 -08001015
Justin Klaassen911e8892016-06-21 18:24:24 -07001016 // Force an update to initialize state.
1017 updateActivated();
1018 }
1019
1020 @Override
1021 public void onStop() {
1022 getContext().unregisterReceiver(mTimeChangedReceiver);
1023
1024 mAlarmManager.cancel(this);
1025 mLastActivatedTime = null;
1026 }
1027
1028 @Override
1029 public void onActivated(boolean activated) {
Christine Franks57fdde82018-07-03 14:46:07 -07001030 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
Christine Franks03213462017-08-25 13:57:26 -07001031 updateNextAlarm(activated, LocalDateTime.now());
Justin Klaassen911e8892016-06-21 18:24:24 -07001032 }
1033
1034 @Override
Christine Franks03213462017-08-25 13:57:26 -07001035 public void onCustomStartTimeChanged(LocalTime startTime) {
Justin Klaassen911e8892016-06-21 18:24:24 -07001036 mStartTime = startTime;
1037 mLastActivatedTime = null;
1038 updateActivated();
1039 }
1040
1041 @Override
Christine Franks03213462017-08-25 13:57:26 -07001042 public void onCustomEndTimeChanged(LocalTime endTime) {
Justin Klaassen911e8892016-06-21 18:24:24 -07001043 mEndTime = endTime;
1044 mLastActivatedTime = null;
1045 updateActivated();
1046 }
1047
1048 @Override
1049 public void onAlarm() {
Justin Klaassenec8837a2016-08-23 12:04:42 -07001050 Slog.d(TAG, "onAlarm");
Justin Klaassen911e8892016-06-21 18:24:24 -07001051 updateActivated();
1052 }
1053 }
1054
Christine Franks57fdde82018-07-03 14:46:07 -07001055 private final class TwilightNightDisplayAutoMode extends NightDisplayAutoMode implements
1056 TwilightListener {
Justin Klaassen911e8892016-06-21 18:24:24 -07001057
1058 private final TwilightManager mTwilightManager;
Christine Franks57fdde82018-07-03 14:46:07 -07001059 private LocalDateTime mLastActivatedTime;
Justin Klaassen911e8892016-06-21 18:24:24 -07001060
Christine Franks57fdde82018-07-03 14:46:07 -07001061 TwilightNightDisplayAutoMode() {
Justin Klaassen911e8892016-06-21 18:24:24 -07001062 mTwilightManager = getLocalService(TwilightManager.class);
Justin Klaassen911e8892016-06-21 18:24:24 -07001063 }
1064
Justin Klaassen908b86c2016-08-08 09:18:42 -07001065 private void updateActivated(TwilightState state) {
Justin Klaassen3da4c442017-05-05 15:19:33 -07001066 if (state == null) {
1067 // If there isn't a valid TwilightState then just keep the current activated
1068 // state.
1069 return;
1070 }
1071
1072 boolean activate = state.isNight();
Christine Franks57fdde82018-07-03 14:46:07 -07001073 if (mLastActivatedTime != null) {
Christine Franks03213462017-08-25 13:57:26 -07001074 final LocalDateTime now = LocalDateTime.now();
1075 final LocalDateTime sunrise = state.sunrise();
1076 final LocalDateTime sunset = state.sunset();
Christine Frankse5bb03e2017-02-10 17:36:10 -08001077 // Maintain the existing activated state if within the current period.
Christine Franks57fdde82018-07-03 14:46:07 -07001078 if (mLastActivatedTime.isBefore(now) && (mLastActivatedTime.isBefore(sunrise)
1079 ^ mLastActivatedTime.isBefore(sunset))) {
1080 activate = mNightDisplayController.isActivated();
Justin Klaassen911e8892016-06-21 18:24:24 -07001081 }
1082 }
Justin Klaassen908b86c2016-08-08 09:18:42 -07001083
Christine Franks245ffd42018-11-16 13:45:14 -08001084 if (mNightDisplayTintController.isActivatedStateNotSet() || (
1085 mNightDisplayTintController.isActivated() != activate)) {
Christine Franks57fdde82018-07-03 14:46:07 -07001086 mNightDisplayController.setActivated(activate);
Justin Klaassen908b86c2016-08-08 09:18:42 -07001087 }
Justin Klaassen911e8892016-06-21 18:24:24 -07001088 }
1089
1090 @Override
Christine Franks57fdde82018-07-03 14:46:07 -07001091 public void onActivated(boolean activated) {
1092 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
1093 }
1094
1095 @Override
Justin Klaassen911e8892016-06-21 18:24:24 -07001096 public void onStart() {
1097 mTwilightManager.registerListener(this, mHandler);
Christine Franks57fdde82018-07-03 14:46:07 -07001098 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
Justin Klaassen911e8892016-06-21 18:24:24 -07001099
1100 // Force an update to initialize state.
Justin Klaassen908b86c2016-08-08 09:18:42 -07001101 updateActivated(mTwilightManager.getLastTwilightState());
Justin Klaassen911e8892016-06-21 18:24:24 -07001102 }
1103
1104 @Override
1105 public void onStop() {
1106 mTwilightManager.unregisterListener(this);
Christine Franks57fdde82018-07-03 14:46:07 -07001107 mLastActivatedTime = null;
Justin Klaassen908b86c2016-08-08 09:18:42 -07001108 }
1109
1110 @Override
1111 public void onTwilightStateChanged(@Nullable TwilightState state) {
Justin Klaassenec8837a2016-08-23 12:04:42 -07001112 Slog.d(TAG, "onTwilightStateChanged: isNight="
1113 + (state == null ? null : state.isNight()));
Justin Klaassen908b86c2016-08-08 09:18:42 -07001114 updateActivated(state);
Justin Klaassen911e8892016-06-21 18:24:24 -07001115 }
1116 }
Justin Klaassen639214e2016-07-14 21:00:06 -07001117
1118 /**
1119 * Interpolates between two 4x4 color transform matrices (in column-major order).
1120 */
1121 private static class ColorMatrixEvaluator implements TypeEvaluator<float[]> {
1122
1123 /**
1124 * Result matrix returned by {@link #evaluate(float, float[], float[])}.
1125 */
1126 private final float[] mResultMatrix = new float[16];
1127
1128 @Override
1129 public float[] evaluate(float fraction, float[] startValue, float[] endValue) {
1130 for (int i = 0; i < mResultMatrix.length; i++) {
1131 mResultMatrix[i] = MathUtils.lerp(startValue[i], endValue[i], fraction);
1132 }
1133 return mResultMatrix;
1134 }
1135 }
Christine Franks39b03112018-07-03 14:46:07 -07001136
Christine Franks245ffd42018-11-16 13:45:14 -08001137 private abstract static class TintController {
1138
1139 private ValueAnimator mAnimator;
1140 private Boolean mIsActivated;
1141
1142 public ValueAnimator getAnimator() {
1143 return mAnimator;
1144 }
1145
1146 public void setAnimator(ValueAnimator animator) {
1147 mAnimator = animator;
1148 }
1149
1150 /**
1151 * Cancel the animator if it's still running.
1152 */
1153 public void cancelAnimator() {
1154 if (mAnimator != null) {
1155 mAnimator.cancel();
1156 }
1157 }
1158
1159 /**
1160 * End the animator if it's still running, jumping to the end state.
1161 */
1162 public void endAnimator() {
1163 if (mAnimator != null) {
1164 mAnimator.end();
1165 mAnimator = null;
1166 }
1167 }
1168
1169 public void setActivated(Boolean isActivated) {
1170 mIsActivated = isActivated;
1171 }
1172
1173 public boolean isActivated() {
1174 return mIsActivated != null && mIsActivated;
1175 }
1176
1177 public boolean isActivatedStateNotSet() {
1178 return mIsActivated == null;
1179 }
1180
1181 /**
Daniel Solomon8b72c5b2018-11-25 11:07:13 -08001182 * Dump debug information.
1183 */
1184 public void dump(PrintWriter pw) {
1185 }
1186
1187 /**
Christine Franks245ffd42018-11-16 13:45:14 -08001188 * Set up any constants needed for computing the matrix.
1189 */
1190 public abstract void setUp(Context context, boolean needsLinear);
1191
1192 /**
1193 * Sets the 4x4 matrix to apply.
1194 */
1195 public abstract void setMatrix(int value);
1196
1197 /**
1198 * Get the 4x4 matrix to apply.
1199 */
1200 public abstract float[] getMatrix();
1201
1202 /**
1203 * Get the color transform level to apply the matrix.
1204 */
1205 public abstract int getLevel();
1206 }
1207
1208 /**
1209 * Local service that allows color transforms to be enabled from other system services.
1210 */
1211 public final class ColorDisplayServiceInternal {
1212
1213 /**
1214 * Set the current CCT value for the display white balance transform, and if the transform
1215 * is enabled, apply it.
1216 *
1217 * @param cct the color temperature in Kelvin.
1218 */
1219 public boolean setDisplayWhiteBalanceColorTemperature(int cct) {
1220 // Update the transform matrix even if it can't be applied.
Christine Franks245ffd42018-11-16 13:45:14 -08001221 mDisplayWhiteBalanceTintController.setMatrix(cct);
1222
1223 if (mDisplayWhiteBalanceTintController.isActivated()) {
Daniel Solomon8b72c5b2018-11-25 11:07:13 -08001224 applyTint(mDisplayWhiteBalanceTintController, false);
Christine Franks245ffd42018-11-16 13:45:14 -08001225 return true;
1226 }
1227 return false;
1228 }
1229
1230 /**
1231 * Sets the listener and returns whether display white balance is currently enabled.
1232 */
1233 public boolean setDisplayWhiteBalanceListener(DisplayWhiteBalanceListener listener) {
1234 mDisplayWhiteBalanceListener = listener;
1235 return mDisplayWhiteBalanceTintController.isActivated();
1236 }
Daniel Solomon8b72c5b2018-11-25 11:07:13 -08001237
1238 public void dump(PrintWriter pw) {
1239 mDisplayWhiteBalanceTintController.dump(pw);
1240 }
Christine Franksf3529b22019-01-03 13:20:17 -08001241
1242 /**
1243 * Adds a {@link WeakReference<ColorTransformController>} for a newly started activity, and
1244 * invokes {@link ColorTransformController#applyAppSaturation(float[], float[])} if needed.
1245 */
Christine Franks55194dc2019-01-15 13:47:06 -08001246 public boolean attachColorTransformController(String packageName, @UserIdInt int userId,
Christine Franksf3529b22019-01-03 13:20:17 -08001247 WeakReference<ColorTransformController> controller) {
1248 return mAppSaturationController
Christine Franks55194dc2019-01-15 13:47:06 -08001249 .addColorTransformController(packageName, userId, controller);
Christine Franksf3529b22019-01-03 13:20:17 -08001250 }
Christine Franks245ffd42018-11-16 13:45:14 -08001251 }
1252
1253 /**
1254 * Listener for changes in display white balance status.
1255 */
1256 public interface DisplayWhiteBalanceListener {
1257
1258 /**
1259 * Notify that the display white balance status has changed, either due to preemption by
1260 * another transform or the feature being turned off.
1261 */
1262 void onDisplayWhiteBalanceStatusChanged(boolean enabled);
1263 }
1264
Christine Franks09c229e2018-12-14 10:37:40 -08001265 private final class TintHandler extends Handler {
1266
1267 TintHandler(Looper looper) {
1268 super(looper, null, true /* async */);
1269 }
1270
1271 @Override
1272 public void handleMessage(Message msg) {
1273 switch (msg.what) {
1274 case MSG_APPLY_GLOBAL_SATURATION:
1275 mGlobalSaturationTintController.setMatrix(msg.arg1);
1276 applyTint(mGlobalSaturationTintController, false);
1277 break;
1278 }
1279 }
1280 }
1281
Christine Franksf3529b22019-01-03 13:20:17 -08001282 /**
1283 * Interface for applying transforms to a given AppWindow.
1284 */
1285 public interface ColorTransformController {
1286
1287 /** Apply the given saturation (grayscale) matrix to the associated AppWindow. */
1288 void applyAppSaturation(@Size(9) float[] matrix, @Size(3) float[] translation);
1289 }
1290
Christine Franks39b03112018-07-03 14:46:07 -07001291 private final class BinderService extends IColorDisplayManager.Stub {
Christine Franks57fdde82018-07-03 14:46:07 -07001292
Christine Franks39b03112018-07-03 14:46:07 -07001293 @Override
1294 public boolean isDeviceColorManaged() {
1295 final long token = Binder.clearCallingIdentity();
1296 try {
1297 return isDeviceColorManagedInternal();
1298 } finally {
1299 Binder.restoreCallingIdentity(token);
1300 }
1301 }
Christine Franks09c229e2018-12-14 10:37:40 -08001302
1303 @Override
1304 public boolean setSaturationLevel(int level) {
1305 final boolean hasTransformsPermission = getContext()
1306 .checkCallingPermission(Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
1307 == PackageManager.PERMISSION_GRANTED;
1308 final boolean hasLegacyPermission = getContext()
1309 .checkCallingPermission(Manifest.permission.CONTROL_DISPLAY_SATURATION)
1310 == PackageManager.PERMISSION_GRANTED;
1311 if (!hasTransformsPermission && !hasLegacyPermission) {
1312 throw new SecurityException("Permission required to set display saturation level");
1313 }
1314 final long token = Binder.clearCallingIdentity();
1315 try {
1316 final Message message = mHandler.obtainMessage(MSG_APPLY_GLOBAL_SATURATION);
1317 message.arg1 = level;
1318 mHandler.sendMessage(message);
1319 } finally {
1320 Binder.restoreCallingIdentity(token);
1321 }
1322 return true;
1323 }
Christine Franksf3529b22019-01-03 13:20:17 -08001324
1325 @Override
1326 public boolean setAppSaturationLevel(String packageName, int level) {
1327 getContext().enforceCallingPermission(
1328 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1329 "Permission required to set display saturation level");
1330 final long token = Binder.clearCallingIdentity();
1331 try {
1332 return setAppSaturationLevelInternal(packageName, level);
1333 } finally {
1334 Binder.restoreCallingIdentity(token);
1335 }
1336 }
1337
Christine Franks55194dc2019-01-15 13:47:06 -08001338 public int getTransformCapabilities() {
1339 getContext().enforceCallingPermission(
1340 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1341 "Permission required to query transform capabilities");
1342 final long token = Binder.clearCallingIdentity();
1343 try {
1344 return getTransformCapabilitiesInternal();
1345 } finally {
1346 Binder.restoreCallingIdentity(token);
1347 }
1348 }
1349
Christine Franksf3529b22019-01-03 13:20:17 -08001350 @Override
1351 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1352 if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
1353
1354 final long token = Binder.clearCallingIdentity();
1355 try {
1356 dumpInternal(pw);
1357 } finally {
1358 Binder.restoreCallingIdentity(token);
1359 }
1360 }
Christine Franks39b03112018-07-03 14:46:07 -07001361 }
Justin Klaassen911e8892016-06-21 18:24:24 -07001362}