blob: 27acb6bb485dcd333cc9e61c93782651dfc668d7 [file] [log] [blame]
Justin Klaassen911e8892016-06-21 18:24:24 -07001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Christine Franks0ada2772019-02-25 13:54:57 -080017package com.android.server.display.color;
Justin Klaassen911e8892016-06-21 18:24:24 -070018
Christine Franks83cc5412018-07-03 14:46:07 -070019import static android.hardware.display.ColorDisplayManager.AUTO_MODE_CUSTOM_TIME;
20import static android.hardware.display.ColorDisplayManager.AUTO_MODE_DISABLED;
21import static android.hardware.display.ColorDisplayManager.AUTO_MODE_TWILIGHT;
Christine Franksd154fe52019-01-04 17:17:45 -080022import static android.hardware.display.ColorDisplayManager.COLOR_MODE_AUTOMATIC;
23import static android.hardware.display.ColorDisplayManager.COLOR_MODE_BOOSTED;
24import static android.hardware.display.ColorDisplayManager.COLOR_MODE_NATURAL;
25import static android.hardware.display.ColorDisplayManager.COLOR_MODE_SATURATED;
Christine Franks531c0612019-05-16 14:29:14 -070026import static android.hardware.display.ColorDisplayManager.VENDOR_COLOR_MODE_RANGE_MAX;
27import static android.hardware.display.ColorDisplayManager.VENDOR_COLOR_MODE_RANGE_MIN;
Christine Franksa4ed3762019-01-24 15:37:04 -080028
Christine Franks0ada2772019-02-25 13:54:57 -080029import static com.android.server.display.color.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
Christine Franks39b03112018-07-03 14:46:07 -070030
Christine Franks09c229e2018-12-14 10:37:40 -080031import android.Manifest;
Justin Klaassen639214e2016-07-14 21:00:06 -070032import android.animation.Animator;
33import android.animation.AnimatorListenerAdapter;
34import android.animation.TypeEvaluator;
35import android.animation.ValueAnimator;
Justin Klaassen4346f632016-08-08 15:01:47 -070036import android.annotation.NonNull;
Justin Klaassen908b86c2016-08-08 09:18:42 -070037import android.annotation.Nullable;
Christine Franksf3529b22019-01-03 13:20:17 -080038import android.annotation.Size;
Christine Franks55194dc2019-01-15 13:47:06 -080039import android.annotation.UserIdInt;
Justin Klaassen911e8892016-06-21 18:24:24 -070040import android.app.AlarmManager;
41import android.content.BroadcastReceiver;
42import android.content.ContentResolver;
43import android.content.Context;
44import android.content.Intent;
45import android.content.IntentFilter;
Christine Franks09c229e2018-12-14 10:37:40 -080046import android.content.pm.PackageManager;
Daniel Solomon8b72c5b2018-11-25 11:07:13 -080047import android.content.res.Resources;
Justin Klaassen2696d992016-07-11 21:26:46 -070048import android.database.ContentObserver;
Christine Franksf3529b22019-01-03 13:20:17 -080049import android.hardware.display.ColorDisplayManager;
Christine Franks83cc5412018-07-03 14:46:07 -070050import android.hardware.display.ColorDisplayManager.AutoMode;
Christine Franksd154fe52019-01-04 17:17:45 -080051import android.hardware.display.ColorDisplayManager.ColorMode;
Christine Franks39b03112018-07-03 14:46:07 -070052import android.hardware.display.IColorDisplayManager;
Christine Franks83cc5412018-07-03 14:46:07 -070053import android.hardware.display.Time;
Justin Klaassen2696d992016-07-11 21:26:46 -070054import android.net.Uri;
Justin Klaassen639214e2016-07-14 21:00:06 -070055import android.opengl.Matrix;
Christine Franks39b03112018-07-03 14:46:07 -070056import android.os.Binder;
Justin Klaassen911e8892016-06-21 18:24:24 -070057import android.os.Handler;
58import android.os.Looper;
Christine Franks09c229e2018-12-14 10:37:40 -080059import android.os.Message;
Christine Franksd154fe52019-01-04 17:17:45 -080060import android.os.SystemProperties;
Justin Klaassen911e8892016-06-21 18:24:24 -070061import android.os.UserHandle;
62import android.provider.Settings.Secure;
Christine Franks57fdde82018-07-03 14:46:07 -070063import android.provider.Settings.System;
Justin Klaassen639214e2016-07-14 21:00:06 -070064import android.util.MathUtils;
Justin Klaassen911e8892016-06-21 18:24:24 -070065import android.util.Slog;
Christine Franks55194dc2019-01-15 13:47:06 -080066import android.view.SurfaceControl;
Christine Franks9114f462019-01-04 11:27:30 -080067import android.view.accessibility.AccessibilityManager;
Justin Klaassen639214e2016-07-14 21:00:06 -070068import android.view.animation.AnimationUtils;
Daniel Solomona4ab5672019-01-22 19:35:55 -080069
Christine Franks39b03112018-07-03 14:46:07 -070070import com.android.internal.R;
Christine Franks57fdde82018-07-03 14:46:07 -070071import com.android.internal.annotations.VisibleForTesting;
Christine Franksf3529b22019-01-03 13:20:17 -080072import com.android.internal.util.DumpUtils;
Christine Franks57fdde82018-07-03 14:46:07 -070073import com.android.server.DisplayThread;
Justin Klaassen911e8892016-06-21 18:24:24 -070074import com.android.server.SystemService;
75import com.android.server.twilight.TwilightListener;
76import com.android.server.twilight.TwilightManager;
77import com.android.server.twilight.TwilightState;
Christine Franksa4ed3762019-01-24 15:37:04 -080078
Christine Franksf3529b22019-01-03 13:20:17 -080079import java.io.FileDescriptor;
Daniel Solomon8b72c5b2018-11-25 11:07:13 -080080import java.io.PrintWriter;
Christine Franksf3529b22019-01-03 13:20:17 -080081import java.lang.ref.WeakReference;
Christine Franks57fdde82018-07-03 14:46:07 -070082import java.time.DateTimeException;
83import java.time.Instant;
Christine Franks03213462017-08-25 13:57:26 -070084import java.time.LocalDateTime;
85import java.time.LocalTime;
86import java.time.ZoneId;
Christine Franks57fdde82018-07-03 14:46:07 -070087import java.time.format.DateTimeParseException;
Christine Franks8ad71492017-10-24 19:04:22 -070088
Justin Klaassen911e8892016-06-21 18:24:24 -070089/**
Christine Franks39b03112018-07-03 14:46:07 -070090 * Controls the display's color transforms.
Justin Klaassen911e8892016-06-21 18:24:24 -070091 */
Christine Franks57fdde82018-07-03 14:46:07 -070092public final class ColorDisplayService extends SystemService {
Justin Klaassen911e8892016-06-21 18:24:24 -070093
Christine Franks7119e992019-03-14 17:28:21 -070094 static final String TAG = "ColorDisplayService";
Christine Franks7b83b4282017-01-18 14:55:00 -080095
96 /**
Justin Klaassen639214e2016-07-14 21:00:06 -070097 * The identity matrix, used if one of the given matrices is {@code null}.
98 */
Christine Franks6caba0f2019-03-07 17:48:25 -080099 static final float[] MATRIX_IDENTITY = new float[16];
Christine Franks57fdde82018-07-03 14:46:07 -0700100
Justin Klaassen639214e2016-07-14 21:00:06 -0700101 static {
102 Matrix.setIdentityM(MATRIX_IDENTITY, 0);
103 }
104
Christine Franks7119e992019-03-14 17:28:21 -0700105 /**
106 * The transition time, in milliseconds, for Night Display to turn on/off.
107 */
108 private static final long TRANSITION_DURATION = 3000L;
109
Christine Franks27912a32019-04-02 10:43:10 -0700110 private static final int MSG_USER_CHANGED = 0;
111 private static final int MSG_SET_UP = 1;
112 private static final int MSG_APPLY_NIGHT_DISPLAY_IMMEDIATE = 2;
113 private static final int MSG_APPLY_NIGHT_DISPLAY_ANIMATED = 3;
114 private static final int MSG_APPLY_GLOBAL_SATURATION = 4;
115 private static final int MSG_APPLY_DISPLAY_WHITE_BALANCE = 5;
Christine Franks09c229e2018-12-14 10:37:40 -0800116
Justin Klaassen639214e2016-07-14 21:00:06 -0700117 /**
Christine Franks83cc5412018-07-03 14:46:07 -0700118 * Return value if a setting has not been set.
119 */
120 private static final int NOT_SET = -1;
121
122 /**
Justin Klaassen639214e2016-07-14 21:00:06 -0700123 * Evaluator used to animate color matrix transitions.
124 */
125 private static final ColorMatrixEvaluator COLOR_MATRIX_EVALUATOR = new ColorMatrixEvaluator();
126
Christine Franks83cc5412018-07-03 14:46:07 -0700127 private final NightDisplayTintController mNightDisplayTintController =
128 new NightDisplayTintController();
Christine Franks245ffd42018-11-16 13:45:14 -0800129
Long Ling1d3f1892019-02-06 12:34:02 -0800130 @VisibleForTesting
131 final DisplayWhiteBalanceTintController mDisplayWhiteBalanceTintController =
132 new DisplayWhiteBalanceTintController();
Christine Franks245ffd42018-11-16 13:45:14 -0800133
Christine Franks7119e992019-03-14 17:28:21 -0700134 private final TintController mGlobalSaturationTintController =
135 new GlobalSaturationTintController();
Christine Franks09c229e2018-12-14 10:37:40 -0800136
Christine Franks9114f462019-01-04 11:27:30 -0800137 /**
138 * Matrix and offset used for converting color to grayscale.
139 */
140 private static final float[] MATRIX_GRAYSCALE = new float[]{
141 .2126f, .2126f, .2126f, 0f,
142 .7152f, .7152f, .7152f, 0f,
143 .0722f, .0722f, .0722f, 0f,
144 0f, 0f, 0f, 1f
145 };
146
147 /**
148 * Matrix and offset used for luminance inversion. Represents a transform from RGB to YIQ color
149 * space, rotation around the Y axis by 180 degrees, transform back to RGB color space, and
150 * subtraction from 1. The last row represents a non-multiplied addition, see surfaceflinger's
151 * ProgramCache for full implementation details.
152 */
Christine Franksd154fe52019-01-04 17:17:45 -0800153 private static final float[] MATRIX_INVERT_COLOR = new float[]{
Christine Franks9114f462019-01-04 11:27:30 -0800154 0.402f, -0.598f, -0.599f, 0f,
155 -1.174f, -0.174f, -1.175f, 0f,
156 -0.228f, -0.228f, 0.772f, 0f,
157 1f, 1f, 1f, 1f
158 };
159
Justin Klaassen2696d992016-07-11 21:26:46 -0700160 private final Handler mHandler;
161
Christine Franksf3529b22019-01-03 13:20:17 -0800162 private final AppSaturationController mAppSaturationController = new AppSaturationController();
163
Justin Klaassen911e8892016-06-21 18:24:24 -0700164 private int mCurrentUser = UserHandle.USER_NULL;
Justin Klaassen2696d992016-07-11 21:26:46 -0700165 private ContentObserver mUserSetupObserver;
Justin Klaassen911e8892016-06-21 18:24:24 -0700166 private boolean mBootCompleted;
167
Christine Franks57fdde82018-07-03 14:46:07 -0700168 private ContentObserver mContentObserver;
Christine Franks57fdde82018-07-03 14:46:07 -0700169
Christine Franks245ffd42018-11-16 13:45:14 -0800170 private DisplayWhiteBalanceListener mDisplayWhiteBalanceListener;
171
Christine Franks57fdde82018-07-03 14:46:07 -0700172 private NightDisplayAutoMode mNightDisplayAutoMode;
Justin Klaassen911e8892016-06-21 18:24:24 -0700173
Christine Franks5397f032017-11-01 18:35:16 -0700174 public ColorDisplayService(Context context) {
Justin Klaassen911e8892016-06-21 18:24:24 -0700175 super(context);
Christine Franks83cc5412018-07-03 14:46:07 -0700176 mHandler = new TintHandler(DisplayThread.get().getLooper());
Justin Klaassen911e8892016-06-21 18:24:24 -0700177 }
178
179 @Override
180 public void onStart() {
Christine Franks39b03112018-07-03 14:46:07 -0700181 publishBinderService(Context.COLOR_DISPLAY_SERVICE, new BinderService());
Christine Franks245ffd42018-11-16 13:45:14 -0800182 publishLocalService(ColorDisplayServiceInternal.class, new ColorDisplayServiceInternal());
Christine Franks0ada2772019-02-25 13:54:57 -0800183 publishLocalService(DisplayTransformManager.class, new DisplayTransformManager());
Justin Klaassen911e8892016-06-21 18:24:24 -0700184 }
185
186 @Override
Justin Klaassen2696d992016-07-11 21:26:46 -0700187 public void onBootPhase(int phase) {
Christine Frankse5bb03e2017-02-10 17:36:10 -0800188 if (phase >= PHASE_BOOT_COMPLETED) {
Justin Klaassen2696d992016-07-11 21:26:46 -0700189 mBootCompleted = true;
190
191 // Register listeners now that boot is complete.
192 if (mCurrentUser != UserHandle.USER_NULL && mUserSetupObserver == null) {
Christine Franks27912a32019-04-02 10:43:10 -0700193 mHandler.sendEmptyMessage(MSG_SET_UP);
Justin Klaassen2696d992016-07-11 21:26:46 -0700194 }
195 }
196 }
197
198 @Override
Justin Klaassen911e8892016-06-21 18:24:24 -0700199 public void onStartUser(int userHandle) {
200 super.onStartUser(userHandle);
201
Justin Klaassen911e8892016-06-21 18:24:24 -0700202 if (mCurrentUser == UserHandle.USER_NULL) {
Christine Franks27912a32019-04-02 10:43:10 -0700203 final Message message = mHandler.obtainMessage(MSG_USER_CHANGED);
204 message.arg1 = userHandle;
205 mHandler.sendMessage(message);
Justin Klaassen911e8892016-06-21 18:24:24 -0700206 }
207 }
208
209 @Override
210 public void onSwitchUser(int userHandle) {
211 super.onSwitchUser(userHandle);
212
Christine Franks27912a32019-04-02 10:43:10 -0700213 final Message message = mHandler.obtainMessage(MSG_USER_CHANGED);
214 message.arg1 = userHandle;
215 mHandler.sendMessage(message);
Justin Klaassen911e8892016-06-21 18:24:24 -0700216 }
217
218 @Override
219 public void onStopUser(int userHandle) {
220 super.onStopUser(userHandle);
221
Justin Klaassen911e8892016-06-21 18:24:24 -0700222 if (mCurrentUser == userHandle) {
Christine Franks27912a32019-04-02 10:43:10 -0700223 final Message message = mHandler.obtainMessage(MSG_USER_CHANGED);
224 message.arg1 = UserHandle.USER_NULL;
225 mHandler.sendMessage(message);
Justin Klaassen911e8892016-06-21 18:24:24 -0700226 }
227 }
228
Justin Klaassen2696d992016-07-11 21:26:46 -0700229 private void onUserChanged(int userHandle) {
230 final ContentResolver cr = getContext().getContentResolver();
Justin Klaassen911e8892016-06-21 18:24:24 -0700231
Justin Klaassen2696d992016-07-11 21:26:46 -0700232 if (mCurrentUser != UserHandle.USER_NULL) {
233 if (mUserSetupObserver != null) {
234 cr.unregisterContentObserver(mUserSetupObserver);
235 mUserSetupObserver = null;
236 } else if (mBootCompleted) {
237 tearDown();
238 }
239 }
240
241 mCurrentUser = userHandle;
242
243 if (mCurrentUser != UserHandle.USER_NULL) {
244 if (!isUserSetupCompleted(cr, mCurrentUser)) {
245 mUserSetupObserver = new ContentObserver(mHandler) {
246 @Override
247 public void onChange(boolean selfChange, Uri uri) {
248 if (isUserSetupCompleted(cr, mCurrentUser)) {
249 cr.unregisterContentObserver(this);
250 mUserSetupObserver = null;
251
252 if (mBootCompleted) {
253 setUp();
254 }
255 }
256 }
257 };
258 cr.registerContentObserver(Secure.getUriFor(Secure.USER_SETUP_COMPLETE),
Christine Franks39b03112018-07-03 14:46:07 -0700259 false /* notifyForDescendants */, mUserSetupObserver, mCurrentUser);
Justin Klaassen2696d992016-07-11 21:26:46 -0700260 } else if (mBootCompleted) {
261 setUp();
Justin Klaassen911e8892016-06-21 18:24:24 -0700262 }
263 }
264 }
265
Justin Klaassen2696d992016-07-11 21:26:46 -0700266 private static boolean isUserSetupCompleted(ContentResolver cr, int userHandle) {
267 return Secure.getIntForUser(cr, Secure.USER_SETUP_COMPLETE, 0, userHandle) == 1;
268 }
269
270 private void setUp() {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700271 Slog.d(TAG, "setUp: currentUser=" + mCurrentUser);
272
Christine Franks57fdde82018-07-03 14:46:07 -0700273 // Listen for external changes to any of the settings.
274 if (mContentObserver == null) {
Christine Franks27912a32019-04-02 10:43:10 -0700275 mContentObserver = new ContentObserver(mHandler) {
Christine Franks57fdde82018-07-03 14:46:07 -0700276 @Override
277 public void onChange(boolean selfChange, Uri uri) {
278 super.onChange(selfChange, uri);
279
280 final String setting = uri == null ? null : uri.getLastPathSegment();
281 if (setting != null) {
282 switch (setting) {
283 case Secure.NIGHT_DISPLAY_ACTIVATED:
Christine Franks78a4dd42019-02-08 11:09:30 -0800284 final boolean activated = mNightDisplayTintController
285 .isActivatedSetting();
Christine Franks83cc5412018-07-03 14:46:07 -0700286 if (mNightDisplayTintController.isActivatedStateNotSet()
287 || mNightDisplayTintController.isActivated() != activated) {
Christine Franks78a4dd42019-02-08 11:09:30 -0800288 mNightDisplayTintController.setActivated(activated);
Christine Franks83cc5412018-07-03 14:46:07 -0700289 }
Christine Franks57fdde82018-07-03 14:46:07 -0700290 break;
291 case Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE:
Christine Franks78a4dd42019-02-08 11:09:30 -0800292 final int temperature = mNightDisplayTintController
293 .getColorTemperatureSetting();
Christine Franks83cc5412018-07-03 14:46:07 -0700294 if (mNightDisplayTintController.getColorTemperature()
295 != temperature) {
296 mNightDisplayTintController
297 .onColorTemperatureChanged(temperature);
298 }
Christine Franks57fdde82018-07-03 14:46:07 -0700299 break;
300 case Secure.NIGHT_DISPLAY_AUTO_MODE:
Christine Franks83cc5412018-07-03 14:46:07 -0700301 onNightDisplayAutoModeChanged(getNightDisplayAutoModeInternal());
Christine Franks57fdde82018-07-03 14:46:07 -0700302 break;
303 case Secure.NIGHT_DISPLAY_CUSTOM_START_TIME:
304 onNightDisplayCustomStartTimeChanged(
Christine Franks83cc5412018-07-03 14:46:07 -0700305 getNightDisplayCustomStartTimeInternal().getLocalTime());
Christine Franks57fdde82018-07-03 14:46:07 -0700306 break;
307 case Secure.NIGHT_DISPLAY_CUSTOM_END_TIME:
308 onNightDisplayCustomEndTimeChanged(
Christine Franks83cc5412018-07-03 14:46:07 -0700309 getNightDisplayCustomEndTimeInternal().getLocalTime());
Christine Franks57fdde82018-07-03 14:46:07 -0700310 break;
311 case System.DISPLAY_COLOR_MODE:
Christine Franks71e003e2019-01-24 14:40:20 -0800312 onDisplayColorModeChanged(getColorModeInternal());
Christine Franks57fdde82018-07-03 14:46:07 -0700313 break;
Christine Franks57fdde82018-07-03 14:46:07 -0700314 case Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED:
Christine Franks9114f462019-01-04 11:27:30 -0800315 onAccessibilityInversionChanged();
316 onAccessibilityActivated();
317 break;
318 case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED:
319 onAccessibilityDaltonizerChanged();
320 onAccessibilityActivated();
321 break;
322 case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER:
323 onAccessibilityDaltonizerChanged();
Christine Franks57fdde82018-07-03 14:46:07 -0700324 break;
Christine Franks245ffd42018-11-16 13:45:14 -0800325 case Secure.DISPLAY_WHITE_BALANCE_ENABLED:
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800326 updateDisplayWhiteBalanceStatus();
Christine Franks245ffd42018-11-16 13:45:14 -0800327 break;
Christine Franks57fdde82018-07-03 14:46:07 -0700328 }
329 }
330 }
331 };
332 }
333 final ContentResolver cr = getContext().getContentResolver();
334 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_ACTIVATED),
335 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
336 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE),
337 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
338 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_AUTO_MODE),
339 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
340 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_START_TIME),
341 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
342 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_END_TIME),
343 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
344 cr.registerContentObserver(System.getUriFor(System.DISPLAY_COLOR_MODE),
345 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
346 cr.registerContentObserver(
347 Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED),
348 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
349 cr.registerContentObserver(
350 Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED),
351 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
Christine Franks9114f462019-01-04 11:27:30 -0800352 cr.registerContentObserver(
353 Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER),
354 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
Christine Franks245ffd42018-11-16 13:45:14 -0800355 cr.registerContentObserver(Secure.getUriFor(Secure.DISPLAY_WHITE_BALANCE_ENABLED),
356 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
Justin Klaassen911e8892016-06-21 18:24:24 -0700357
Christine Franks11e63152019-03-14 11:16:06 -0700358 // Apply the accessibility settings first, since they override most other settings.
359 onAccessibilityInversionChanged();
360 onAccessibilityDaltonizerChanged();
361
Christine Frankscf388c22018-05-15 15:48:10 -0700362 // Set the color mode, if valid, and immediately apply the updated tint matrix based on the
363 // existing activated state. This ensures consistency of tint across the color mode change.
Christine Franks71e003e2019-01-24 14:40:20 -0800364 onDisplayColorModeChanged(getColorModeInternal());
Christine Frankscf388c22018-05-15 15:48:10 -0700365
Christine Franksa4ed3762019-01-24 15:37:04 -0800366 if (mNightDisplayTintController.isAvailable(getContext())) {
Christine Franks245ffd42018-11-16 13:45:14 -0800367 // Reset the activated state.
368 mNightDisplayTintController.setActivated(null);
Christine Frankscf388c22018-05-15 15:48:10 -0700369
Christine Franks245ffd42018-11-16 13:45:14 -0800370 // Prepare the night display color transformation matrix.
371 mNightDisplayTintController
372 .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix());
Christine Franks78a4dd42019-02-08 11:09:30 -0800373 mNightDisplayTintController
374 .setMatrix(mNightDisplayTintController.getColorTemperatureSetting());
Christine Franks8ad71492017-10-24 19:04:22 -0700375
Christine Franks245ffd42018-11-16 13:45:14 -0800376 // Initialize the current auto mode.
Christine Franks83cc5412018-07-03 14:46:07 -0700377 onNightDisplayAutoModeChanged(getNightDisplayAutoModeInternal());
Christine Franks6418d0b2017-02-13 09:48:00 -0800378
Christine Franks83cc5412018-07-03 14:46:07 -0700379 // Force the initialization of the current saved activation state.
Christine Franks245ffd42018-11-16 13:45:14 -0800380 if (mNightDisplayTintController.isActivatedStateNotSet()) {
Christine Franks78a4dd42019-02-08 11:09:30 -0800381 mNightDisplayTintController
382 .setActivated(mNightDisplayTintController.isActivatedSetting());
Christine Franks245ffd42018-11-16 13:45:14 -0800383 }
384 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700385
Christine Franksa4ed3762019-01-24 15:37:04 -0800386 if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) {
Christine Franks245ffd42018-11-16 13:45:14 -0800387 // Prepare the display white balance transform matrix.
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800388 mDisplayWhiteBalanceTintController.setUp(getContext(), true /* needsLinear */);
Christine Franks245ffd42018-11-16 13:45:14 -0800389
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800390 updateDisplayWhiteBalanceStatus();
Justin Klaassen911e8892016-06-21 18:24:24 -0700391 }
392 }
393
Justin Klaassen2696d992016-07-11 21:26:46 -0700394 private void tearDown() {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700395 Slog.d(TAG, "tearDown: currentUser=" + mCurrentUser);
396
Keun young Parka2162f52019-05-29 11:33:11 -0700397 if (mContentObserver != null) {
398 getContext().getContentResolver().unregisterContentObserver(mContentObserver);
399 }
Christine Franks57fdde82018-07-03 14:46:07 -0700400
Christine Franksa4ed3762019-01-24 15:37:04 -0800401 if (mNightDisplayTintController.isAvailable(getContext())) {
Christine Franks245ffd42018-11-16 13:45:14 -0800402 if (mNightDisplayAutoMode != null) {
403 mNightDisplayAutoMode.onStop();
404 mNightDisplayAutoMode = null;
405 }
406 mNightDisplayTintController.endAnimator();
Justin Klaassen911e8892016-06-21 18:24:24 -0700407 }
408
Christine Franksa4ed3762019-01-24 15:37:04 -0800409 if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) {
Christine Franks245ffd42018-11-16 13:45:14 -0800410 mDisplayWhiteBalanceTintController.endAnimator();
Justin Klaassen639214e2016-07-14 21:00:06 -0700411 }
Christine Franks6d21d342019-02-07 15:09:03 -0800412
413 if (mGlobalSaturationTintController.isAvailable(getContext())) {
414 mGlobalSaturationTintController.setActivated(null);
415 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700416 }
417
Christine Franks57fdde82018-07-03 14:46:07 -0700418 private void onNightDisplayAutoModeChanged(int autoMode) {
419 Slog.d(TAG, "onNightDisplayAutoModeChanged: autoMode=" + autoMode);
Justin Klaassenec8837a2016-08-23 12:04:42 -0700420
Christine Franks57fdde82018-07-03 14:46:07 -0700421 if (mNightDisplayAutoMode != null) {
422 mNightDisplayAutoMode.onStop();
423 mNightDisplayAutoMode = null;
Justin Klaassen911e8892016-06-21 18:24:24 -0700424 }
425
Christine Franks83cc5412018-07-03 14:46:07 -0700426 if (autoMode == AUTO_MODE_CUSTOM_TIME) {
Christine Franks57fdde82018-07-03 14:46:07 -0700427 mNightDisplayAutoMode = new CustomNightDisplayAutoMode();
Christine Franks83cc5412018-07-03 14:46:07 -0700428 } else if (autoMode == AUTO_MODE_TWILIGHT) {
Christine Franks57fdde82018-07-03 14:46:07 -0700429 mNightDisplayAutoMode = new TwilightNightDisplayAutoMode();
Justin Klaassen911e8892016-06-21 18:24:24 -0700430 }
431
Christine Franks57fdde82018-07-03 14:46:07 -0700432 if (mNightDisplayAutoMode != null) {
433 mNightDisplayAutoMode.onStart();
Justin Klaassen911e8892016-06-21 18:24:24 -0700434 }
435 }
436
Christine Franks57fdde82018-07-03 14:46:07 -0700437 private void onNightDisplayCustomStartTimeChanged(LocalTime startTime) {
438 Slog.d(TAG, "onNightDisplayCustomStartTimeChanged: startTime=" + startTime);
Justin Klaassenec8837a2016-08-23 12:04:42 -0700439
Christine Franks57fdde82018-07-03 14:46:07 -0700440 if (mNightDisplayAutoMode != null) {
441 mNightDisplayAutoMode.onCustomStartTimeChanged(startTime);
Justin Klaassen911e8892016-06-21 18:24:24 -0700442 }
443 }
444
Christine Franks57fdde82018-07-03 14:46:07 -0700445 private void onNightDisplayCustomEndTimeChanged(LocalTime endTime) {
446 Slog.d(TAG, "onNightDisplayCustomEndTimeChanged: endTime=" + endTime);
Justin Klaassenec8837a2016-08-23 12:04:42 -0700447
Christine Franks57fdde82018-07-03 14:46:07 -0700448 if (mNightDisplayAutoMode != null) {
449 mNightDisplayAutoMode.onCustomEndTimeChanged(endTime);
Justin Klaassen911e8892016-06-21 18:24:24 -0700450 }
451 }
452
Christine Franks57fdde82018-07-03 14:46:07 -0700453 private void onDisplayColorModeChanged(int mode) {
Christine Franks83cc5412018-07-03 14:46:07 -0700454 if (mode == NOT_SET) {
Christine Frankscf388c22018-05-15 15:48:10 -0700455 return;
456 }
457
Christine Franks245ffd42018-11-16 13:45:14 -0800458 mNightDisplayTintController.cancelAnimator();
459 mDisplayWhiteBalanceTintController.cancelAnimator();
460
Christine Franksa4ed3762019-01-24 15:37:04 -0800461 if (mNightDisplayTintController.isAvailable(getContext())) {
462 mNightDisplayTintController
463 .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
Christine Franks78a4dd42019-02-08 11:09:30 -0800464 mNightDisplayTintController
465 .setMatrix(mNightDisplayTintController.getColorTemperatureSetting());
Christine Franksa4ed3762019-01-24 15:37:04 -0800466 }
Christine Franks245ffd42018-11-16 13:45:14 -0800467
Anthony Han2c2e4a82019-06-07 11:13:35 -0700468 // dtm.setColorMode() needs to be called before
469 // updateDisplayWhiteBalanceStatus(), this is because the latter calls
470 // DisplayTransformManager.needsLinearColorMatrix(), therefore it is dependent
471 // on the state of DisplayTransformManager.
472 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
473 dtm.setColorMode(mode, mNightDisplayTintController.getMatrix());
474
Christine Franks18ac4e22019-04-05 18:30:50 -0700475 if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) {
476 updateDisplayWhiteBalanceStatus();
477 }
Christine Franks8ad71492017-10-24 19:04:22 -0700478 }
479
Christine Franks9114f462019-01-04 11:27:30 -0800480 private void onAccessibilityActivated() {
Christine Franks71e003e2019-01-24 14:40:20 -0800481 onDisplayColorModeChanged(getColorModeInternal());
Daniel Solomon317a3572018-03-30 18:36:37 -0700482 }
483
Daniel Solomonff24ba92019-04-11 19:29:31 -0700484 private boolean isAccessiblityDaltonizerEnabled() {
485 return Secure.getIntForUser(getContext().getContentResolver(),
486 Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, mCurrentUser) != 0;
487 }
488
489 private boolean isAccessiblityInversionEnabled() {
490 return Secure.getIntForUser(getContext().getContentResolver(),
491 Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, mCurrentUser) != 0;
492 }
493
494 private boolean isAccessibilityEnabled() {
495 return isAccessiblityDaltonizerEnabled() || isAccessiblityInversionEnabled();
496 }
497
Christine Franks9114f462019-01-04 11:27:30 -0800498 /**
499 * Apply the accessibility daltonizer transform based on the settings value.
500 */
501 private void onAccessibilityDaltonizerChanged() {
Christine Franks27912a32019-04-02 10:43:10 -0700502 if (mCurrentUser == UserHandle.USER_NULL) {
503 return;
504 }
Daniel Solomonff24ba92019-04-11 19:29:31 -0700505 final int daltonizerMode = isAccessiblityDaltonizerEnabled()
506 ? Secure.getIntForUser(getContext().getContentResolver(),
507 Secure.ACCESSIBILITY_DISPLAY_DALTONIZER,
508 AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY, mCurrentUser)
Christine Franks9114f462019-01-04 11:27:30 -0800509 : AccessibilityManager.DALTONIZER_DISABLED;
510
511 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
512 if (daltonizerMode == AccessibilityManager.DALTONIZER_SIMULATE_MONOCHROMACY) {
513 // Monochromacy isn't supported by the native Daltonizer implementation; use grayscale.
514 dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_GRAYSCALE,
515 MATRIX_GRAYSCALE);
516 dtm.setDaltonizerMode(AccessibilityManager.DALTONIZER_DISABLED);
517 } else {
518 dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_GRAYSCALE, null);
519 dtm.setDaltonizerMode(daltonizerMode);
520 }
521 }
522
523 /**
524 * Apply the accessibility inversion transform based on the settings value.
525 */
526 private void onAccessibilityInversionChanged() {
Christine Franks27912a32019-04-02 10:43:10 -0700527 if (mCurrentUser == UserHandle.USER_NULL) {
528 return;
529 }
Christine Franks9114f462019-01-04 11:27:30 -0800530 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
531 dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_INVERT_COLOR,
Daniel Solomonff24ba92019-04-11 19:29:31 -0700532 isAccessiblityInversionEnabled() ? MATRIX_INVERT_COLOR : null);
Christine Franks9114f462019-01-04 11:27:30 -0800533 }
Christine Franks8ad71492017-10-24 19:04:22 -0700534
Christine Franks6418d0b2017-02-13 09:48:00 -0800535 /**
536 * Applies current color temperature matrix, or removes it if deactivated.
537 *
538 * @param immediate {@code true} skips transition animation
539 */
Christine Franks245ffd42018-11-16 13:45:14 -0800540 private void applyTint(TintController tintController, boolean immediate) {
541 tintController.cancelAnimator();
Christine Franks6418d0b2017-02-13 09:48:00 -0800542
Christine Franks6418d0b2017-02-13 09:48:00 -0800543 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
Christine Franks245ffd42018-11-16 13:45:14 -0800544 final float[] from = dtm.getColorMatrix(tintController.getLevel());
545 final float[] to = tintController.getMatrix();
Christine Franks6418d0b2017-02-13 09:48:00 -0800546
547 if (immediate) {
Christine Franks245ffd42018-11-16 13:45:14 -0800548 dtm.setColorMatrix(tintController.getLevel(), to);
Christine Franks6418d0b2017-02-13 09:48:00 -0800549 } else {
Christine Franks245ffd42018-11-16 13:45:14 -0800550 tintController.setAnimator(ValueAnimator.ofObject(COLOR_MATRIX_EVALUATOR,
551 from == null ? MATRIX_IDENTITY : from, to));
552 tintController.getAnimator().setDuration(TRANSITION_DURATION);
553 tintController.getAnimator().setInterpolator(AnimationUtils.loadInterpolator(
Christine Franks6418d0b2017-02-13 09:48:00 -0800554 getContext(), android.R.interpolator.fast_out_slow_in));
Christine Franks245ffd42018-11-16 13:45:14 -0800555 tintController.getAnimator().addUpdateListener((ValueAnimator animator) -> {
556 final float[] value = (float[]) animator.getAnimatedValue();
557 dtm.setColorMatrix(tintController.getLevel(), value);
Christine Franks6418d0b2017-02-13 09:48:00 -0800558 });
Christine Franks245ffd42018-11-16 13:45:14 -0800559 tintController.getAnimator().addListener(new AnimatorListenerAdapter() {
Christine Franks6418d0b2017-02-13 09:48:00 -0800560
561 private boolean mIsCancelled;
562
563 @Override
564 public void onAnimationCancel(Animator animator) {
565 mIsCancelled = true;
566 }
567
568 @Override
569 public void onAnimationEnd(Animator animator) {
570 if (!mIsCancelled) {
571 // Ensure final color matrix is set at the end of the animation. If the
572 // animation is cancelled then don't set the final color matrix so the new
573 // animator can pick up from where this one left off.
Christine Franks245ffd42018-11-16 13:45:14 -0800574 dtm.setColorMatrix(tintController.getLevel(), to);
Christine Franks6418d0b2017-02-13 09:48:00 -0800575 }
Christine Franks245ffd42018-11-16 13:45:14 -0800576 tintController.setAnimator(null);
Christine Franks6418d0b2017-02-13 09:48:00 -0800577 }
578 });
Christine Franks245ffd42018-11-16 13:45:14 -0800579 tintController.getAnimator().start();
Christine Franks6418d0b2017-02-13 09:48:00 -0800580 }
581 }
582
583 /**
Christine Franks39b03112018-07-03 14:46:07 -0700584 * Returns the first date time corresponding to the local time that occurs before the provided
585 * date time.
Christine Franks03213462017-08-25 13:57:26 -0700586 *
587 * @param compareTime the LocalDateTime to compare against
588 * @return the prior LocalDateTime corresponding to this local time
589 */
Christine Franks57fdde82018-07-03 14:46:07 -0700590 @VisibleForTesting
591 static LocalDateTime getDateTimeBefore(LocalTime localTime, LocalDateTime compareTime) {
Christine Franks03213462017-08-25 13:57:26 -0700592 final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
593 compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
594
595 // Check if the local time has passed, if so return the same time yesterday.
596 return ldt.isAfter(compareTime) ? ldt.minusDays(1) : ldt;
597 }
598
599 /**
Christine Franks39b03112018-07-03 14:46:07 -0700600 * Returns the first date time corresponding to this local time that occurs after the provided
601 * date time.
Christine Franks03213462017-08-25 13:57:26 -0700602 *
603 * @param compareTime the LocalDateTime to compare against
604 * @return the next LocalDateTime corresponding to this local time
605 */
Christine Franks57fdde82018-07-03 14:46:07 -0700606 @VisibleForTesting
607 static LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) {
Christine Franks03213462017-08-25 13:57:26 -0700608 final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
609 compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
610
611 // Check if the local time has passed, if so return the same time tomorrow.
612 return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
613 }
614
Long Ling1d3f1892019-02-06 12:34:02 -0800615 @VisibleForTesting
616 void updateDisplayWhiteBalanceStatus() {
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800617 boolean oldActivated = mDisplayWhiteBalanceTintController.isActivated();
Christine Franks0ada2772019-02-25 13:54:57 -0800618 mDisplayWhiteBalanceTintController.setActivated(isDisplayWhiteBalanceSettingEnabled()
619 && !mNightDisplayTintController.isActivated()
Daniel Solomonff24ba92019-04-11 19:29:31 -0700620 && !isAccessibilityEnabled()
Christine Franks0ada2772019-02-25 13:54:57 -0800621 && DisplayTransformManager.needsLinearColorMatrix());
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800622 boolean activated = mDisplayWhiteBalanceTintController.isActivated();
623
624 if (mDisplayWhiteBalanceListener != null && oldActivated != activated) {
625 mDisplayWhiteBalanceListener.onDisplayWhiteBalanceStatusChanged(activated);
Christine Franks245ffd42018-11-16 13:45:14 -0800626 }
Daniel Solomon86508f82019-01-18 19:01:02 -0800627
628 // If disabled, clear the tint. If enabled, do nothing more here and let the next
629 // temperature update set the correct tint.
630 if (!activated) {
Christine Franksc7fb9452019-02-04 08:45:33 -0800631 mHandler.sendEmptyMessage(MSG_APPLY_DISPLAY_WHITE_BALANCE);
Daniel Solomon86508f82019-01-18 19:01:02 -0800632 }
Christine Franks245ffd42018-11-16 13:45:14 -0800633 }
634
Christine Franks66783a82019-03-28 11:45:56 -0700635 private boolean setDisplayWhiteBalanceSettingEnabled(boolean enabled) {
636 if (mCurrentUser == UserHandle.USER_NULL) {
637 return false;
638 }
639 return Secure.putIntForUser(getContext().getContentResolver(),
640 Secure.DISPLAY_WHITE_BALANCE_ENABLED,
641 enabled ? 1 : 0, mCurrentUser);
642 }
643
Christine Franks245ffd42018-11-16 13:45:14 -0800644 private boolean isDisplayWhiteBalanceSettingEnabled() {
Christine Franks27912a32019-04-02 10:43:10 -0700645 if (mCurrentUser == UserHandle.USER_NULL) {
646 return false;
647 }
Christine Franks245ffd42018-11-16 13:45:14 -0800648 return Secure.getIntForUser(getContext().getContentResolver(),
Christine Franks18ac4e22019-04-05 18:30:50 -0700649 Secure.DISPLAY_WHITE_BALANCE_ENABLED,
650 getContext().getResources()
651 .getBoolean(R.bool.config_displayWhiteBalanceEnabledDefault) ? 1
652 : 0,
653 mCurrentUser) == 1;
Christine Franks245ffd42018-11-16 13:45:14 -0800654 }
655
Christine Franks39b03112018-07-03 14:46:07 -0700656 private boolean isDeviceColorManagedInternal() {
657 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
658 return dtm.isDeviceColorManaged();
659 }
660
Christine Franks55194dc2019-01-15 13:47:06 -0800661 private int getTransformCapabilitiesInternal() {
662 int availabilityFlags = ColorDisplayManager.CAPABILITY_NONE;
663 if (SurfaceControl.getProtectedContentSupport()) {
664 availabilityFlags |= ColorDisplayManager.CAPABILITY_PROTECTED_CONTENT;
665 }
666 final Resources res = getContext().getResources();
667 if (res.getBoolean(R.bool.config_setColorTransformAccelerated)) {
668 availabilityFlags |= ColorDisplayManager.CAPABILITY_HARDWARE_ACCELERATION_GLOBAL;
669 }
670 if (res.getBoolean(R.bool.config_setColorTransformAcceleratedPerLayer)) {
671 availabilityFlags |= ColorDisplayManager.CAPABILITY_HARDWARE_ACCELERATION_PER_APP;
672 }
673 return availabilityFlags;
674 }
675
Christine Franks83cc5412018-07-03 14:46:07 -0700676 private boolean setNightDisplayAutoModeInternal(@AutoMode int autoMode) {
677 if (getNightDisplayAutoModeInternal() != autoMode) {
678 Secure.putStringForUser(getContext().getContentResolver(),
679 Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
680 null,
681 mCurrentUser);
682 }
683 return Secure.putIntForUser(getContext().getContentResolver(),
684 Secure.NIGHT_DISPLAY_AUTO_MODE, autoMode, mCurrentUser);
685 }
686
687 private int getNightDisplayAutoModeInternal() {
688 int autoMode = getNightDisplayAutoModeRawInternal();
689 if (autoMode == NOT_SET) {
690 autoMode = getContext().getResources().getInteger(
691 R.integer.config_defaultNightDisplayAutoMode);
692 }
693 if (autoMode != AUTO_MODE_DISABLED
694 && autoMode != AUTO_MODE_CUSTOM_TIME
695 && autoMode != AUTO_MODE_TWILIGHT) {
696 Slog.e(TAG, "Invalid autoMode: " + autoMode);
697 autoMode = AUTO_MODE_DISABLED;
698 }
699 return autoMode;
700 }
701
702 private int getNightDisplayAutoModeRawInternal() {
Christine Franks27912a32019-04-02 10:43:10 -0700703 if (mCurrentUser == UserHandle.USER_NULL) {
704 return NOT_SET;
705 }
Christine Franks83cc5412018-07-03 14:46:07 -0700706 return Secure
707 .getIntForUser(getContext().getContentResolver(), Secure.NIGHT_DISPLAY_AUTO_MODE,
708 NOT_SET, mCurrentUser);
709 }
710
711 private Time getNightDisplayCustomStartTimeInternal() {
712 int startTimeValue = Secure.getIntForUser(getContext().getContentResolver(),
713 Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, NOT_SET, mCurrentUser);
714 if (startTimeValue == NOT_SET) {
715 startTimeValue = getContext().getResources().getInteger(
716 R.integer.config_defaultNightDisplayCustomStartTime);
717 }
718 return new Time(LocalTime.ofSecondOfDay(startTimeValue / 1000));
719 }
720
721 private boolean setNightDisplayCustomStartTimeInternal(Time startTime) {
722 return Secure.putIntForUser(getContext().getContentResolver(),
723 Secure.NIGHT_DISPLAY_CUSTOM_START_TIME,
724 startTime.getLocalTime().toSecondOfDay() * 1000,
725 mCurrentUser);
726 }
727
728 private Time getNightDisplayCustomEndTimeInternal() {
729 int endTimeValue = Secure.getIntForUser(getContext().getContentResolver(),
730 Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, NOT_SET, mCurrentUser);
731 if (endTimeValue == NOT_SET) {
732 endTimeValue = getContext().getResources().getInteger(
733 R.integer.config_defaultNightDisplayCustomEndTime);
734 }
735 return new Time(LocalTime.ofSecondOfDay(endTimeValue / 1000));
736 }
737
738 private boolean setNightDisplayCustomEndTimeInternal(Time endTime) {
739 return Secure.putIntForUser(getContext().getContentResolver(),
740 Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, endTime.getLocalTime().toSecondOfDay() * 1000,
741 mCurrentUser);
742 }
743
Christine Franks57fdde82018-07-03 14:46:07 -0700744 /**
745 * Returns the last time the night display transform activation state was changed, or {@link
746 * LocalDateTime#MIN} if night display has never been activated.
747 */
Christine Franks245ffd42018-11-16 13:45:14 -0800748 private LocalDateTime getNightDisplayLastActivatedTimeSetting() {
Christine Franks57fdde82018-07-03 14:46:07 -0700749 final ContentResolver cr = getContext().getContentResolver();
750 final String lastActivatedTime = Secure.getStringForUser(
751 cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, getContext().getUserId());
752 if (lastActivatedTime != null) {
753 try {
754 return LocalDateTime.parse(lastActivatedTime);
755 } catch (DateTimeParseException ignored) {
756 }
757 // Uses the old epoch time.
758 try {
759 return LocalDateTime.ofInstant(
760 Instant.ofEpochMilli(Long.parseLong(lastActivatedTime)),
761 ZoneId.systemDefault());
762 } catch (DateTimeException | NumberFormatException ignored) {
763 }
764 }
765 return LocalDateTime.MIN;
766 }
767
Christine Franksf3529b22019-01-03 13:20:17 -0800768 private boolean setAppSaturationLevelInternal(String packageName, int saturationLevel) {
769 return mAppSaturationController
770 .setSaturationLevel(packageName, mCurrentUser, saturationLevel);
771 }
772
Christine Franksd154fe52019-01-04 17:17:45 -0800773 private void setColorModeInternal(@ColorMode int colorMode) {
774 if (!isColorModeAvailable(colorMode)) {
775 throw new IllegalArgumentException("Invalid colorMode: " + colorMode);
776 }
777 System.putIntForUser(getContext().getContentResolver(), System.DISPLAY_COLOR_MODE,
778 colorMode,
779 mCurrentUser);
780 }
781
Christine Franks71e003e2019-01-24 14:40:20 -0800782 private @ColorMode int getColorModeInternal() {
Christine Franksd154fe52019-01-04 17:17:45 -0800783 final ContentResolver cr = getContext().getContentResolver();
Daniel Solomonff24ba92019-04-11 19:29:31 -0700784 if (isAccessibilityEnabled()) {
Christine Franksd154fe52019-01-04 17:17:45 -0800785 // There are restrictions on the available color modes combined with a11y transforms.
Christine Franksb8d03a52019-05-08 15:08:21 -0700786 final int a11yColorMode = getContext().getResources().getInteger(
787 R.integer.config_accessibilityColorMode);
788 if (a11yColorMode >= 0) {
789 return a11yColorMode;
Christine Franksd154fe52019-01-04 17:17:45 -0800790 }
791 }
792
793 int colorMode = System.getIntForUser(cr, System.DISPLAY_COLOR_MODE, -1, mCurrentUser);
794 if (colorMode == -1) {
795 // There might be a system property controlling color mode that we need to respect; if
796 // not, this will set a suitable default.
797 colorMode = getCurrentColorModeFromSystemProperties();
798 }
799
800 // This happens when a color mode is no longer available (e.g., after system update or B&R)
801 // or the device does not support any color mode.
802 if (!isColorModeAvailable(colorMode)) {
803 if (colorMode == COLOR_MODE_BOOSTED && isColorModeAvailable(COLOR_MODE_NATURAL)) {
804 colorMode = COLOR_MODE_NATURAL;
805 } else if (colorMode == COLOR_MODE_SATURATED
806 && isColorModeAvailable(COLOR_MODE_AUTOMATIC)) {
807 colorMode = COLOR_MODE_AUTOMATIC;
808 } else if (colorMode == COLOR_MODE_AUTOMATIC
809 && isColorModeAvailable(COLOR_MODE_SATURATED)) {
810 colorMode = COLOR_MODE_SATURATED;
811 } else {
812 colorMode = -1;
813 }
814 }
815
816 return colorMode;
817 }
818
819 /**
820 * Get the current color mode from system properties, or return -1 if invalid.
821 *
Christine Franks0ada2772019-02-25 13:54:57 -0800822 * See {@link DisplayTransformManager}
Christine Franksd154fe52019-01-04 17:17:45 -0800823 */
Christine Franks78a4dd42019-02-08 11:09:30 -0800824 private @ColorMode int getCurrentColorModeFromSystemProperties() {
Christine Franksd154fe52019-01-04 17:17:45 -0800825 final int displayColorSetting = SystemProperties.getInt("persist.sys.sf.native_mode", 0);
826 if (displayColorSetting == 0) {
827 return "1.0".equals(SystemProperties.get("persist.sys.sf.color_saturation"))
828 ? COLOR_MODE_NATURAL : COLOR_MODE_BOOSTED;
829 } else if (displayColorSetting == 1) {
830 return COLOR_MODE_SATURATED;
831 } else if (displayColorSetting == 2) {
832 return COLOR_MODE_AUTOMATIC;
Christine Franks531c0612019-05-16 14:29:14 -0700833 } else if (displayColorSetting >= VENDOR_COLOR_MODE_RANGE_MIN
834 && displayColorSetting <= VENDOR_COLOR_MODE_RANGE_MAX) {
835 return displayColorSetting;
Christine Franksd154fe52019-01-04 17:17:45 -0800836 } else {
837 return -1;
838 }
839 }
840
841 private boolean isColorModeAvailable(@ColorMode int colorMode) {
842 final int[] availableColorModes = getContext().getResources().getIntArray(
843 R.array.config_availableColorModes);
844 if (availableColorModes != null) {
845 for (int mode : availableColorModes) {
846 if (mode == colorMode) {
847 return true;
848 }
849 }
850 }
851 return false;
852 }
853
Christine Franksf3529b22019-01-03 13:20:17 -0800854 private void dumpInternal(PrintWriter pw) {
855 pw.println("COLOR DISPLAY MANAGER dumpsys (color_display)");
Christine Franksa4ed3762019-01-24 15:37:04 -0800856
Christine Franks0ada2772019-02-25 13:54:57 -0800857 pw.println("Night display:");
Christine Franksa4ed3762019-01-24 15:37:04 -0800858 if (mNightDisplayTintController.isAvailable(getContext())) {
Christine Franksf3529b22019-01-03 13:20:17 -0800859 pw.println(" Activated: " + mNightDisplayTintController.isActivated());
Christine Franksa4ed3762019-01-24 15:37:04 -0800860 pw.println(" Color temp: " + mNightDisplayTintController.getColorTemperature());
Christine Franksf3529b22019-01-03 13:20:17 -0800861 } else {
862 pw.println(" Not available");
863 }
Christine Franksa4ed3762019-01-24 15:37:04 -0800864
865 pw.println("Global saturation:");
866 if (mGlobalSaturationTintController.isAvailable(getContext())) {
867 pw.println(" Activated: " + mGlobalSaturationTintController.isActivated());
868 } else {
869 pw.println(" Not available");
870 }
871
Christine Franksf3529b22019-01-03 13:20:17 -0800872 mAppSaturationController.dump(pw);
Christine Franksa4ed3762019-01-24 15:37:04 -0800873
874 pw.println("Display white balance:");
875 if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) {
876 pw.println(" Activated: " + mDisplayWhiteBalanceTintController.isActivated());
Long Ling1d3f1892019-02-06 12:34:02 -0800877 mDisplayWhiteBalanceTintController.dump(pw);
Christine Franksa4ed3762019-01-24 15:37:04 -0800878 } else {
879 pw.println(" Not available");
880 }
881
882 pw.println("Color mode: " + getColorModeInternal());
Christine Franksf3529b22019-01-03 13:20:17 -0800883 }
884
Christine Franks57fdde82018-07-03 14:46:07 -0700885 private abstract class NightDisplayAutoMode {
886
887 public abstract void onActivated(boolean activated);
888
Justin Klaassen911e8892016-06-21 18:24:24 -0700889 public abstract void onStart();
Christine Frankse5bb03e2017-02-10 17:36:10 -0800890
Justin Klaassen911e8892016-06-21 18:24:24 -0700891 public abstract void onStop();
Christine Franks57fdde82018-07-03 14:46:07 -0700892
893 public void onCustomStartTimeChanged(LocalTime startTime) {
894 }
895
896 public void onCustomEndTimeChanged(LocalTime endTime) {
897 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700898 }
899
Christine Franks57fdde82018-07-03 14:46:07 -0700900 private final class CustomNightDisplayAutoMode extends NightDisplayAutoMode implements
901 AlarmManager.OnAlarmListener {
Justin Klaassen911e8892016-06-21 18:24:24 -0700902
903 private final AlarmManager mAlarmManager;
904 private final BroadcastReceiver mTimeChangedReceiver;
905
Christine Franks03213462017-08-25 13:57:26 -0700906 private LocalTime mStartTime;
907 private LocalTime mEndTime;
Justin Klaassen911e8892016-06-21 18:24:24 -0700908
Christine Franks03213462017-08-25 13:57:26 -0700909 private LocalDateTime mLastActivatedTime;
Justin Klaassen911e8892016-06-21 18:24:24 -0700910
Christine Franks57fdde82018-07-03 14:46:07 -0700911 CustomNightDisplayAutoMode() {
Justin Klaassen911e8892016-06-21 18:24:24 -0700912 mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
913 mTimeChangedReceiver = new BroadcastReceiver() {
914 @Override
915 public void onReceive(Context context, Intent intent) {
916 updateActivated();
917 }
918 };
919 }
920
921 private void updateActivated() {
Christine Franks03213462017-08-25 13:57:26 -0700922 final LocalDateTime now = LocalDateTime.now();
923 final LocalDateTime start = getDateTimeBefore(mStartTime, now);
924 final LocalDateTime end = getDateTimeAfter(mEndTime, start);
925 boolean activate = now.isBefore(end);
Justin Klaassen911e8892016-06-21 18:24:24 -0700926
Christine Frankse5bb03e2017-02-10 17:36:10 -0800927 if (mLastActivatedTime != null) {
Christine Frankse5bb03e2017-02-10 17:36:10 -0800928 // Maintain the existing activated state if within the current period.
Christine Franks0ada2772019-02-25 13:54:57 -0800929 if (mLastActivatedTime.isBefore(now)
930 && mLastActivatedTime.isAfter(start)
Christine Franks03213462017-08-25 13:57:26 -0700931 && (mLastActivatedTime.isAfter(end) || now.isBefore(end))) {
Christine Franks78a4dd42019-02-08 11:09:30 -0800932 activate = mNightDisplayTintController.isActivatedSetting();
Justin Klaassen911e8892016-06-21 18:24:24 -0700933 }
934 }
935
Christine Franks0ada2772019-02-25 13:54:57 -0800936 if (mNightDisplayTintController.isActivatedStateNotSet()
937 || (mNightDisplayTintController.isActivated() != activate)) {
Christine Franks8851ac82019-06-14 09:44:43 -0700938 mNightDisplayTintController.setActivated(activate, activate ? start : end);
Justin Klaassen911e8892016-06-21 18:24:24 -0700939 }
Christine Franks03213462017-08-25 13:57:26 -0700940
Christine Franks245ffd42018-11-16 13:45:14 -0800941 updateNextAlarm(mNightDisplayTintController.isActivated(), now);
Justin Klaassen911e8892016-06-21 18:24:24 -0700942 }
943
Christine Franks03213462017-08-25 13:57:26 -0700944 private void updateNextAlarm(@Nullable Boolean activated, @NonNull LocalDateTime now) {
Justin Klaassen4346f632016-08-08 15:01:47 -0700945 if (activated != null) {
Christine Franks03213462017-08-25 13:57:26 -0700946 final LocalDateTime next = activated ? getDateTimeAfter(mEndTime, now)
947 : getDateTimeAfter(mStartTime, now);
948 final long millis = next.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
949 mAlarmManager.setExact(AlarmManager.RTC, millis, TAG, this, null);
Justin Klaassen911e8892016-06-21 18:24:24 -0700950 }
951 }
952
953 @Override
954 public void onStart() {
955 final IntentFilter intentFilter = new IntentFilter(Intent.ACTION_TIME_CHANGED);
956 intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
957 getContext().registerReceiver(mTimeChangedReceiver, intentFilter);
958
Christine Franks83cc5412018-07-03 14:46:07 -0700959 mStartTime = getNightDisplayCustomStartTimeInternal().getLocalTime();
960 mEndTime = getNightDisplayCustomEndTimeInternal().getLocalTime();
Justin Klaassen911e8892016-06-21 18:24:24 -0700961
Christine Franks57fdde82018-07-03 14:46:07 -0700962 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
Christine Frankse5bb03e2017-02-10 17:36:10 -0800963
Justin Klaassen911e8892016-06-21 18:24:24 -0700964 // Force an update to initialize state.
965 updateActivated();
966 }
967
968 @Override
969 public void onStop() {
970 getContext().unregisterReceiver(mTimeChangedReceiver);
971
972 mAlarmManager.cancel(this);
973 mLastActivatedTime = null;
974 }
975
976 @Override
977 public void onActivated(boolean activated) {
Christine Franks57fdde82018-07-03 14:46:07 -0700978 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
Christine Franks03213462017-08-25 13:57:26 -0700979 updateNextAlarm(activated, LocalDateTime.now());
Justin Klaassen911e8892016-06-21 18:24:24 -0700980 }
981
982 @Override
Christine Franks03213462017-08-25 13:57:26 -0700983 public void onCustomStartTimeChanged(LocalTime startTime) {
Justin Klaassen911e8892016-06-21 18:24:24 -0700984 mStartTime = startTime;
985 mLastActivatedTime = null;
986 updateActivated();
987 }
988
989 @Override
Christine Franks03213462017-08-25 13:57:26 -0700990 public void onCustomEndTimeChanged(LocalTime endTime) {
Justin Klaassen911e8892016-06-21 18:24:24 -0700991 mEndTime = endTime;
992 mLastActivatedTime = null;
993 updateActivated();
994 }
995
996 @Override
997 public void onAlarm() {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700998 Slog.d(TAG, "onAlarm");
Justin Klaassen911e8892016-06-21 18:24:24 -0700999 updateActivated();
1000 }
1001 }
1002
Christine Franks57fdde82018-07-03 14:46:07 -07001003 private final class TwilightNightDisplayAutoMode extends NightDisplayAutoMode implements
1004 TwilightListener {
Justin Klaassen911e8892016-06-21 18:24:24 -07001005
1006 private final TwilightManager mTwilightManager;
Christine Franks57fdde82018-07-03 14:46:07 -07001007 private LocalDateTime mLastActivatedTime;
Justin Klaassen911e8892016-06-21 18:24:24 -07001008
Christine Franks57fdde82018-07-03 14:46:07 -07001009 TwilightNightDisplayAutoMode() {
Justin Klaassen911e8892016-06-21 18:24:24 -07001010 mTwilightManager = getLocalService(TwilightManager.class);
Justin Klaassen911e8892016-06-21 18:24:24 -07001011 }
1012
Justin Klaassen908b86c2016-08-08 09:18:42 -07001013 private void updateActivated(TwilightState state) {
Justin Klaassen3da4c442017-05-05 15:19:33 -07001014 if (state == null) {
1015 // If there isn't a valid TwilightState then just keep the current activated
1016 // state.
1017 return;
1018 }
1019
1020 boolean activate = state.isNight();
Christine Franks57fdde82018-07-03 14:46:07 -07001021 if (mLastActivatedTime != null) {
Christine Franks03213462017-08-25 13:57:26 -07001022 final LocalDateTime now = LocalDateTime.now();
1023 final LocalDateTime sunrise = state.sunrise();
1024 final LocalDateTime sunset = state.sunset();
Christine Frankse5bb03e2017-02-10 17:36:10 -08001025 // Maintain the existing activated state if within the current period.
Christine Franks57fdde82018-07-03 14:46:07 -07001026 if (mLastActivatedTime.isBefore(now) && (mLastActivatedTime.isBefore(sunrise)
1027 ^ mLastActivatedTime.isBefore(sunset))) {
Christine Franks78a4dd42019-02-08 11:09:30 -08001028 activate = mNightDisplayTintController.isActivatedSetting();
Justin Klaassen911e8892016-06-21 18:24:24 -07001029 }
1030 }
Justin Klaassen908b86c2016-08-08 09:18:42 -07001031
Christine Franks245ffd42018-11-16 13:45:14 -08001032 if (mNightDisplayTintController.isActivatedStateNotSet() || (
1033 mNightDisplayTintController.isActivated() != activate)) {
Christine Franks83cc5412018-07-03 14:46:07 -07001034 mNightDisplayTintController.setActivated(activate);
Justin Klaassen908b86c2016-08-08 09:18:42 -07001035 }
Justin Klaassen911e8892016-06-21 18:24:24 -07001036 }
1037
1038 @Override
Christine Franks57fdde82018-07-03 14:46:07 -07001039 public void onActivated(boolean activated) {
1040 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
1041 }
1042
1043 @Override
Justin Klaassen911e8892016-06-21 18:24:24 -07001044 public void onStart() {
1045 mTwilightManager.registerListener(this, mHandler);
Christine Franks57fdde82018-07-03 14:46:07 -07001046 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
Justin Klaassen911e8892016-06-21 18:24:24 -07001047
1048 // Force an update to initialize state.
Justin Klaassen908b86c2016-08-08 09:18:42 -07001049 updateActivated(mTwilightManager.getLastTwilightState());
Justin Klaassen911e8892016-06-21 18:24:24 -07001050 }
1051
1052 @Override
1053 public void onStop() {
1054 mTwilightManager.unregisterListener(this);
Christine Franks57fdde82018-07-03 14:46:07 -07001055 mLastActivatedTime = null;
Justin Klaassen908b86c2016-08-08 09:18:42 -07001056 }
1057
1058 @Override
1059 public void onTwilightStateChanged(@Nullable TwilightState state) {
Justin Klaassenec8837a2016-08-23 12:04:42 -07001060 Slog.d(TAG, "onTwilightStateChanged: isNight="
1061 + (state == null ? null : state.isNight()));
Justin Klaassen908b86c2016-08-08 09:18:42 -07001062 updateActivated(state);
Justin Klaassen911e8892016-06-21 18:24:24 -07001063 }
1064 }
Justin Klaassen639214e2016-07-14 21:00:06 -07001065
1066 /**
1067 * Interpolates between two 4x4 color transform matrices (in column-major order).
1068 */
1069 private static class ColorMatrixEvaluator implements TypeEvaluator<float[]> {
1070
1071 /**
1072 * Result matrix returned by {@link #evaluate(float, float[], float[])}.
1073 */
1074 private final float[] mResultMatrix = new float[16];
1075
1076 @Override
1077 public float[] evaluate(float fraction, float[] startValue, float[] endValue) {
1078 for (int i = 0; i < mResultMatrix.length; i++) {
1079 mResultMatrix[i] = MathUtils.lerp(startValue[i], endValue[i], fraction);
1080 }
1081 return mResultMatrix;
1082 }
1083 }
Christine Franks39b03112018-07-03 14:46:07 -07001084
Christine Franks83cc5412018-07-03 14:46:07 -07001085 private final class NightDisplayTintController extends TintController {
1086
Christine Franksa4ed3762019-01-24 15:37:04 -08001087 private final float[] mMatrix = new float[16];
Christine Franks83cc5412018-07-03 14:46:07 -07001088 private final float[] mColorTempCoefficients = new float[9];
Christine Franksa4ed3762019-01-24 15:37:04 -08001089
1090 private Boolean mIsAvailable;
Christine Franks83cc5412018-07-03 14:46:07 -07001091 private Integer mColorTemp;
1092
1093 /**
1094 * Set coefficients based on whether the color matrix is linear or not.
1095 */
1096 @Override
1097 public void setUp(Context context, boolean needsLinear) {
1098 final String[] coefficients = context.getResources().getStringArray(needsLinear
1099 ? R.array.config_nightDisplayColorTemperatureCoefficients
1100 : R.array.config_nightDisplayColorTemperatureCoefficientsNative);
1101 for (int i = 0; i < 9 && i < coefficients.length; i++) {
1102 mColorTempCoefficients[i] = Float.parseFloat(coefficients[i]);
1103 }
1104 }
1105
1106 @Override
1107 public void setMatrix(int cct) {
1108 if (mMatrix.length != 16) {
1109 Slog.d(TAG, "The display transformation matrix must be 4x4");
1110 return;
1111 }
1112
1113 Matrix.setIdentityM(mMatrix, 0);
1114
1115 final float squareTemperature = cct * cct;
1116 final float red = squareTemperature * mColorTempCoefficients[0]
1117 + cct * mColorTempCoefficients[1] + mColorTempCoefficients[2];
1118 final float green = squareTemperature * mColorTempCoefficients[3]
1119 + cct * mColorTempCoefficients[4] + mColorTempCoefficients[5];
1120 final float blue = squareTemperature * mColorTempCoefficients[6]
1121 + cct * mColorTempCoefficients[7] + mColorTempCoefficients[8];
1122 mMatrix[0] = red;
1123 mMatrix[5] = green;
1124 mMatrix[10] = blue;
1125 }
1126
1127 @Override
1128 public float[] getMatrix() {
1129 return isActivated() ? mMatrix : MATRIX_IDENTITY;
1130 }
1131
1132 @Override
1133 public void setActivated(Boolean activated) {
Christine Franks8851ac82019-06-14 09:44:43 -07001134 setActivated(activated, LocalDateTime.now());
1135 }
1136
1137 /**
1138 * Use directly when it is important that the last activation time be exact (for example, an
1139 * automatic change). Otherwise use {@link #setActivated(Boolean)}.
1140 */
1141 public void setActivated(Boolean activated, @NonNull LocalDateTime lastActivationTime) {
Christine Franks83cc5412018-07-03 14:46:07 -07001142 if (activated == null) {
1143 super.setActivated(null);
1144 return;
1145 }
1146
1147 boolean activationStateChanged = activated != isActivated();
1148
1149 if (!isActivatedStateNotSet() && activationStateChanged) {
1150 // This is a true state change, so set this as the last activation time.
1151 Secure.putStringForUser(getContext().getContentResolver(),
1152 Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
Christine Franks8851ac82019-06-14 09:44:43 -07001153 lastActivationTime.toString(),
Christine Franks83cc5412018-07-03 14:46:07 -07001154 mCurrentUser);
1155 }
1156
1157 if (isActivatedStateNotSet() || activationStateChanged) {
1158 super.setActivated(activated);
Christine Franks78a4dd42019-02-08 11:09:30 -08001159 if (isActivatedSetting() != activated) {
1160 Secure.putIntForUser(getContext().getContentResolver(),
1161 Secure.NIGHT_DISPLAY_ACTIVATED,
1162 activated ? 1 : 0, mCurrentUser);
1163 }
Christine Franks83cc5412018-07-03 14:46:07 -07001164 onActivated(activated);
1165 }
1166 }
1167
1168 @Override
1169 public int getLevel() {
1170 return LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
1171 }
1172
Christine Franksa4ed3762019-01-24 15:37:04 -08001173 @Override
1174 public boolean isAvailable(Context context) {
1175 if (mIsAvailable == null) {
1176 mIsAvailable = ColorDisplayManager.isNightDisplayAvailable(context);
1177 }
1178 return mIsAvailable;
1179 }
1180
Christine Franks78a4dd42019-02-08 11:09:30 -08001181 private void onActivated(boolean activated) {
Christine Franks83cc5412018-07-03 14:46:07 -07001182 Slog.i(TAG, activated ? "Turning on night display" : "Turning off night display");
1183 if (mNightDisplayAutoMode != null) {
1184 mNightDisplayAutoMode.onActivated(activated);
1185 }
1186
Christine Franksa4ed3762019-01-24 15:37:04 -08001187 if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) {
Christine Franks83cc5412018-07-03 14:46:07 -07001188 updateDisplayWhiteBalanceStatus();
1189 }
1190
1191 mHandler.sendEmptyMessage(MSG_APPLY_NIGHT_DISPLAY_ANIMATED);
1192 }
1193
1194 int getColorTemperature() {
1195 return mColorTemp != null ? clampNightDisplayColorTemperature(mColorTemp)
Christine Franks78a4dd42019-02-08 11:09:30 -08001196 : getColorTemperatureSetting();
Christine Franks83cc5412018-07-03 14:46:07 -07001197 }
1198
1199 boolean setColorTemperature(int temperature) {
1200 mColorTemp = temperature;
1201 final boolean success = Secure.putIntForUser(getContext().getContentResolver(),
1202 Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, temperature, mCurrentUser);
1203 onColorTemperatureChanged(temperature);
1204 return success;
1205 }
1206
1207 void onColorTemperatureChanged(int temperature) {
1208 setMatrix(temperature);
1209 mHandler.sendEmptyMessage(MSG_APPLY_NIGHT_DISPLAY_IMMEDIATE);
1210 }
Christine Franks78a4dd42019-02-08 11:09:30 -08001211
1212 boolean isActivatedSetting() {
Christine Franks44782612019-03-07 17:25:39 -08001213 if (mCurrentUser == UserHandle.USER_NULL) {
1214 return false;
1215 }
Christine Franks78a4dd42019-02-08 11:09:30 -08001216 return Secure.getIntForUser(getContext().getContentResolver(),
1217 Secure.NIGHT_DISPLAY_ACTIVATED, 0, mCurrentUser) == 1;
1218 }
1219
1220 int getColorTemperatureSetting() {
Christine Franks44782612019-03-07 17:25:39 -08001221 if (mCurrentUser == UserHandle.USER_NULL) {
1222 return NOT_SET;
1223 }
Christine Franks78a4dd42019-02-08 11:09:30 -08001224 return clampNightDisplayColorTemperature(Secure.getIntForUser(
1225 getContext().getContentResolver(), Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE,
1226 NOT_SET,
1227 mCurrentUser));
1228 }
1229
1230 private int clampNightDisplayColorTemperature(int colorTemperature) {
1231 if (colorTemperature == NOT_SET) {
1232 colorTemperature = getContext().getResources().getInteger(
1233 R.integer.config_nightDisplayColorTemperatureDefault);
1234 }
1235 final int minimumTemperature = ColorDisplayManager
1236 .getMinimumColorTemperature(getContext());
1237 final int maximumTemperature = ColorDisplayManager
1238 .getMaximumColorTemperature(getContext());
1239 if (colorTemperature < minimumTemperature) {
1240 colorTemperature = minimumTemperature;
1241 } else if (colorTemperature > maximumTemperature) {
1242 colorTemperature = maximumTemperature;
1243 }
1244
1245 return colorTemperature;
1246 }
Christine Franks83cc5412018-07-03 14:46:07 -07001247 }
1248
Christine Franks245ffd42018-11-16 13:45:14 -08001249 /**
1250 * Local service that allows color transforms to be enabled from other system services.
1251 */
1252 public final class ColorDisplayServiceInternal {
1253
1254 /**
1255 * Set the current CCT value for the display white balance transform, and if the transform
1256 * is enabled, apply it.
1257 *
1258 * @param cct the color temperature in Kelvin.
1259 */
1260 public boolean setDisplayWhiteBalanceColorTemperature(int cct) {
1261 // Update the transform matrix even if it can't be applied.
Christine Franks245ffd42018-11-16 13:45:14 -08001262 mDisplayWhiteBalanceTintController.setMatrix(cct);
1263
1264 if (mDisplayWhiteBalanceTintController.isActivated()) {
Christine Franksc7fb9452019-02-04 08:45:33 -08001265 mHandler.sendEmptyMessage(MSG_APPLY_DISPLAY_WHITE_BALANCE);
Christine Franks245ffd42018-11-16 13:45:14 -08001266 return true;
1267 }
1268 return false;
1269 }
1270
1271 /**
Daniel Solomon37816412019-04-10 15:17:41 -07001272 * Reset the CCT value for the display white balance transform to its default value.
1273 */
1274 public boolean resetDisplayWhiteBalanceColorTemperature() {
1275 return setDisplayWhiteBalanceColorTemperature(getContext().getResources()
1276 .getInteger(R.integer.config_displayWhiteBalanceColorTemperatureDefault));
1277 }
1278
1279 /**
Christine Franks245ffd42018-11-16 13:45:14 -08001280 * Sets the listener and returns whether display white balance is currently enabled.
1281 */
1282 public boolean setDisplayWhiteBalanceListener(DisplayWhiteBalanceListener listener) {
1283 mDisplayWhiteBalanceListener = listener;
1284 return mDisplayWhiteBalanceTintController.isActivated();
1285 }
Daniel Solomon8b72c5b2018-11-25 11:07:13 -08001286
Christine Franksf3529b22019-01-03 13:20:17 -08001287 /**
Christine Franks66783a82019-03-28 11:45:56 -07001288 * Returns whether Display white balance is currently enabled.
1289 */
1290 public boolean isDisplayWhiteBalanceEnabled() {
1291 return isDisplayWhiteBalanceSettingEnabled();
1292 }
1293
1294 /**
Christine Franksf3529b22019-01-03 13:20:17 -08001295 * Adds a {@link WeakReference<ColorTransformController>} for a newly started activity, and
1296 * invokes {@link ColorTransformController#applyAppSaturation(float[], float[])} if needed.
1297 */
Christine Franks55194dc2019-01-15 13:47:06 -08001298 public boolean attachColorTransformController(String packageName, @UserIdInt int userId,
Christine Franksf3529b22019-01-03 13:20:17 -08001299 WeakReference<ColorTransformController> controller) {
1300 return mAppSaturationController
Christine Franks55194dc2019-01-15 13:47:06 -08001301 .addColorTransformController(packageName, userId, controller);
Christine Franksf3529b22019-01-03 13:20:17 -08001302 }
Christine Franks245ffd42018-11-16 13:45:14 -08001303 }
1304
1305 /**
1306 * Listener for changes in display white balance status.
1307 */
1308 public interface DisplayWhiteBalanceListener {
1309
1310 /**
1311 * Notify that the display white balance status has changed, either due to preemption by
1312 * another transform or the feature being turned off.
1313 */
Christine Franks66783a82019-03-28 11:45:56 -07001314 void onDisplayWhiteBalanceStatusChanged(boolean activated);
Christine Franks245ffd42018-11-16 13:45:14 -08001315 }
1316
Christine Franks09c229e2018-12-14 10:37:40 -08001317 private final class TintHandler extends Handler {
1318
Christine Franks83cc5412018-07-03 14:46:07 -07001319 private TintHandler(Looper looper) {
Christine Franks09c229e2018-12-14 10:37:40 -08001320 super(looper, null, true /* async */);
1321 }
1322
1323 @Override
1324 public void handleMessage(Message msg) {
1325 switch (msg.what) {
Christine Franks27912a32019-04-02 10:43:10 -07001326 case MSG_USER_CHANGED:
1327 onUserChanged(msg.arg1);
1328 break;
1329 case MSG_SET_UP:
1330 setUp();
1331 break;
Christine Franks09c229e2018-12-14 10:37:40 -08001332 case MSG_APPLY_GLOBAL_SATURATION:
1333 mGlobalSaturationTintController.setMatrix(msg.arg1);
1334 applyTint(mGlobalSaturationTintController, false);
1335 break;
Christine Franks83cc5412018-07-03 14:46:07 -07001336 case MSG_APPLY_NIGHT_DISPLAY_IMMEDIATE:
1337 applyTint(mNightDisplayTintController, true);
1338 break;
1339 case MSG_APPLY_NIGHT_DISPLAY_ANIMATED:
1340 applyTint(mNightDisplayTintController, false);
1341 break;
Christine Franksc7fb9452019-02-04 08:45:33 -08001342 case MSG_APPLY_DISPLAY_WHITE_BALANCE:
1343 applyTint(mDisplayWhiteBalanceTintController, false);
1344 break;
Christine Franks09c229e2018-12-14 10:37:40 -08001345 }
1346 }
1347 }
1348
Christine Franksf3529b22019-01-03 13:20:17 -08001349 /**
1350 * Interface for applying transforms to a given AppWindow.
1351 */
1352 public interface ColorTransformController {
1353
Christine Franksd154fe52019-01-04 17:17:45 -08001354 /**
1355 * Apply the given saturation (grayscale) matrix to the associated AppWindow.
1356 */
Christine Franksf3529b22019-01-03 13:20:17 -08001357 void applyAppSaturation(@Size(9) float[] matrix, @Size(3) float[] translation);
1358 }
1359
Christine Franks83cc5412018-07-03 14:46:07 -07001360 @VisibleForTesting
1361 final class BinderService extends IColorDisplayManager.Stub {
Christine Franks57fdde82018-07-03 14:46:07 -07001362
Christine Franks39b03112018-07-03 14:46:07 -07001363 @Override
Christine Franksd154fe52019-01-04 17:17:45 -08001364 public void setColorMode(int colorMode) {
1365 getContext().enforceCallingOrSelfPermission(
1366 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1367 "Permission required to set display color mode");
1368 final long token = Binder.clearCallingIdentity();
1369 try {
1370 setColorModeInternal(colorMode);
1371 } finally {
1372 Binder.restoreCallingIdentity(token);
1373 }
1374 }
1375
1376 @Override
1377 public int getColorMode() {
1378 final long token = Binder.clearCallingIdentity();
1379 try {
1380 return getColorModeInternal();
1381 } finally {
1382 Binder.restoreCallingIdentity(token);
1383 }
1384 }
1385
1386 @Override
Christine Franks39b03112018-07-03 14:46:07 -07001387 public boolean isDeviceColorManaged() {
1388 final long token = Binder.clearCallingIdentity();
1389 try {
1390 return isDeviceColorManagedInternal();
1391 } finally {
1392 Binder.restoreCallingIdentity(token);
1393 }
1394 }
Christine Franks09c229e2018-12-14 10:37:40 -08001395
1396 @Override
1397 public boolean setSaturationLevel(int level) {
1398 final boolean hasTransformsPermission = getContext()
1399 .checkCallingPermission(Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
1400 == PackageManager.PERMISSION_GRANTED;
1401 final boolean hasLegacyPermission = getContext()
1402 .checkCallingPermission(Manifest.permission.CONTROL_DISPLAY_SATURATION)
1403 == PackageManager.PERMISSION_GRANTED;
1404 if (!hasTransformsPermission && !hasLegacyPermission) {
1405 throw new SecurityException("Permission required to set display saturation level");
1406 }
1407 final long token = Binder.clearCallingIdentity();
1408 try {
1409 final Message message = mHandler.obtainMessage(MSG_APPLY_GLOBAL_SATURATION);
1410 message.arg1 = level;
1411 mHandler.sendMessage(message);
1412 } finally {
1413 Binder.restoreCallingIdentity(token);
1414 }
1415 return true;
1416 }
Christine Franksf3529b22019-01-03 13:20:17 -08001417
1418 @Override
Christine Franks6d21d342019-02-07 15:09:03 -08001419 public boolean isSaturationActivated() {
1420 getContext().enforceCallingPermission(
1421 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1422 "Permission required to get display saturation level");
1423 final long token = Binder.clearCallingIdentity();
1424 try {
1425 return !mGlobalSaturationTintController.isActivatedStateNotSet()
1426 && mGlobalSaturationTintController.isActivated();
1427 } finally {
1428 Binder.restoreCallingIdentity(token);
1429 }
1430 }
1431
1432 @Override
Christine Franksf3529b22019-01-03 13:20:17 -08001433 public boolean setAppSaturationLevel(String packageName, int level) {
1434 getContext().enforceCallingPermission(
1435 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1436 "Permission required to set display saturation level");
1437 final long token = Binder.clearCallingIdentity();
1438 try {
1439 return setAppSaturationLevelInternal(packageName, level);
1440 } finally {
1441 Binder.restoreCallingIdentity(token);
1442 }
1443 }
1444
Christine Franks55194dc2019-01-15 13:47:06 -08001445 public int getTransformCapabilities() {
1446 getContext().enforceCallingPermission(
1447 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1448 "Permission required to query transform capabilities");
1449 final long token = Binder.clearCallingIdentity();
1450 try {
1451 return getTransformCapabilitiesInternal();
1452 } finally {
1453 Binder.restoreCallingIdentity(token);
1454 }
1455 }
1456
Christine Franksf3529b22019-01-03 13:20:17 -08001457 @Override
Christine Franks83cc5412018-07-03 14:46:07 -07001458 public boolean setNightDisplayActivated(boolean activated) {
1459 getContext().enforceCallingOrSelfPermission(
1460 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1461 "Permission required to set night display activated");
1462 final long token = Binder.clearCallingIdentity();
1463 try {
1464 mNightDisplayTintController.setActivated(activated);
1465 return true;
1466 } finally {
1467 Binder.restoreCallingIdentity(token);
1468 }
1469 }
1470
1471 @Override
1472 public boolean isNightDisplayActivated() {
1473 final long token = Binder.clearCallingIdentity();
1474 try {
1475 return mNightDisplayTintController.isActivated();
1476 } finally {
1477 Binder.restoreCallingIdentity(token);
1478 }
1479 }
1480
1481 @Override
1482 public boolean setNightDisplayColorTemperature(int temperature) {
1483 getContext().enforceCallingOrSelfPermission(
1484 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1485 "Permission required to set night display temperature");
1486 final long token = Binder.clearCallingIdentity();
1487 try {
1488 return mNightDisplayTintController.setColorTemperature(temperature);
1489 } finally {
1490 Binder.restoreCallingIdentity(token);
1491 }
1492 }
1493
1494 @Override
1495 public int getNightDisplayColorTemperature() {
1496 final long token = Binder.clearCallingIdentity();
1497 try {
1498 return mNightDisplayTintController.getColorTemperature();
1499 } finally {
1500 Binder.restoreCallingIdentity(token);
1501 }
1502 }
1503
1504 @Override
1505 public boolean setNightDisplayAutoMode(int autoMode) {
1506 getContext().enforceCallingOrSelfPermission(
1507 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1508 "Permission required to set night display auto mode");
1509 final long token = Binder.clearCallingIdentity();
1510 try {
1511 return setNightDisplayAutoModeInternal(autoMode);
1512 } finally {
1513 Binder.restoreCallingIdentity(token);
1514 }
1515 }
1516
1517 @Override
1518 public int getNightDisplayAutoMode() {
1519 getContext().enforceCallingOrSelfPermission(
1520 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1521 "Permission required to get night display auto mode");
1522 final long token = Binder.clearCallingIdentity();
1523 try {
1524 return getNightDisplayAutoModeInternal();
1525 } finally {
1526 Binder.restoreCallingIdentity(token);
1527 }
1528 }
1529
1530 @Override
1531 public int getNightDisplayAutoModeRaw() {
1532 final long token = Binder.clearCallingIdentity();
1533 try {
1534 return getNightDisplayAutoModeRawInternal();
1535 } finally {
1536 Binder.restoreCallingIdentity(token);
1537 }
1538 }
1539
1540 @Override
1541 public boolean setNightDisplayCustomStartTime(Time startTime) {
1542 getContext().enforceCallingOrSelfPermission(
1543 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1544 "Permission required to set night display custom start time");
1545 final long token = Binder.clearCallingIdentity();
1546 try {
1547 return setNightDisplayCustomStartTimeInternal(startTime);
1548 } finally {
1549 Binder.restoreCallingIdentity(token);
1550 }
1551 }
1552
1553 @Override
1554 public Time getNightDisplayCustomStartTime() {
1555 final long token = Binder.clearCallingIdentity();
1556 try {
1557 return getNightDisplayCustomStartTimeInternal();
1558 } finally {
1559 Binder.restoreCallingIdentity(token);
1560 }
1561 }
1562
1563 @Override
1564 public boolean setNightDisplayCustomEndTime(Time endTime) {
1565 getContext().enforceCallingOrSelfPermission(
1566 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1567 "Permission required to set night display custom end time");
1568 final long token = Binder.clearCallingIdentity();
1569 try {
1570 return setNightDisplayCustomEndTimeInternal(endTime);
1571 } finally {
1572 Binder.restoreCallingIdentity(token);
1573 }
1574 }
1575
1576 @Override
1577 public Time getNightDisplayCustomEndTime() {
1578 final long token = Binder.clearCallingIdentity();
1579 try {
1580 return getNightDisplayCustomEndTimeInternal();
1581 } finally {
1582 Binder.restoreCallingIdentity(token);
1583 }
1584 }
1585
1586 @Override
Christine Franks66783a82019-03-28 11:45:56 -07001587 public boolean setDisplayWhiteBalanceEnabled(boolean enabled) {
1588 getContext().enforceCallingOrSelfPermission(
1589 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1590 "Permission required to set night display activated");
1591 final long token = Binder.clearCallingIdentity();
1592 try {
1593 return setDisplayWhiteBalanceSettingEnabled(enabled);
1594 } finally {
1595 Binder.restoreCallingIdentity(token);
1596 }
1597 }
1598
1599 @Override
1600 public boolean isDisplayWhiteBalanceEnabled() {
1601 final long token = Binder.clearCallingIdentity();
1602 try {
1603 return isDisplayWhiteBalanceSettingEnabled();
1604 } finally {
1605 Binder.restoreCallingIdentity(token);
1606 }
1607 }
1608
1609 @Override
Christine Franksf3529b22019-01-03 13:20:17 -08001610 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Christine Franksd154fe52019-01-04 17:17:45 -08001611 if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
1612 return;
1613 }
Christine Franksf3529b22019-01-03 13:20:17 -08001614
1615 final long token = Binder.clearCallingIdentity();
1616 try {
1617 dumpInternal(pw);
1618 } finally {
1619 Binder.restoreCallingIdentity(token);
1620 }
1621 }
Christine Franks39b03112018-07-03 14:46:07 -07001622 }
Justin Klaassen911e8892016-06-21 18:24:24 -07001623}