blob: 4f81c03adff69d3336136abc03e1db7727c973e5 [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 Franksa4ed3762019-01-24 15:37:04 -080026
Christine Franks0ada2772019-02-25 13:54:57 -080027import static com.android.server.display.color.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
Christine Franks39b03112018-07-03 14:46:07 -070028
Christine Franks09c229e2018-12-14 10:37:40 -080029import android.Manifest;
Justin Klaassen639214e2016-07-14 21:00:06 -070030import android.animation.Animator;
31import android.animation.AnimatorListenerAdapter;
32import android.animation.TypeEvaluator;
33import android.animation.ValueAnimator;
Justin Klaassen4346f632016-08-08 15:01:47 -070034import android.annotation.NonNull;
Justin Klaassen908b86c2016-08-08 09:18:42 -070035import android.annotation.Nullable;
Christine Franksf3529b22019-01-03 13:20:17 -080036import android.annotation.Size;
Christine Franks55194dc2019-01-15 13:47:06 -080037import android.annotation.UserIdInt;
Justin Klaassen911e8892016-06-21 18:24:24 -070038import android.app.AlarmManager;
39import android.content.BroadcastReceiver;
40import android.content.ContentResolver;
41import android.content.Context;
42import android.content.Intent;
43import android.content.IntentFilter;
Christine Franks09c229e2018-12-14 10:37:40 -080044import android.content.pm.PackageManager;
Daniel Solomon8b72c5b2018-11-25 11:07:13 -080045import android.content.res.Resources;
Justin Klaassen2696d992016-07-11 21:26:46 -070046import android.database.ContentObserver;
Christine Franksf3529b22019-01-03 13:20:17 -080047import android.hardware.display.ColorDisplayManager;
Christine Franks83cc5412018-07-03 14:46:07 -070048import android.hardware.display.ColorDisplayManager.AutoMode;
Christine Franksd154fe52019-01-04 17:17:45 -080049import android.hardware.display.ColorDisplayManager.ColorMode;
Christine Franks39b03112018-07-03 14:46:07 -070050import android.hardware.display.IColorDisplayManager;
Christine Franks83cc5412018-07-03 14:46:07 -070051import android.hardware.display.Time;
Justin Klaassen2696d992016-07-11 21:26:46 -070052import android.net.Uri;
Justin Klaassen639214e2016-07-14 21:00:06 -070053import android.opengl.Matrix;
Christine Franks39b03112018-07-03 14:46:07 -070054import android.os.Binder;
Justin Klaassen911e8892016-06-21 18:24:24 -070055import android.os.Handler;
56import android.os.Looper;
Christine Franks09c229e2018-12-14 10:37:40 -080057import android.os.Message;
Christine Franksd154fe52019-01-04 17:17:45 -080058import android.os.SystemProperties;
Justin Klaassen911e8892016-06-21 18:24:24 -070059import android.os.UserHandle;
60import android.provider.Settings.Secure;
Christine Franks57fdde82018-07-03 14:46:07 -070061import android.provider.Settings.System;
Justin Klaassen639214e2016-07-14 21:00:06 -070062import android.util.MathUtils;
Justin Klaassen911e8892016-06-21 18:24:24 -070063import android.util.Slog;
Christine Franks55194dc2019-01-15 13:47:06 -080064import android.view.SurfaceControl;
Christine Franks9114f462019-01-04 11:27:30 -080065import android.view.accessibility.AccessibilityManager;
Justin Klaassen639214e2016-07-14 21:00:06 -070066import android.view.animation.AnimationUtils;
Daniel Solomona4ab5672019-01-22 19:35:55 -080067
Christine Franks39b03112018-07-03 14:46:07 -070068import com.android.internal.R;
Christine Franks57fdde82018-07-03 14:46:07 -070069import com.android.internal.annotations.VisibleForTesting;
Christine Franksf3529b22019-01-03 13:20:17 -080070import com.android.internal.util.DumpUtils;
Christine Franks57fdde82018-07-03 14:46:07 -070071import com.android.server.DisplayThread;
Justin Klaassen911e8892016-06-21 18:24:24 -070072import com.android.server.SystemService;
73import com.android.server.twilight.TwilightListener;
74import com.android.server.twilight.TwilightManager;
75import com.android.server.twilight.TwilightState;
Christine Franksa4ed3762019-01-24 15:37:04 -080076
Christine Franksf3529b22019-01-03 13:20:17 -080077import java.io.FileDescriptor;
Daniel Solomon8b72c5b2018-11-25 11:07:13 -080078import java.io.PrintWriter;
Christine Franksf3529b22019-01-03 13:20:17 -080079import java.lang.ref.WeakReference;
Christine Franks57fdde82018-07-03 14:46:07 -070080import java.time.DateTimeException;
81import java.time.Instant;
Christine Franks03213462017-08-25 13:57:26 -070082import java.time.LocalDateTime;
83import java.time.LocalTime;
84import java.time.ZoneId;
Christine Franks57fdde82018-07-03 14:46:07 -070085import java.time.format.DateTimeParseException;
Christine Franks8ad71492017-10-24 19:04:22 -070086
Justin Klaassen911e8892016-06-21 18:24:24 -070087/**
Christine Franks39b03112018-07-03 14:46:07 -070088 * Controls the display's color transforms.
Justin Klaassen911e8892016-06-21 18:24:24 -070089 */
Christine Franks57fdde82018-07-03 14:46:07 -070090public final class ColorDisplayService extends SystemService {
Justin Klaassen911e8892016-06-21 18:24:24 -070091
Christine Franks7119e992019-03-14 17:28:21 -070092 static final String TAG = "ColorDisplayService";
Christine Franks7b83b4282017-01-18 14:55:00 -080093
94 /**
Justin Klaassen639214e2016-07-14 21:00:06 -070095 * The identity matrix, used if one of the given matrices is {@code null}.
96 */
Christine Franks6caba0f2019-03-07 17:48:25 -080097 static final float[] MATRIX_IDENTITY = new float[16];
Christine Franks57fdde82018-07-03 14:46:07 -070098
Justin Klaassen639214e2016-07-14 21:00:06 -070099 static {
100 Matrix.setIdentityM(MATRIX_IDENTITY, 0);
101 }
102
Christine Franks7119e992019-03-14 17:28:21 -0700103 /**
104 * The transition time, in milliseconds, for Night Display to turn on/off.
105 */
106 private static final long TRANSITION_DURATION = 3000L;
107
Christine Franks09c229e2018-12-14 10:37:40 -0800108 private static final int MSG_APPLY_NIGHT_DISPLAY_IMMEDIATE = 0;
109 private static final int MSG_APPLY_NIGHT_DISPLAY_ANIMATED = 1;
110 private static final int MSG_APPLY_GLOBAL_SATURATION = 2;
Christine Franksc7fb9452019-02-04 08:45:33 -0800111 private static final int MSG_APPLY_DISPLAY_WHITE_BALANCE = 3;
Christine Franks09c229e2018-12-14 10:37:40 -0800112
Justin Klaassen639214e2016-07-14 21:00:06 -0700113 /**
Christine Franks83cc5412018-07-03 14:46:07 -0700114 * Return value if a setting has not been set.
115 */
116 private static final int NOT_SET = -1;
117
118 /**
Justin Klaassen639214e2016-07-14 21:00:06 -0700119 * Evaluator used to animate color matrix transitions.
120 */
121 private static final ColorMatrixEvaluator COLOR_MATRIX_EVALUATOR = new ColorMatrixEvaluator();
122
Christine Franks83cc5412018-07-03 14:46:07 -0700123 private final NightDisplayTintController mNightDisplayTintController =
124 new NightDisplayTintController();
Christine Franks245ffd42018-11-16 13:45:14 -0800125
Long Ling1d3f1892019-02-06 12:34:02 -0800126 @VisibleForTesting
127 final DisplayWhiteBalanceTintController mDisplayWhiteBalanceTintController =
128 new DisplayWhiteBalanceTintController();
Christine Franks245ffd42018-11-16 13:45:14 -0800129
Christine Franks7119e992019-03-14 17:28:21 -0700130 private final TintController mGlobalSaturationTintController =
131 new GlobalSaturationTintController();
Christine Franks09c229e2018-12-14 10:37:40 -0800132
Christine Franks9114f462019-01-04 11:27:30 -0800133 /**
134 * Matrix and offset used for converting color to grayscale.
135 */
136 private static final float[] MATRIX_GRAYSCALE = new float[]{
137 .2126f, .2126f, .2126f, 0f,
138 .7152f, .7152f, .7152f, 0f,
139 .0722f, .0722f, .0722f, 0f,
140 0f, 0f, 0f, 1f
141 };
142
143 /**
144 * Matrix and offset used for luminance inversion. Represents a transform from RGB to YIQ color
145 * space, rotation around the Y axis by 180 degrees, transform back to RGB color space, and
146 * subtraction from 1. The last row represents a non-multiplied addition, see surfaceflinger's
147 * ProgramCache for full implementation details.
148 */
Christine Franksd154fe52019-01-04 17:17:45 -0800149 private static final float[] MATRIX_INVERT_COLOR = new float[]{
Christine Franks9114f462019-01-04 11:27:30 -0800150 0.402f, -0.598f, -0.599f, 0f,
151 -1.174f, -0.174f, -1.175f, 0f,
152 -0.228f, -0.228f, 0.772f, 0f,
153 1f, 1f, 1f, 1f
154 };
155
Justin Klaassen2696d992016-07-11 21:26:46 -0700156 private final Handler mHandler;
157
Christine Franksf3529b22019-01-03 13:20:17 -0800158 private final AppSaturationController mAppSaturationController = new AppSaturationController();
159
Justin Klaassen911e8892016-06-21 18:24:24 -0700160 private int mCurrentUser = UserHandle.USER_NULL;
Justin Klaassen2696d992016-07-11 21:26:46 -0700161 private ContentObserver mUserSetupObserver;
Justin Klaassen911e8892016-06-21 18:24:24 -0700162 private boolean mBootCompleted;
163
Christine Franks57fdde82018-07-03 14:46:07 -0700164 private ContentObserver mContentObserver;
Christine Franks57fdde82018-07-03 14:46:07 -0700165
Christine Franks245ffd42018-11-16 13:45:14 -0800166 private DisplayWhiteBalanceListener mDisplayWhiteBalanceListener;
167
Christine Franks57fdde82018-07-03 14:46:07 -0700168 private NightDisplayAutoMode mNightDisplayAutoMode;
Justin Klaassen911e8892016-06-21 18:24:24 -0700169
Christine Franks5397f032017-11-01 18:35:16 -0700170 public ColorDisplayService(Context context) {
Justin Klaassen911e8892016-06-21 18:24:24 -0700171 super(context);
Christine Franks83cc5412018-07-03 14:46:07 -0700172 mHandler = new TintHandler(DisplayThread.get().getLooper());
Justin Klaassen911e8892016-06-21 18:24:24 -0700173 }
174
175 @Override
176 public void onStart() {
Christine Franks39b03112018-07-03 14:46:07 -0700177 publishBinderService(Context.COLOR_DISPLAY_SERVICE, new BinderService());
Christine Franks245ffd42018-11-16 13:45:14 -0800178 publishLocalService(ColorDisplayServiceInternal.class, new ColorDisplayServiceInternal());
Christine Franks0ada2772019-02-25 13:54:57 -0800179 publishLocalService(DisplayTransformManager.class, new DisplayTransformManager());
Justin Klaassen911e8892016-06-21 18:24:24 -0700180 }
181
182 @Override
Justin Klaassen2696d992016-07-11 21:26:46 -0700183 public void onBootPhase(int phase) {
Christine Frankse5bb03e2017-02-10 17:36:10 -0800184 if (phase >= PHASE_BOOT_COMPLETED) {
Justin Klaassen2696d992016-07-11 21:26:46 -0700185 mBootCompleted = true;
186
187 // Register listeners now that boot is complete.
188 if (mCurrentUser != UserHandle.USER_NULL && mUserSetupObserver == null) {
189 setUp();
190 }
191 }
192 }
193
194 @Override
Justin Klaassen911e8892016-06-21 18:24:24 -0700195 public void onStartUser(int userHandle) {
196 super.onStartUser(userHandle);
197
Justin Klaassen911e8892016-06-21 18:24:24 -0700198 if (mCurrentUser == UserHandle.USER_NULL) {
Justin Klaassen2696d992016-07-11 21:26:46 -0700199 onUserChanged(userHandle);
Justin Klaassen911e8892016-06-21 18:24:24 -0700200 }
201 }
202
203 @Override
204 public void onSwitchUser(int userHandle) {
205 super.onSwitchUser(userHandle);
206
Justin Klaassen2696d992016-07-11 21:26:46 -0700207 onUserChanged(userHandle);
Justin Klaassen911e8892016-06-21 18:24:24 -0700208 }
209
210 @Override
211 public void onStopUser(int userHandle) {
212 super.onStopUser(userHandle);
213
Justin Klaassen911e8892016-06-21 18:24:24 -0700214 if (mCurrentUser == userHandle) {
Justin Klaassen2696d992016-07-11 21:26:46 -0700215 onUserChanged(UserHandle.USER_NULL);
Justin Klaassen911e8892016-06-21 18:24:24 -0700216 }
217 }
218
Justin Klaassen2696d992016-07-11 21:26:46 -0700219 private void onUserChanged(int userHandle) {
220 final ContentResolver cr = getContext().getContentResolver();
Justin Klaassen911e8892016-06-21 18:24:24 -0700221
Justin Klaassen2696d992016-07-11 21:26:46 -0700222 if (mCurrentUser != UserHandle.USER_NULL) {
223 if (mUserSetupObserver != null) {
224 cr.unregisterContentObserver(mUserSetupObserver);
225 mUserSetupObserver = null;
226 } else if (mBootCompleted) {
227 tearDown();
228 }
229 }
230
231 mCurrentUser = userHandle;
232
233 if (mCurrentUser != UserHandle.USER_NULL) {
234 if (!isUserSetupCompleted(cr, mCurrentUser)) {
235 mUserSetupObserver = new ContentObserver(mHandler) {
236 @Override
237 public void onChange(boolean selfChange, Uri uri) {
238 if (isUserSetupCompleted(cr, mCurrentUser)) {
239 cr.unregisterContentObserver(this);
240 mUserSetupObserver = null;
241
242 if (mBootCompleted) {
243 setUp();
244 }
245 }
246 }
247 };
248 cr.registerContentObserver(Secure.getUriFor(Secure.USER_SETUP_COMPLETE),
Christine Franks39b03112018-07-03 14:46:07 -0700249 false /* notifyForDescendants */, mUserSetupObserver, mCurrentUser);
Justin Klaassen2696d992016-07-11 21:26:46 -0700250 } else if (mBootCompleted) {
251 setUp();
Justin Klaassen911e8892016-06-21 18:24:24 -0700252 }
253 }
254 }
255
Justin Klaassen2696d992016-07-11 21:26:46 -0700256 private static boolean isUserSetupCompleted(ContentResolver cr, int userHandle) {
257 return Secure.getIntForUser(cr, Secure.USER_SETUP_COMPLETE, 0, userHandle) == 1;
258 }
259
260 private void setUp() {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700261 Slog.d(TAG, "setUp: currentUser=" + mCurrentUser);
262
Christine Franks57fdde82018-07-03 14:46:07 -0700263 // Listen for external changes to any of the settings.
264 if (mContentObserver == null) {
265 mContentObserver = new ContentObserver(new Handler(DisplayThread.get().getLooper())) {
266 @Override
267 public void onChange(boolean selfChange, Uri uri) {
268 super.onChange(selfChange, uri);
269
270 final String setting = uri == null ? null : uri.getLastPathSegment();
271 if (setting != null) {
272 switch (setting) {
273 case Secure.NIGHT_DISPLAY_ACTIVATED:
Christine Franks78a4dd42019-02-08 11:09:30 -0800274 final boolean activated = mNightDisplayTintController
275 .isActivatedSetting();
Christine Franks83cc5412018-07-03 14:46:07 -0700276 if (mNightDisplayTintController.isActivatedStateNotSet()
277 || mNightDisplayTintController.isActivated() != activated) {
Christine Franks78a4dd42019-02-08 11:09:30 -0800278 mNightDisplayTintController.setActivated(activated);
Christine Franks83cc5412018-07-03 14:46:07 -0700279 }
Christine Franks57fdde82018-07-03 14:46:07 -0700280 break;
281 case Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE:
Christine Franks78a4dd42019-02-08 11:09:30 -0800282 final int temperature = mNightDisplayTintController
283 .getColorTemperatureSetting();
Christine Franks83cc5412018-07-03 14:46:07 -0700284 if (mNightDisplayTintController.getColorTemperature()
285 != temperature) {
286 mNightDisplayTintController
287 .onColorTemperatureChanged(temperature);
288 }
Christine Franks57fdde82018-07-03 14:46:07 -0700289 break;
290 case Secure.NIGHT_DISPLAY_AUTO_MODE:
Christine Franks83cc5412018-07-03 14:46:07 -0700291 onNightDisplayAutoModeChanged(getNightDisplayAutoModeInternal());
Christine Franks57fdde82018-07-03 14:46:07 -0700292 break;
293 case Secure.NIGHT_DISPLAY_CUSTOM_START_TIME:
294 onNightDisplayCustomStartTimeChanged(
Christine Franks83cc5412018-07-03 14:46:07 -0700295 getNightDisplayCustomStartTimeInternal().getLocalTime());
Christine Franks57fdde82018-07-03 14:46:07 -0700296 break;
297 case Secure.NIGHT_DISPLAY_CUSTOM_END_TIME:
298 onNightDisplayCustomEndTimeChanged(
Christine Franks83cc5412018-07-03 14:46:07 -0700299 getNightDisplayCustomEndTimeInternal().getLocalTime());
Christine Franks57fdde82018-07-03 14:46:07 -0700300 break;
301 case System.DISPLAY_COLOR_MODE:
Christine Franks71e003e2019-01-24 14:40:20 -0800302 onDisplayColorModeChanged(getColorModeInternal());
Christine Franks57fdde82018-07-03 14:46:07 -0700303 break;
Christine Franks57fdde82018-07-03 14:46:07 -0700304 case Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED:
Christine Franks9114f462019-01-04 11:27:30 -0800305 onAccessibilityInversionChanged();
306 onAccessibilityActivated();
307 break;
308 case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED:
309 onAccessibilityDaltonizerChanged();
310 onAccessibilityActivated();
311 break;
312 case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER:
313 onAccessibilityDaltonizerChanged();
Christine Franks57fdde82018-07-03 14:46:07 -0700314 break;
Christine Franks245ffd42018-11-16 13:45:14 -0800315 case Secure.DISPLAY_WHITE_BALANCE_ENABLED:
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800316 updateDisplayWhiteBalanceStatus();
Christine Franks245ffd42018-11-16 13:45:14 -0800317 break;
Christine Franks57fdde82018-07-03 14:46:07 -0700318 }
319 }
320 }
321 };
322 }
323 final ContentResolver cr = getContext().getContentResolver();
324 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_ACTIVATED),
325 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
326 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE),
327 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
328 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_AUTO_MODE),
329 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
330 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_START_TIME),
331 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
332 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_END_TIME),
333 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
334 cr.registerContentObserver(System.getUriFor(System.DISPLAY_COLOR_MODE),
335 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
336 cr.registerContentObserver(
337 Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED),
338 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
339 cr.registerContentObserver(
340 Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED),
341 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
Christine Franks9114f462019-01-04 11:27:30 -0800342 cr.registerContentObserver(
343 Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER),
344 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
Christine Franks245ffd42018-11-16 13:45:14 -0800345 cr.registerContentObserver(Secure.getUriFor(Secure.DISPLAY_WHITE_BALANCE_ENABLED),
346 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
Justin Klaassen911e8892016-06-21 18:24:24 -0700347
Christine Franks11e63152019-03-14 11:16:06 -0700348 // Apply the accessibility settings first, since they override most other settings.
349 onAccessibilityInversionChanged();
350 onAccessibilityDaltonizerChanged();
351
Christine Frankscf388c22018-05-15 15:48:10 -0700352 // Set the color mode, if valid, and immediately apply the updated tint matrix based on the
353 // existing activated state. This ensures consistency of tint across the color mode change.
Christine Franks71e003e2019-01-24 14:40:20 -0800354 onDisplayColorModeChanged(getColorModeInternal());
Christine Frankscf388c22018-05-15 15:48:10 -0700355
Christine Franksa4ed3762019-01-24 15:37:04 -0800356 if (mNightDisplayTintController.isAvailable(getContext())) {
Christine Franks245ffd42018-11-16 13:45:14 -0800357 // Reset the activated state.
358 mNightDisplayTintController.setActivated(null);
Christine Frankscf388c22018-05-15 15:48:10 -0700359
Christine Franks245ffd42018-11-16 13:45:14 -0800360 // Prepare the night display color transformation matrix.
361 mNightDisplayTintController
362 .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix());
Christine Franks78a4dd42019-02-08 11:09:30 -0800363 mNightDisplayTintController
364 .setMatrix(mNightDisplayTintController.getColorTemperatureSetting());
Christine Franks8ad71492017-10-24 19:04:22 -0700365
Christine Franks245ffd42018-11-16 13:45:14 -0800366 // Initialize the current auto mode.
Christine Franks83cc5412018-07-03 14:46:07 -0700367 onNightDisplayAutoModeChanged(getNightDisplayAutoModeInternal());
Christine Franks6418d0b2017-02-13 09:48:00 -0800368
Christine Franks83cc5412018-07-03 14:46:07 -0700369 // Force the initialization of the current saved activation state.
Christine Franks245ffd42018-11-16 13:45:14 -0800370 if (mNightDisplayTintController.isActivatedStateNotSet()) {
Christine Franks78a4dd42019-02-08 11:09:30 -0800371 mNightDisplayTintController
372 .setActivated(mNightDisplayTintController.isActivatedSetting());
Christine Franks245ffd42018-11-16 13:45:14 -0800373 }
374 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700375
Christine Franksa4ed3762019-01-24 15:37:04 -0800376 if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) {
Christine Franks245ffd42018-11-16 13:45:14 -0800377 // Prepare the display white balance transform matrix.
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800378 mDisplayWhiteBalanceTintController.setUp(getContext(), true /* needsLinear */);
Christine Franks245ffd42018-11-16 13:45:14 -0800379
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800380 updateDisplayWhiteBalanceStatus();
Justin Klaassen911e8892016-06-21 18:24:24 -0700381 }
382 }
383
Justin Klaassen2696d992016-07-11 21:26:46 -0700384 private void tearDown() {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700385 Slog.d(TAG, "tearDown: currentUser=" + mCurrentUser);
386
Christine Franks57fdde82018-07-03 14:46:07 -0700387 getContext().getContentResolver().unregisterContentObserver(mContentObserver);
388
Christine Franksa4ed3762019-01-24 15:37:04 -0800389 if (mNightDisplayTintController.isAvailable(getContext())) {
Christine Franks245ffd42018-11-16 13:45:14 -0800390 if (mNightDisplayAutoMode != null) {
391 mNightDisplayAutoMode.onStop();
392 mNightDisplayAutoMode = null;
393 }
394 mNightDisplayTintController.endAnimator();
Justin Klaassen911e8892016-06-21 18:24:24 -0700395 }
396
Christine Franksa4ed3762019-01-24 15:37:04 -0800397 if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) {
Christine Franks245ffd42018-11-16 13:45:14 -0800398 mDisplayWhiteBalanceTintController.endAnimator();
Justin Klaassen639214e2016-07-14 21:00:06 -0700399 }
Christine Franks6d21d342019-02-07 15:09:03 -0800400
401 if (mGlobalSaturationTintController.isAvailable(getContext())) {
402 mGlobalSaturationTintController.setActivated(null);
403 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700404 }
405
Christine Franks57fdde82018-07-03 14:46:07 -0700406 private void onNightDisplayAutoModeChanged(int autoMode) {
407 Slog.d(TAG, "onNightDisplayAutoModeChanged: autoMode=" + autoMode);
Justin Klaassenec8837a2016-08-23 12:04:42 -0700408
Christine Franks57fdde82018-07-03 14:46:07 -0700409 if (mNightDisplayAutoMode != null) {
410 mNightDisplayAutoMode.onStop();
411 mNightDisplayAutoMode = null;
Justin Klaassen911e8892016-06-21 18:24:24 -0700412 }
413
Christine Franks83cc5412018-07-03 14:46:07 -0700414 if (autoMode == AUTO_MODE_CUSTOM_TIME) {
Christine Franks57fdde82018-07-03 14:46:07 -0700415 mNightDisplayAutoMode = new CustomNightDisplayAutoMode();
Christine Franks83cc5412018-07-03 14:46:07 -0700416 } else if (autoMode == AUTO_MODE_TWILIGHT) {
Christine Franks57fdde82018-07-03 14:46:07 -0700417 mNightDisplayAutoMode = new TwilightNightDisplayAutoMode();
Justin Klaassen911e8892016-06-21 18:24:24 -0700418 }
419
Christine Franks57fdde82018-07-03 14:46:07 -0700420 if (mNightDisplayAutoMode != null) {
421 mNightDisplayAutoMode.onStart();
Justin Klaassen911e8892016-06-21 18:24:24 -0700422 }
423 }
424
Christine Franks57fdde82018-07-03 14:46:07 -0700425 private void onNightDisplayCustomStartTimeChanged(LocalTime startTime) {
426 Slog.d(TAG, "onNightDisplayCustomStartTimeChanged: startTime=" + startTime);
Justin Klaassenec8837a2016-08-23 12:04:42 -0700427
Christine Franks57fdde82018-07-03 14:46:07 -0700428 if (mNightDisplayAutoMode != null) {
429 mNightDisplayAutoMode.onCustomStartTimeChanged(startTime);
Justin Klaassen911e8892016-06-21 18:24:24 -0700430 }
431 }
432
Christine Franks57fdde82018-07-03 14:46:07 -0700433 private void onNightDisplayCustomEndTimeChanged(LocalTime endTime) {
434 Slog.d(TAG, "onNightDisplayCustomEndTimeChanged: endTime=" + endTime);
Justin Klaassenec8837a2016-08-23 12:04:42 -0700435
Christine Franks57fdde82018-07-03 14:46:07 -0700436 if (mNightDisplayAutoMode != null) {
437 mNightDisplayAutoMode.onCustomEndTimeChanged(endTime);
Justin Klaassen911e8892016-06-21 18:24:24 -0700438 }
439 }
440
Christine Franks57fdde82018-07-03 14:46:07 -0700441 private void onDisplayColorModeChanged(int mode) {
Christine Franks83cc5412018-07-03 14:46:07 -0700442 if (mode == NOT_SET) {
Christine Frankscf388c22018-05-15 15:48:10 -0700443 return;
444 }
445
Christine Franks245ffd42018-11-16 13:45:14 -0800446 mNightDisplayTintController.cancelAnimator();
447 mDisplayWhiteBalanceTintController.cancelAnimator();
448
Christine Franksa4ed3762019-01-24 15:37:04 -0800449 if (mNightDisplayTintController.isAvailable(getContext())) {
450 mNightDisplayTintController
451 .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
Christine Franks78a4dd42019-02-08 11:09:30 -0800452 mNightDisplayTintController
453 .setMatrix(mNightDisplayTintController.getColorTemperatureSetting());
Christine Franksa4ed3762019-01-24 15:37:04 -0800454 }
Christine Franks245ffd42018-11-16 13:45:14 -0800455
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800456 updateDisplayWhiteBalanceStatus();
Christine Franks8ad71492017-10-24 19:04:22 -0700457
Christine Franks218e6562017-11-27 10:20:14 -0800458 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
Christine Franks245ffd42018-11-16 13:45:14 -0800459 dtm.setColorMode(mode, mNightDisplayTintController.getMatrix());
Christine Franks8ad71492017-10-24 19:04:22 -0700460 }
461
Christine Franks9114f462019-01-04 11:27:30 -0800462 private void onAccessibilityActivated() {
Christine Franks71e003e2019-01-24 14:40:20 -0800463 onDisplayColorModeChanged(getColorModeInternal());
Daniel Solomon317a3572018-03-30 18:36:37 -0700464 }
465
Christine Franks9114f462019-01-04 11:27:30 -0800466 /**
467 * Apply the accessibility daltonizer transform based on the settings value.
468 */
469 private void onAccessibilityDaltonizerChanged() {
470 final boolean enabled = Secure.getIntForUser(getContext().getContentResolver(),
471 Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, mCurrentUser) != 0;
472 final int daltonizerMode = enabled ? Secure.getIntForUser(getContext().getContentResolver(),
473 Secure.ACCESSIBILITY_DISPLAY_DALTONIZER,
474 AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY, mCurrentUser)
475 : AccessibilityManager.DALTONIZER_DISABLED;
476
477 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
478 if (daltonizerMode == AccessibilityManager.DALTONIZER_SIMULATE_MONOCHROMACY) {
479 // Monochromacy isn't supported by the native Daltonizer implementation; use grayscale.
480 dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_GRAYSCALE,
481 MATRIX_GRAYSCALE);
482 dtm.setDaltonizerMode(AccessibilityManager.DALTONIZER_DISABLED);
483 } else {
484 dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_GRAYSCALE, null);
485 dtm.setDaltonizerMode(daltonizerMode);
486 }
487 }
488
489 /**
490 * Apply the accessibility inversion transform based on the settings value.
491 */
492 private void onAccessibilityInversionChanged() {
493 final boolean enabled = Secure.getIntForUser(getContext().getContentResolver(),
494 Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, mCurrentUser) != 0;
495 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
496 dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_INVERT_COLOR,
497 enabled ? MATRIX_INVERT_COLOR : null);
498 }
Christine Franks8ad71492017-10-24 19:04:22 -0700499
Christine Franks6418d0b2017-02-13 09:48:00 -0800500 /**
501 * Applies current color temperature matrix, or removes it if deactivated.
502 *
503 * @param immediate {@code true} skips transition animation
504 */
Christine Franks245ffd42018-11-16 13:45:14 -0800505 private void applyTint(TintController tintController, boolean immediate) {
506 tintController.cancelAnimator();
Christine Franks6418d0b2017-02-13 09:48:00 -0800507
Christine Franks6418d0b2017-02-13 09:48:00 -0800508 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
Christine Franks245ffd42018-11-16 13:45:14 -0800509 final float[] from = dtm.getColorMatrix(tintController.getLevel());
510 final float[] to = tintController.getMatrix();
Christine Franks6418d0b2017-02-13 09:48:00 -0800511
512 if (immediate) {
Christine Franks245ffd42018-11-16 13:45:14 -0800513 dtm.setColorMatrix(tintController.getLevel(), to);
Christine Franks6418d0b2017-02-13 09:48:00 -0800514 } else {
Christine Franks245ffd42018-11-16 13:45:14 -0800515 tintController.setAnimator(ValueAnimator.ofObject(COLOR_MATRIX_EVALUATOR,
516 from == null ? MATRIX_IDENTITY : from, to));
517 tintController.getAnimator().setDuration(TRANSITION_DURATION);
518 tintController.getAnimator().setInterpolator(AnimationUtils.loadInterpolator(
Christine Franks6418d0b2017-02-13 09:48:00 -0800519 getContext(), android.R.interpolator.fast_out_slow_in));
Christine Franks245ffd42018-11-16 13:45:14 -0800520 tintController.getAnimator().addUpdateListener((ValueAnimator animator) -> {
521 final float[] value = (float[]) animator.getAnimatedValue();
522 dtm.setColorMatrix(tintController.getLevel(), value);
Christine Franks6418d0b2017-02-13 09:48:00 -0800523 });
Christine Franks245ffd42018-11-16 13:45:14 -0800524 tintController.getAnimator().addListener(new AnimatorListenerAdapter() {
Christine Franks6418d0b2017-02-13 09:48:00 -0800525
526 private boolean mIsCancelled;
527
528 @Override
529 public void onAnimationCancel(Animator animator) {
530 mIsCancelled = true;
531 }
532
533 @Override
534 public void onAnimationEnd(Animator animator) {
535 if (!mIsCancelled) {
536 // Ensure final color matrix is set at the end of the animation. If the
537 // animation is cancelled then don't set the final color matrix so the new
538 // animator can pick up from where this one left off.
Christine Franks245ffd42018-11-16 13:45:14 -0800539 dtm.setColorMatrix(tintController.getLevel(), to);
Christine Franks6418d0b2017-02-13 09:48:00 -0800540 }
Christine Franks245ffd42018-11-16 13:45:14 -0800541 tintController.setAnimator(null);
Christine Franks6418d0b2017-02-13 09:48:00 -0800542 }
543 });
Christine Franks245ffd42018-11-16 13:45:14 -0800544 tintController.getAnimator().start();
Christine Franks6418d0b2017-02-13 09:48:00 -0800545 }
546 }
547
548 /**
Christine Franks39b03112018-07-03 14:46:07 -0700549 * Returns the first date time corresponding to the local time that occurs before the provided
550 * date time.
Christine Franks03213462017-08-25 13:57:26 -0700551 *
552 * @param compareTime the LocalDateTime to compare against
553 * @return the prior LocalDateTime corresponding to this local time
554 */
Christine Franks57fdde82018-07-03 14:46:07 -0700555 @VisibleForTesting
556 static LocalDateTime getDateTimeBefore(LocalTime localTime, LocalDateTime compareTime) {
Christine Franks03213462017-08-25 13:57:26 -0700557 final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
558 compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
559
560 // Check if the local time has passed, if so return the same time yesterday.
561 return ldt.isAfter(compareTime) ? ldt.minusDays(1) : ldt;
562 }
563
564 /**
Christine Franks39b03112018-07-03 14:46:07 -0700565 * Returns the first date time corresponding to this local time that occurs after the provided
566 * date time.
Christine Franks03213462017-08-25 13:57:26 -0700567 *
568 * @param compareTime the LocalDateTime to compare against
569 * @return the next LocalDateTime corresponding to this local time
570 */
Christine Franks57fdde82018-07-03 14:46:07 -0700571 @VisibleForTesting
572 static LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) {
Christine Franks03213462017-08-25 13:57:26 -0700573 final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
574 compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
575
576 // Check if the local time has passed, if so return the same time tomorrow.
577 return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
578 }
579
Long Ling1d3f1892019-02-06 12:34:02 -0800580 @VisibleForTesting
581 void updateDisplayWhiteBalanceStatus() {
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800582 boolean oldActivated = mDisplayWhiteBalanceTintController.isActivated();
Christine Franks0ada2772019-02-25 13:54:57 -0800583 mDisplayWhiteBalanceTintController.setActivated(isDisplayWhiteBalanceSettingEnabled()
584 && !mNightDisplayTintController.isActivated()
585 && DisplayTransformManager.needsLinearColorMatrix());
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800586 boolean activated = mDisplayWhiteBalanceTintController.isActivated();
587
588 if (mDisplayWhiteBalanceListener != null && oldActivated != activated) {
589 mDisplayWhiteBalanceListener.onDisplayWhiteBalanceStatusChanged(activated);
Christine Franks245ffd42018-11-16 13:45:14 -0800590 }
Daniel Solomon86508f82019-01-18 19:01:02 -0800591
592 // If disabled, clear the tint. If enabled, do nothing more here and let the next
593 // temperature update set the correct tint.
594 if (!activated) {
Christine Franksc7fb9452019-02-04 08:45:33 -0800595 mHandler.sendEmptyMessage(MSG_APPLY_DISPLAY_WHITE_BALANCE);
Daniel Solomon86508f82019-01-18 19:01:02 -0800596 }
Christine Franks245ffd42018-11-16 13:45:14 -0800597 }
598
599 private boolean isDisplayWhiteBalanceSettingEnabled() {
600 return Secure.getIntForUser(getContext().getContentResolver(),
601 Secure.DISPLAY_WHITE_BALANCE_ENABLED, 0, mCurrentUser) == 1;
602 }
603
Christine Franks39b03112018-07-03 14:46:07 -0700604 private boolean isDeviceColorManagedInternal() {
605 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
606 return dtm.isDeviceColorManaged();
607 }
608
Christine Franks55194dc2019-01-15 13:47:06 -0800609 private int getTransformCapabilitiesInternal() {
610 int availabilityFlags = ColorDisplayManager.CAPABILITY_NONE;
611 if (SurfaceControl.getProtectedContentSupport()) {
612 availabilityFlags |= ColorDisplayManager.CAPABILITY_PROTECTED_CONTENT;
613 }
614 final Resources res = getContext().getResources();
615 if (res.getBoolean(R.bool.config_setColorTransformAccelerated)) {
616 availabilityFlags |= ColorDisplayManager.CAPABILITY_HARDWARE_ACCELERATION_GLOBAL;
617 }
618 if (res.getBoolean(R.bool.config_setColorTransformAcceleratedPerLayer)) {
619 availabilityFlags |= ColorDisplayManager.CAPABILITY_HARDWARE_ACCELERATION_PER_APP;
620 }
621 return availabilityFlags;
622 }
623
Christine Franks83cc5412018-07-03 14:46:07 -0700624 private boolean setNightDisplayAutoModeInternal(@AutoMode int autoMode) {
625 if (getNightDisplayAutoModeInternal() != autoMode) {
626 Secure.putStringForUser(getContext().getContentResolver(),
627 Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
628 null,
629 mCurrentUser);
630 }
631 return Secure.putIntForUser(getContext().getContentResolver(),
632 Secure.NIGHT_DISPLAY_AUTO_MODE, autoMode, mCurrentUser);
633 }
634
635 private int getNightDisplayAutoModeInternal() {
636 int autoMode = getNightDisplayAutoModeRawInternal();
637 if (autoMode == NOT_SET) {
638 autoMode = getContext().getResources().getInteger(
639 R.integer.config_defaultNightDisplayAutoMode);
640 }
641 if (autoMode != AUTO_MODE_DISABLED
642 && autoMode != AUTO_MODE_CUSTOM_TIME
643 && autoMode != AUTO_MODE_TWILIGHT) {
644 Slog.e(TAG, "Invalid autoMode: " + autoMode);
645 autoMode = AUTO_MODE_DISABLED;
646 }
647 return autoMode;
648 }
649
650 private int getNightDisplayAutoModeRawInternal() {
651 return Secure
652 .getIntForUser(getContext().getContentResolver(), Secure.NIGHT_DISPLAY_AUTO_MODE,
653 NOT_SET, mCurrentUser);
654 }
655
656 private Time getNightDisplayCustomStartTimeInternal() {
657 int startTimeValue = Secure.getIntForUser(getContext().getContentResolver(),
658 Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, NOT_SET, mCurrentUser);
659 if (startTimeValue == NOT_SET) {
660 startTimeValue = getContext().getResources().getInteger(
661 R.integer.config_defaultNightDisplayCustomStartTime);
662 }
663 return new Time(LocalTime.ofSecondOfDay(startTimeValue / 1000));
664 }
665
666 private boolean setNightDisplayCustomStartTimeInternal(Time startTime) {
667 return Secure.putIntForUser(getContext().getContentResolver(),
668 Secure.NIGHT_DISPLAY_CUSTOM_START_TIME,
669 startTime.getLocalTime().toSecondOfDay() * 1000,
670 mCurrentUser);
671 }
672
673 private Time getNightDisplayCustomEndTimeInternal() {
674 int endTimeValue = Secure.getIntForUser(getContext().getContentResolver(),
675 Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, NOT_SET, mCurrentUser);
676 if (endTimeValue == NOT_SET) {
677 endTimeValue = getContext().getResources().getInteger(
678 R.integer.config_defaultNightDisplayCustomEndTime);
679 }
680 return new Time(LocalTime.ofSecondOfDay(endTimeValue / 1000));
681 }
682
683 private boolean setNightDisplayCustomEndTimeInternal(Time endTime) {
684 return Secure.putIntForUser(getContext().getContentResolver(),
685 Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, endTime.getLocalTime().toSecondOfDay() * 1000,
686 mCurrentUser);
687 }
688
Christine Franks57fdde82018-07-03 14:46:07 -0700689 /**
690 * Returns the last time the night display transform activation state was changed, or {@link
691 * LocalDateTime#MIN} if night display has never been activated.
692 */
Christine Franks245ffd42018-11-16 13:45:14 -0800693 private LocalDateTime getNightDisplayLastActivatedTimeSetting() {
Christine Franks57fdde82018-07-03 14:46:07 -0700694 final ContentResolver cr = getContext().getContentResolver();
695 final String lastActivatedTime = Secure.getStringForUser(
696 cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, getContext().getUserId());
697 if (lastActivatedTime != null) {
698 try {
699 return LocalDateTime.parse(lastActivatedTime);
700 } catch (DateTimeParseException ignored) {
701 }
702 // Uses the old epoch time.
703 try {
704 return LocalDateTime.ofInstant(
705 Instant.ofEpochMilli(Long.parseLong(lastActivatedTime)),
706 ZoneId.systemDefault());
707 } catch (DateTimeException | NumberFormatException ignored) {
708 }
709 }
710 return LocalDateTime.MIN;
711 }
712
Christine Franksf3529b22019-01-03 13:20:17 -0800713 private boolean setAppSaturationLevelInternal(String packageName, int saturationLevel) {
714 return mAppSaturationController
715 .setSaturationLevel(packageName, mCurrentUser, saturationLevel);
716 }
717
Christine Franksd154fe52019-01-04 17:17:45 -0800718 private void setColorModeInternal(@ColorMode int colorMode) {
719 if (!isColorModeAvailable(colorMode)) {
720 throw new IllegalArgumentException("Invalid colorMode: " + colorMode);
721 }
722 System.putIntForUser(getContext().getContentResolver(), System.DISPLAY_COLOR_MODE,
723 colorMode,
724 mCurrentUser);
725 }
726
Christine Franks71e003e2019-01-24 14:40:20 -0800727 private @ColorMode int getColorModeInternal() {
Christine Franksd154fe52019-01-04 17:17:45 -0800728 final ContentResolver cr = getContext().getContentResolver();
729 if (Secure.getIntForUser(cr, Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED,
730 0, mCurrentUser) == 1
731 || Secure.getIntForUser(cr, Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
732 0, mCurrentUser) == 1) {
733 // There are restrictions on the available color modes combined with a11y transforms.
734 if (isColorModeAvailable(COLOR_MODE_SATURATED)) {
735 return COLOR_MODE_SATURATED;
736 } else if (isColorModeAvailable(COLOR_MODE_AUTOMATIC)) {
737 return COLOR_MODE_AUTOMATIC;
738 }
739 }
740
741 int colorMode = System.getIntForUser(cr, System.DISPLAY_COLOR_MODE, -1, mCurrentUser);
742 if (colorMode == -1) {
743 // There might be a system property controlling color mode that we need to respect; if
744 // not, this will set a suitable default.
745 colorMode = getCurrentColorModeFromSystemProperties();
746 }
747
748 // This happens when a color mode is no longer available (e.g., after system update or B&R)
749 // or the device does not support any color mode.
750 if (!isColorModeAvailable(colorMode)) {
751 if (colorMode == COLOR_MODE_BOOSTED && isColorModeAvailable(COLOR_MODE_NATURAL)) {
752 colorMode = COLOR_MODE_NATURAL;
753 } else if (colorMode == COLOR_MODE_SATURATED
754 && isColorModeAvailable(COLOR_MODE_AUTOMATIC)) {
755 colorMode = COLOR_MODE_AUTOMATIC;
756 } else if (colorMode == COLOR_MODE_AUTOMATIC
757 && isColorModeAvailable(COLOR_MODE_SATURATED)) {
758 colorMode = COLOR_MODE_SATURATED;
759 } else {
760 colorMode = -1;
761 }
762 }
763
764 return colorMode;
765 }
766
767 /**
768 * Get the current color mode from system properties, or return -1 if invalid.
769 *
Christine Franks0ada2772019-02-25 13:54:57 -0800770 * See {@link DisplayTransformManager}
Christine Franksd154fe52019-01-04 17:17:45 -0800771 */
Christine Franks78a4dd42019-02-08 11:09:30 -0800772 private @ColorMode int getCurrentColorModeFromSystemProperties() {
Christine Franksd154fe52019-01-04 17:17:45 -0800773 final int displayColorSetting = SystemProperties.getInt("persist.sys.sf.native_mode", 0);
774 if (displayColorSetting == 0) {
775 return "1.0".equals(SystemProperties.get("persist.sys.sf.color_saturation"))
776 ? COLOR_MODE_NATURAL : COLOR_MODE_BOOSTED;
777 } else if (displayColorSetting == 1) {
778 return COLOR_MODE_SATURATED;
779 } else if (displayColorSetting == 2) {
780 return COLOR_MODE_AUTOMATIC;
781 } else {
782 return -1;
783 }
784 }
785
786 private boolean isColorModeAvailable(@ColorMode int colorMode) {
787 final int[] availableColorModes = getContext().getResources().getIntArray(
788 R.array.config_availableColorModes);
789 if (availableColorModes != null) {
790 for (int mode : availableColorModes) {
791 if (mode == colorMode) {
792 return true;
793 }
794 }
795 }
796 return false;
797 }
798
Christine Franksf3529b22019-01-03 13:20:17 -0800799 private void dumpInternal(PrintWriter pw) {
800 pw.println("COLOR DISPLAY MANAGER dumpsys (color_display)");
Christine Franksa4ed3762019-01-24 15:37:04 -0800801
Christine Franks0ada2772019-02-25 13:54:57 -0800802 pw.println("Night display:");
Christine Franksa4ed3762019-01-24 15:37:04 -0800803 if (mNightDisplayTintController.isAvailable(getContext())) {
Christine Franksf3529b22019-01-03 13:20:17 -0800804 pw.println(" Activated: " + mNightDisplayTintController.isActivated());
Christine Franksa4ed3762019-01-24 15:37:04 -0800805 pw.println(" Color temp: " + mNightDisplayTintController.getColorTemperature());
Christine Franksf3529b22019-01-03 13:20:17 -0800806 } else {
807 pw.println(" Not available");
808 }
Christine Franksa4ed3762019-01-24 15:37:04 -0800809
810 pw.println("Global saturation:");
811 if (mGlobalSaturationTintController.isAvailable(getContext())) {
812 pw.println(" Activated: " + mGlobalSaturationTintController.isActivated());
813 } else {
814 pw.println(" Not available");
815 }
816
Christine Franksf3529b22019-01-03 13:20:17 -0800817 mAppSaturationController.dump(pw);
Christine Franksa4ed3762019-01-24 15:37:04 -0800818
819 pw.println("Display white balance:");
820 if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) {
821 pw.println(" Activated: " + mDisplayWhiteBalanceTintController.isActivated());
Long Ling1d3f1892019-02-06 12:34:02 -0800822 mDisplayWhiteBalanceTintController.dump(pw);
Christine Franksa4ed3762019-01-24 15:37:04 -0800823 } else {
824 pw.println(" Not available");
825 }
826
827 pw.println("Color mode: " + getColorModeInternal());
Christine Franksf3529b22019-01-03 13:20:17 -0800828 }
829
Christine Franks57fdde82018-07-03 14:46:07 -0700830 private abstract class NightDisplayAutoMode {
831
832 public abstract void onActivated(boolean activated);
833
Justin Klaassen911e8892016-06-21 18:24:24 -0700834 public abstract void onStart();
Christine Frankse5bb03e2017-02-10 17:36:10 -0800835
Justin Klaassen911e8892016-06-21 18:24:24 -0700836 public abstract void onStop();
Christine Franks57fdde82018-07-03 14:46:07 -0700837
838 public void onCustomStartTimeChanged(LocalTime startTime) {
839 }
840
841 public void onCustomEndTimeChanged(LocalTime endTime) {
842 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700843 }
844
Christine Franks57fdde82018-07-03 14:46:07 -0700845 private final class CustomNightDisplayAutoMode extends NightDisplayAutoMode implements
846 AlarmManager.OnAlarmListener {
Justin Klaassen911e8892016-06-21 18:24:24 -0700847
848 private final AlarmManager mAlarmManager;
849 private final BroadcastReceiver mTimeChangedReceiver;
850
Christine Franks03213462017-08-25 13:57:26 -0700851 private LocalTime mStartTime;
852 private LocalTime mEndTime;
Justin Klaassen911e8892016-06-21 18:24:24 -0700853
Christine Franks03213462017-08-25 13:57:26 -0700854 private LocalDateTime mLastActivatedTime;
Justin Klaassen911e8892016-06-21 18:24:24 -0700855
Christine Franks57fdde82018-07-03 14:46:07 -0700856 CustomNightDisplayAutoMode() {
Justin Klaassen911e8892016-06-21 18:24:24 -0700857 mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
858 mTimeChangedReceiver = new BroadcastReceiver() {
859 @Override
860 public void onReceive(Context context, Intent intent) {
861 updateActivated();
862 }
863 };
864 }
865
866 private void updateActivated() {
Christine Franks03213462017-08-25 13:57:26 -0700867 final LocalDateTime now = LocalDateTime.now();
868 final LocalDateTime start = getDateTimeBefore(mStartTime, now);
869 final LocalDateTime end = getDateTimeAfter(mEndTime, start);
870 boolean activate = now.isBefore(end);
Justin Klaassen911e8892016-06-21 18:24:24 -0700871
Christine Frankse5bb03e2017-02-10 17:36:10 -0800872 if (mLastActivatedTime != null) {
Christine Frankse5bb03e2017-02-10 17:36:10 -0800873 // Maintain the existing activated state if within the current period.
Christine Franks0ada2772019-02-25 13:54:57 -0800874 if (mLastActivatedTime.isBefore(now)
875 && mLastActivatedTime.isAfter(start)
Christine Franks03213462017-08-25 13:57:26 -0700876 && (mLastActivatedTime.isAfter(end) || now.isBefore(end))) {
Christine Franks78a4dd42019-02-08 11:09:30 -0800877 activate = mNightDisplayTintController.isActivatedSetting();
Justin Klaassen911e8892016-06-21 18:24:24 -0700878 }
879 }
880
Christine Franks0ada2772019-02-25 13:54:57 -0800881 if (mNightDisplayTintController.isActivatedStateNotSet()
882 || (mNightDisplayTintController.isActivated() != activate)) {
Christine Franks83cc5412018-07-03 14:46:07 -0700883 mNightDisplayTintController.setActivated(activate);
Justin Klaassen911e8892016-06-21 18:24:24 -0700884 }
Christine Franks03213462017-08-25 13:57:26 -0700885
Christine Franks245ffd42018-11-16 13:45:14 -0800886 updateNextAlarm(mNightDisplayTintController.isActivated(), now);
Justin Klaassen911e8892016-06-21 18:24:24 -0700887 }
888
Christine Franks03213462017-08-25 13:57:26 -0700889 private void updateNextAlarm(@Nullable Boolean activated, @NonNull LocalDateTime now) {
Justin Klaassen4346f632016-08-08 15:01:47 -0700890 if (activated != null) {
Christine Franks03213462017-08-25 13:57:26 -0700891 final LocalDateTime next = activated ? getDateTimeAfter(mEndTime, now)
892 : getDateTimeAfter(mStartTime, now);
893 final long millis = next.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
894 mAlarmManager.setExact(AlarmManager.RTC, millis, TAG, this, null);
Justin Klaassen911e8892016-06-21 18:24:24 -0700895 }
896 }
897
898 @Override
899 public void onStart() {
900 final IntentFilter intentFilter = new IntentFilter(Intent.ACTION_TIME_CHANGED);
901 intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
902 getContext().registerReceiver(mTimeChangedReceiver, intentFilter);
903
Christine Franks83cc5412018-07-03 14:46:07 -0700904 mStartTime = getNightDisplayCustomStartTimeInternal().getLocalTime();
905 mEndTime = getNightDisplayCustomEndTimeInternal().getLocalTime();
Justin Klaassen911e8892016-06-21 18:24:24 -0700906
Christine Franks57fdde82018-07-03 14:46:07 -0700907 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
Christine Frankse5bb03e2017-02-10 17:36:10 -0800908
Justin Klaassen911e8892016-06-21 18:24:24 -0700909 // Force an update to initialize state.
910 updateActivated();
911 }
912
913 @Override
914 public void onStop() {
915 getContext().unregisterReceiver(mTimeChangedReceiver);
916
917 mAlarmManager.cancel(this);
918 mLastActivatedTime = null;
919 }
920
921 @Override
922 public void onActivated(boolean activated) {
Christine Franks57fdde82018-07-03 14:46:07 -0700923 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
Christine Franks03213462017-08-25 13:57:26 -0700924 updateNextAlarm(activated, LocalDateTime.now());
Justin Klaassen911e8892016-06-21 18:24:24 -0700925 }
926
927 @Override
Christine Franks03213462017-08-25 13:57:26 -0700928 public void onCustomStartTimeChanged(LocalTime startTime) {
Justin Klaassen911e8892016-06-21 18:24:24 -0700929 mStartTime = startTime;
930 mLastActivatedTime = null;
931 updateActivated();
932 }
933
934 @Override
Christine Franks03213462017-08-25 13:57:26 -0700935 public void onCustomEndTimeChanged(LocalTime endTime) {
Justin Klaassen911e8892016-06-21 18:24:24 -0700936 mEndTime = endTime;
937 mLastActivatedTime = null;
938 updateActivated();
939 }
940
941 @Override
942 public void onAlarm() {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700943 Slog.d(TAG, "onAlarm");
Justin Klaassen911e8892016-06-21 18:24:24 -0700944 updateActivated();
945 }
946 }
947
Christine Franks57fdde82018-07-03 14:46:07 -0700948 private final class TwilightNightDisplayAutoMode extends NightDisplayAutoMode implements
949 TwilightListener {
Justin Klaassen911e8892016-06-21 18:24:24 -0700950
951 private final TwilightManager mTwilightManager;
Christine Franks57fdde82018-07-03 14:46:07 -0700952 private LocalDateTime mLastActivatedTime;
Justin Klaassen911e8892016-06-21 18:24:24 -0700953
Christine Franks57fdde82018-07-03 14:46:07 -0700954 TwilightNightDisplayAutoMode() {
Justin Klaassen911e8892016-06-21 18:24:24 -0700955 mTwilightManager = getLocalService(TwilightManager.class);
Justin Klaassen911e8892016-06-21 18:24:24 -0700956 }
957
Justin Klaassen908b86c2016-08-08 09:18:42 -0700958 private void updateActivated(TwilightState state) {
Justin Klaassen3da4c442017-05-05 15:19:33 -0700959 if (state == null) {
960 // If there isn't a valid TwilightState then just keep the current activated
961 // state.
962 return;
963 }
964
965 boolean activate = state.isNight();
Christine Franks57fdde82018-07-03 14:46:07 -0700966 if (mLastActivatedTime != null) {
Christine Franks03213462017-08-25 13:57:26 -0700967 final LocalDateTime now = LocalDateTime.now();
968 final LocalDateTime sunrise = state.sunrise();
969 final LocalDateTime sunset = state.sunset();
Christine Frankse5bb03e2017-02-10 17:36:10 -0800970 // Maintain the existing activated state if within the current period.
Christine Franks57fdde82018-07-03 14:46:07 -0700971 if (mLastActivatedTime.isBefore(now) && (mLastActivatedTime.isBefore(sunrise)
972 ^ mLastActivatedTime.isBefore(sunset))) {
Christine Franks78a4dd42019-02-08 11:09:30 -0800973 activate = mNightDisplayTintController.isActivatedSetting();
Justin Klaassen911e8892016-06-21 18:24:24 -0700974 }
975 }
Justin Klaassen908b86c2016-08-08 09:18:42 -0700976
Christine Franks245ffd42018-11-16 13:45:14 -0800977 if (mNightDisplayTintController.isActivatedStateNotSet() || (
978 mNightDisplayTintController.isActivated() != activate)) {
Christine Franks83cc5412018-07-03 14:46:07 -0700979 mNightDisplayTintController.setActivated(activate);
Justin Klaassen908b86c2016-08-08 09:18:42 -0700980 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700981 }
982
983 @Override
Christine Franks57fdde82018-07-03 14:46:07 -0700984 public void onActivated(boolean activated) {
985 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
986 }
987
988 @Override
Justin Klaassen911e8892016-06-21 18:24:24 -0700989 public void onStart() {
990 mTwilightManager.registerListener(this, mHandler);
Christine Franks57fdde82018-07-03 14:46:07 -0700991 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
Justin Klaassen911e8892016-06-21 18:24:24 -0700992
993 // Force an update to initialize state.
Justin Klaassen908b86c2016-08-08 09:18:42 -0700994 updateActivated(mTwilightManager.getLastTwilightState());
Justin Klaassen911e8892016-06-21 18:24:24 -0700995 }
996
997 @Override
998 public void onStop() {
999 mTwilightManager.unregisterListener(this);
Christine Franks57fdde82018-07-03 14:46:07 -07001000 mLastActivatedTime = null;
Justin Klaassen908b86c2016-08-08 09:18:42 -07001001 }
1002
1003 @Override
1004 public void onTwilightStateChanged(@Nullable TwilightState state) {
Justin Klaassenec8837a2016-08-23 12:04:42 -07001005 Slog.d(TAG, "onTwilightStateChanged: isNight="
1006 + (state == null ? null : state.isNight()));
Justin Klaassen908b86c2016-08-08 09:18:42 -07001007 updateActivated(state);
Justin Klaassen911e8892016-06-21 18:24:24 -07001008 }
1009 }
Justin Klaassen639214e2016-07-14 21:00:06 -07001010
1011 /**
1012 * Interpolates between two 4x4 color transform matrices (in column-major order).
1013 */
1014 private static class ColorMatrixEvaluator implements TypeEvaluator<float[]> {
1015
1016 /**
1017 * Result matrix returned by {@link #evaluate(float, float[], float[])}.
1018 */
1019 private final float[] mResultMatrix = new float[16];
1020
1021 @Override
1022 public float[] evaluate(float fraction, float[] startValue, float[] endValue) {
1023 for (int i = 0; i < mResultMatrix.length; i++) {
1024 mResultMatrix[i] = MathUtils.lerp(startValue[i], endValue[i], fraction);
1025 }
1026 return mResultMatrix;
1027 }
1028 }
Christine Franks39b03112018-07-03 14:46:07 -07001029
Christine Franks83cc5412018-07-03 14:46:07 -07001030 private final class NightDisplayTintController extends TintController {
1031
Christine Franksa4ed3762019-01-24 15:37:04 -08001032 private final float[] mMatrix = new float[16];
Christine Franks83cc5412018-07-03 14:46:07 -07001033 private final float[] mColorTempCoefficients = new float[9];
Christine Franksa4ed3762019-01-24 15:37:04 -08001034
1035 private Boolean mIsAvailable;
Christine Franks83cc5412018-07-03 14:46:07 -07001036 private Integer mColorTemp;
1037
1038 /**
1039 * Set coefficients based on whether the color matrix is linear or not.
1040 */
1041 @Override
1042 public void setUp(Context context, boolean needsLinear) {
1043 final String[] coefficients = context.getResources().getStringArray(needsLinear
1044 ? R.array.config_nightDisplayColorTemperatureCoefficients
1045 : R.array.config_nightDisplayColorTemperatureCoefficientsNative);
1046 for (int i = 0; i < 9 && i < coefficients.length; i++) {
1047 mColorTempCoefficients[i] = Float.parseFloat(coefficients[i]);
1048 }
1049 }
1050
1051 @Override
1052 public void setMatrix(int cct) {
1053 if (mMatrix.length != 16) {
1054 Slog.d(TAG, "The display transformation matrix must be 4x4");
1055 return;
1056 }
1057
1058 Matrix.setIdentityM(mMatrix, 0);
1059
1060 final float squareTemperature = cct * cct;
1061 final float red = squareTemperature * mColorTempCoefficients[0]
1062 + cct * mColorTempCoefficients[1] + mColorTempCoefficients[2];
1063 final float green = squareTemperature * mColorTempCoefficients[3]
1064 + cct * mColorTempCoefficients[4] + mColorTempCoefficients[5];
1065 final float blue = squareTemperature * mColorTempCoefficients[6]
1066 + cct * mColorTempCoefficients[7] + mColorTempCoefficients[8];
1067 mMatrix[0] = red;
1068 mMatrix[5] = green;
1069 mMatrix[10] = blue;
1070 }
1071
1072 @Override
1073 public float[] getMatrix() {
1074 return isActivated() ? mMatrix : MATRIX_IDENTITY;
1075 }
1076
1077 @Override
1078 public void setActivated(Boolean activated) {
1079 if (activated == null) {
1080 super.setActivated(null);
1081 return;
1082 }
1083
1084 boolean activationStateChanged = activated != isActivated();
1085
1086 if (!isActivatedStateNotSet() && activationStateChanged) {
1087 // This is a true state change, so set this as the last activation time.
1088 Secure.putStringForUser(getContext().getContentResolver(),
1089 Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
1090 LocalDateTime.now().toString(),
1091 mCurrentUser);
1092 }
1093
1094 if (isActivatedStateNotSet() || activationStateChanged) {
1095 super.setActivated(activated);
Christine Franks78a4dd42019-02-08 11:09:30 -08001096 if (isActivatedSetting() != activated) {
1097 Secure.putIntForUser(getContext().getContentResolver(),
1098 Secure.NIGHT_DISPLAY_ACTIVATED,
1099 activated ? 1 : 0, mCurrentUser);
1100 }
Christine Franks83cc5412018-07-03 14:46:07 -07001101 onActivated(activated);
1102 }
1103 }
1104
1105 @Override
1106 public int getLevel() {
1107 return LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
1108 }
1109
Christine Franksa4ed3762019-01-24 15:37:04 -08001110 @Override
1111 public boolean isAvailable(Context context) {
1112 if (mIsAvailable == null) {
1113 mIsAvailable = ColorDisplayManager.isNightDisplayAvailable(context);
1114 }
1115 return mIsAvailable;
1116 }
1117
Christine Franks78a4dd42019-02-08 11:09:30 -08001118 private void onActivated(boolean activated) {
Christine Franks83cc5412018-07-03 14:46:07 -07001119 Slog.i(TAG, activated ? "Turning on night display" : "Turning off night display");
1120 if (mNightDisplayAutoMode != null) {
1121 mNightDisplayAutoMode.onActivated(activated);
1122 }
1123
Christine Franksa4ed3762019-01-24 15:37:04 -08001124 if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) {
Christine Franks83cc5412018-07-03 14:46:07 -07001125 updateDisplayWhiteBalanceStatus();
1126 }
1127
1128 mHandler.sendEmptyMessage(MSG_APPLY_NIGHT_DISPLAY_ANIMATED);
1129 }
1130
1131 int getColorTemperature() {
1132 return mColorTemp != null ? clampNightDisplayColorTemperature(mColorTemp)
Christine Franks78a4dd42019-02-08 11:09:30 -08001133 : getColorTemperatureSetting();
Christine Franks83cc5412018-07-03 14:46:07 -07001134 }
1135
1136 boolean setColorTemperature(int temperature) {
1137 mColorTemp = temperature;
1138 final boolean success = Secure.putIntForUser(getContext().getContentResolver(),
1139 Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, temperature, mCurrentUser);
1140 onColorTemperatureChanged(temperature);
1141 return success;
1142 }
1143
1144 void onColorTemperatureChanged(int temperature) {
1145 setMatrix(temperature);
1146 mHandler.sendEmptyMessage(MSG_APPLY_NIGHT_DISPLAY_IMMEDIATE);
1147 }
Christine Franks78a4dd42019-02-08 11:09:30 -08001148
1149 boolean isActivatedSetting() {
Christine Franks44782612019-03-07 17:25:39 -08001150 if (mCurrentUser == UserHandle.USER_NULL) {
1151 return false;
1152 }
Christine Franks78a4dd42019-02-08 11:09:30 -08001153 return Secure.getIntForUser(getContext().getContentResolver(),
1154 Secure.NIGHT_DISPLAY_ACTIVATED, 0, mCurrentUser) == 1;
1155 }
1156
1157 int getColorTemperatureSetting() {
Christine Franks44782612019-03-07 17:25:39 -08001158 if (mCurrentUser == UserHandle.USER_NULL) {
1159 return NOT_SET;
1160 }
Christine Franks78a4dd42019-02-08 11:09:30 -08001161 return clampNightDisplayColorTemperature(Secure.getIntForUser(
1162 getContext().getContentResolver(), Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE,
1163 NOT_SET,
1164 mCurrentUser));
1165 }
1166
1167 private int clampNightDisplayColorTemperature(int colorTemperature) {
1168 if (colorTemperature == NOT_SET) {
1169 colorTemperature = getContext().getResources().getInteger(
1170 R.integer.config_nightDisplayColorTemperatureDefault);
1171 }
1172 final int minimumTemperature = ColorDisplayManager
1173 .getMinimumColorTemperature(getContext());
1174 final int maximumTemperature = ColorDisplayManager
1175 .getMaximumColorTemperature(getContext());
1176 if (colorTemperature < minimumTemperature) {
1177 colorTemperature = minimumTemperature;
1178 } else if (colorTemperature > maximumTemperature) {
1179 colorTemperature = maximumTemperature;
1180 }
1181
1182 return colorTemperature;
1183 }
Christine Franks83cc5412018-07-03 14:46:07 -07001184 }
1185
Christine Franks245ffd42018-11-16 13:45:14 -08001186 /**
1187 * Local service that allows color transforms to be enabled from other system services.
1188 */
1189 public final class ColorDisplayServiceInternal {
1190
1191 /**
1192 * Set the current CCT value for the display white balance transform, and if the transform
1193 * is enabled, apply it.
1194 *
1195 * @param cct the color temperature in Kelvin.
1196 */
1197 public boolean setDisplayWhiteBalanceColorTemperature(int cct) {
1198 // Update the transform matrix even if it can't be applied.
Christine Franks245ffd42018-11-16 13:45:14 -08001199 mDisplayWhiteBalanceTintController.setMatrix(cct);
1200
1201 if (mDisplayWhiteBalanceTintController.isActivated()) {
Christine Franksc7fb9452019-02-04 08:45:33 -08001202 mHandler.sendEmptyMessage(MSG_APPLY_DISPLAY_WHITE_BALANCE);
Christine Franks245ffd42018-11-16 13:45:14 -08001203 return true;
1204 }
1205 return false;
1206 }
1207
1208 /**
1209 * Sets the listener and returns whether display white balance is currently enabled.
1210 */
1211 public boolean setDisplayWhiteBalanceListener(DisplayWhiteBalanceListener listener) {
1212 mDisplayWhiteBalanceListener = listener;
1213 return mDisplayWhiteBalanceTintController.isActivated();
1214 }
Daniel Solomon8b72c5b2018-11-25 11:07:13 -08001215
Christine Franksf3529b22019-01-03 13:20:17 -08001216 /**
1217 * Adds a {@link WeakReference<ColorTransformController>} for a newly started activity, and
1218 * invokes {@link ColorTransformController#applyAppSaturation(float[], float[])} if needed.
1219 */
Christine Franks55194dc2019-01-15 13:47:06 -08001220 public boolean attachColorTransformController(String packageName, @UserIdInt int userId,
Christine Franksf3529b22019-01-03 13:20:17 -08001221 WeakReference<ColorTransformController> controller) {
1222 return mAppSaturationController
Christine Franks55194dc2019-01-15 13:47:06 -08001223 .addColorTransformController(packageName, userId, controller);
Christine Franksf3529b22019-01-03 13:20:17 -08001224 }
Christine Franks245ffd42018-11-16 13:45:14 -08001225 }
1226
1227 /**
1228 * Listener for changes in display white balance status.
1229 */
1230 public interface DisplayWhiteBalanceListener {
1231
1232 /**
1233 * Notify that the display white balance status has changed, either due to preemption by
1234 * another transform or the feature being turned off.
1235 */
1236 void onDisplayWhiteBalanceStatusChanged(boolean enabled);
1237 }
1238
Christine Franks09c229e2018-12-14 10:37:40 -08001239 private final class TintHandler extends Handler {
1240
Christine Franks83cc5412018-07-03 14:46:07 -07001241 private TintHandler(Looper looper) {
Christine Franks09c229e2018-12-14 10:37:40 -08001242 super(looper, null, true /* async */);
1243 }
1244
1245 @Override
1246 public void handleMessage(Message msg) {
1247 switch (msg.what) {
1248 case MSG_APPLY_GLOBAL_SATURATION:
1249 mGlobalSaturationTintController.setMatrix(msg.arg1);
1250 applyTint(mGlobalSaturationTintController, false);
1251 break;
Christine Franks83cc5412018-07-03 14:46:07 -07001252 case MSG_APPLY_NIGHT_DISPLAY_IMMEDIATE:
1253 applyTint(mNightDisplayTintController, true);
1254 break;
1255 case MSG_APPLY_NIGHT_DISPLAY_ANIMATED:
1256 applyTint(mNightDisplayTintController, false);
1257 break;
Christine Franksc7fb9452019-02-04 08:45:33 -08001258 case MSG_APPLY_DISPLAY_WHITE_BALANCE:
1259 applyTint(mDisplayWhiteBalanceTintController, false);
1260 break;
Christine Franks09c229e2018-12-14 10:37:40 -08001261 }
1262 }
1263 }
1264
Christine Franksf3529b22019-01-03 13:20:17 -08001265 /**
1266 * Interface for applying transforms to a given AppWindow.
1267 */
1268 public interface ColorTransformController {
1269
Christine Franksd154fe52019-01-04 17:17:45 -08001270 /**
1271 * Apply the given saturation (grayscale) matrix to the associated AppWindow.
1272 */
Christine Franksf3529b22019-01-03 13:20:17 -08001273 void applyAppSaturation(@Size(9) float[] matrix, @Size(3) float[] translation);
1274 }
1275
Christine Franks83cc5412018-07-03 14:46:07 -07001276 @VisibleForTesting
1277 final class BinderService extends IColorDisplayManager.Stub {
Christine Franks57fdde82018-07-03 14:46:07 -07001278
Christine Franks39b03112018-07-03 14:46:07 -07001279 @Override
Christine Franksd154fe52019-01-04 17:17:45 -08001280 public void setColorMode(int colorMode) {
1281 getContext().enforceCallingOrSelfPermission(
1282 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1283 "Permission required to set display color mode");
1284 final long token = Binder.clearCallingIdentity();
1285 try {
1286 setColorModeInternal(colorMode);
1287 } finally {
1288 Binder.restoreCallingIdentity(token);
1289 }
1290 }
1291
1292 @Override
1293 public int getColorMode() {
1294 final long token = Binder.clearCallingIdentity();
1295 try {
1296 return getColorModeInternal();
1297 } finally {
1298 Binder.restoreCallingIdentity(token);
1299 }
1300 }
1301
1302 @Override
Christine Franks39b03112018-07-03 14:46:07 -07001303 public boolean isDeviceColorManaged() {
1304 final long token = Binder.clearCallingIdentity();
1305 try {
1306 return isDeviceColorManagedInternal();
1307 } finally {
1308 Binder.restoreCallingIdentity(token);
1309 }
1310 }
Christine Franks09c229e2018-12-14 10:37:40 -08001311
1312 @Override
1313 public boolean setSaturationLevel(int level) {
1314 final boolean hasTransformsPermission = getContext()
1315 .checkCallingPermission(Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
1316 == PackageManager.PERMISSION_GRANTED;
1317 final boolean hasLegacyPermission = getContext()
1318 .checkCallingPermission(Manifest.permission.CONTROL_DISPLAY_SATURATION)
1319 == PackageManager.PERMISSION_GRANTED;
1320 if (!hasTransformsPermission && !hasLegacyPermission) {
1321 throw new SecurityException("Permission required to set display saturation level");
1322 }
1323 final long token = Binder.clearCallingIdentity();
1324 try {
1325 final Message message = mHandler.obtainMessage(MSG_APPLY_GLOBAL_SATURATION);
1326 message.arg1 = level;
1327 mHandler.sendMessage(message);
1328 } finally {
1329 Binder.restoreCallingIdentity(token);
1330 }
1331 return true;
1332 }
Christine Franksf3529b22019-01-03 13:20:17 -08001333
1334 @Override
Christine Franks6d21d342019-02-07 15:09:03 -08001335 public boolean isSaturationActivated() {
1336 getContext().enforceCallingPermission(
1337 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1338 "Permission required to get display saturation level");
1339 final long token = Binder.clearCallingIdentity();
1340 try {
1341 return !mGlobalSaturationTintController.isActivatedStateNotSet()
1342 && mGlobalSaturationTintController.isActivated();
1343 } finally {
1344 Binder.restoreCallingIdentity(token);
1345 }
1346 }
1347
1348 @Override
Christine Franksf3529b22019-01-03 13:20:17 -08001349 public boolean setAppSaturationLevel(String packageName, int level) {
1350 getContext().enforceCallingPermission(
1351 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1352 "Permission required to set display saturation level");
1353 final long token = Binder.clearCallingIdentity();
1354 try {
1355 return setAppSaturationLevelInternal(packageName, level);
1356 } finally {
1357 Binder.restoreCallingIdentity(token);
1358 }
1359 }
1360
Christine Franks55194dc2019-01-15 13:47:06 -08001361 public int getTransformCapabilities() {
1362 getContext().enforceCallingPermission(
1363 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1364 "Permission required to query transform capabilities");
1365 final long token = Binder.clearCallingIdentity();
1366 try {
1367 return getTransformCapabilitiesInternal();
1368 } finally {
1369 Binder.restoreCallingIdentity(token);
1370 }
1371 }
1372
Christine Franksf3529b22019-01-03 13:20:17 -08001373 @Override
Christine Franks83cc5412018-07-03 14:46:07 -07001374 public boolean setNightDisplayActivated(boolean activated) {
1375 getContext().enforceCallingOrSelfPermission(
1376 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1377 "Permission required to set night display activated");
1378 final long token = Binder.clearCallingIdentity();
1379 try {
1380 mNightDisplayTintController.setActivated(activated);
1381 return true;
1382 } finally {
1383 Binder.restoreCallingIdentity(token);
1384 }
1385 }
1386
1387 @Override
1388 public boolean isNightDisplayActivated() {
1389 final long token = Binder.clearCallingIdentity();
1390 try {
1391 return mNightDisplayTintController.isActivated();
1392 } finally {
1393 Binder.restoreCallingIdentity(token);
1394 }
1395 }
1396
1397 @Override
1398 public boolean setNightDisplayColorTemperature(int temperature) {
1399 getContext().enforceCallingOrSelfPermission(
1400 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1401 "Permission required to set night display temperature");
1402 final long token = Binder.clearCallingIdentity();
1403 try {
1404 return mNightDisplayTintController.setColorTemperature(temperature);
1405 } finally {
1406 Binder.restoreCallingIdentity(token);
1407 }
1408 }
1409
1410 @Override
1411 public int getNightDisplayColorTemperature() {
1412 final long token = Binder.clearCallingIdentity();
1413 try {
1414 return mNightDisplayTintController.getColorTemperature();
1415 } finally {
1416 Binder.restoreCallingIdentity(token);
1417 }
1418 }
1419
1420 @Override
1421 public boolean setNightDisplayAutoMode(int autoMode) {
1422 getContext().enforceCallingOrSelfPermission(
1423 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1424 "Permission required to set night display auto mode");
1425 final long token = Binder.clearCallingIdentity();
1426 try {
1427 return setNightDisplayAutoModeInternal(autoMode);
1428 } finally {
1429 Binder.restoreCallingIdentity(token);
1430 }
1431 }
1432
1433 @Override
1434 public int getNightDisplayAutoMode() {
1435 getContext().enforceCallingOrSelfPermission(
1436 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1437 "Permission required to get night display auto mode");
1438 final long token = Binder.clearCallingIdentity();
1439 try {
1440 return getNightDisplayAutoModeInternal();
1441 } finally {
1442 Binder.restoreCallingIdentity(token);
1443 }
1444 }
1445
1446 @Override
1447 public int getNightDisplayAutoModeRaw() {
1448 final long token = Binder.clearCallingIdentity();
1449 try {
1450 return getNightDisplayAutoModeRawInternal();
1451 } finally {
1452 Binder.restoreCallingIdentity(token);
1453 }
1454 }
1455
1456 @Override
1457 public boolean setNightDisplayCustomStartTime(Time startTime) {
1458 getContext().enforceCallingOrSelfPermission(
1459 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1460 "Permission required to set night display custom start time");
1461 final long token = Binder.clearCallingIdentity();
1462 try {
1463 return setNightDisplayCustomStartTimeInternal(startTime);
1464 } finally {
1465 Binder.restoreCallingIdentity(token);
1466 }
1467 }
1468
1469 @Override
1470 public Time getNightDisplayCustomStartTime() {
1471 final long token = Binder.clearCallingIdentity();
1472 try {
1473 return getNightDisplayCustomStartTimeInternal();
1474 } finally {
1475 Binder.restoreCallingIdentity(token);
1476 }
1477 }
1478
1479 @Override
1480 public boolean setNightDisplayCustomEndTime(Time endTime) {
1481 getContext().enforceCallingOrSelfPermission(
1482 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1483 "Permission required to set night display custom end time");
1484 final long token = Binder.clearCallingIdentity();
1485 try {
1486 return setNightDisplayCustomEndTimeInternal(endTime);
1487 } finally {
1488 Binder.restoreCallingIdentity(token);
1489 }
1490 }
1491
1492 @Override
1493 public Time getNightDisplayCustomEndTime() {
1494 final long token = Binder.clearCallingIdentity();
1495 try {
1496 return getNightDisplayCustomEndTimeInternal();
1497 } finally {
1498 Binder.restoreCallingIdentity(token);
1499 }
1500 }
1501
1502 @Override
Christine Franksf3529b22019-01-03 13:20:17 -08001503 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Christine Franksd154fe52019-01-04 17:17:45 -08001504 if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
1505 return;
1506 }
Christine Franksf3529b22019-01-03 13:20:17 -08001507
1508 final long token = Binder.clearCallingIdentity();
1509 try {
1510 dumpInternal(pw);
1511 } finally {
1512 Binder.restoreCallingIdentity(token);
1513 }
1514 }
Christine Franks39b03112018-07-03 14:46:07 -07001515 }
Justin Klaassen911e8892016-06-21 18:24:24 -07001516}