blob: ef92401bf3fd75b78b0da4d6a87f03e5b6d0759f [file] [log] [blame]
Justin Klaassen22eb1992016-07-11 20:52:23 -07001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.display;
18
Wale Ogunwale04d9cb52018-04-30 13:55:07 -070019import android.app.ActivityTaskManager;
Christine Franksd154fe52019-01-04 17:17:45 -080020import android.hardware.display.ColorDisplayManager;
Justin Klaassen22eb1992016-07-11 20:52:23 -070021import android.opengl.Matrix;
22import android.os.IBinder;
23import android.os.Parcel;
24import android.os.RemoteException;
25import android.os.ServiceManager;
Christine Franks8ad71492017-10-24 19:04:22 -070026import android.os.SystemProperties;
Justin Klaassen22eb1992016-07-11 20:52:23 -070027import android.util.Slog;
28import android.util.SparseArray;
Christine Franks39b03112018-07-03 14:46:07 -070029
Justin Klaassen639214e2016-07-14 21:00:06 -070030import com.android.internal.annotations.GuardedBy;
Christine Franks39b03112018-07-03 14:46:07 -070031
Justin Klaassen639214e2016-07-14 21:00:06 -070032import java.util.Arrays;
33
Justin Klaassen22eb1992016-07-11 20:52:23 -070034/**
35 * Manager for applying color transformations to the display.
36 */
37public class DisplayTransformManager {
38
39 private static final String TAG = "DisplayTransformManager";
40
Christine Franks8ad71492017-10-24 19:04:22 -070041 private static final String SURFACE_FLINGER = "SurfaceFlinger";
42
Justin Klaassen22eb1992016-07-11 20:52:23 -070043 /**
44 * Color transform level used by Night display to tint the display red.
45 */
46 public static final int LEVEL_COLOR_MATRIX_NIGHT_DISPLAY = 100;
47 /**
Christine Franks245ffd42018-11-16 13:45:14 -080048 * Color transform level used by display white balance to adjust the display's white point.
49 */
50 public static final int LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE = 125;
51 /**
Bryan Mawhinney462e29d2018-03-22 15:52:41 +000052 * Color transform level used to adjust the color saturation of the display.
53 */
54 public static final int LEVEL_COLOR_MATRIX_SATURATION = 150;
55 /**
Justin Klaassen22eb1992016-07-11 20:52:23 -070056 * Color transform level used by A11y services to make the display monochromatic.
57 */
58 public static final int LEVEL_COLOR_MATRIX_GRAYSCALE = 200;
59 /**
60 * Color transform level used by A11y services to invert the display colors.
61 */
62 public static final int LEVEL_COLOR_MATRIX_INVERT_COLOR = 300;
63
Romain Guy26a2b972017-04-17 09:39:51 -070064 private static final int SURFACE_FLINGER_TRANSACTION_COLOR_MATRIX = 1015;
65 private static final int SURFACE_FLINGER_TRANSACTION_DALTONIZER = 1014;
Chia-I Wu1e0e7172018-03-14 15:45:29 -070066 /**
67 * SurfaceFlinger global saturation factor.
68 */
Christine Franks8ad71492017-10-24 19:04:22 -070069 private static final int SURFACE_FLINGER_TRANSACTION_SATURATION = 1022;
Chia-I Wu1e0e7172018-03-14 15:45:29 -070070 /**
71 * SurfaceFlinger display color (managed, unmanaged, etc.).
72 */
73 private static final int SURFACE_FLINGER_TRANSACTION_DISPLAY_COLOR = 1023;
Christine Franks87e681b2018-11-29 15:03:42 -080074 private static final int SURFACE_FLINGER_TRANSACTION_QUERY_COLOR_MANAGED = 1030;
Christine Franks39b03112018-07-03 14:46:07 -070075
76 private static final String PERSISTENT_PROPERTY_SATURATION = "persist.sys.sf.color_saturation";
77 private static final String PERSISTENT_PROPERTY_DISPLAY_COLOR = "persist.sys.sf.native_mode";
Christine Franks8ad71492017-10-24 19:04:22 -070078
79 private static final float COLOR_SATURATION_NATURAL = 1.0f;
80 private static final float COLOR_SATURATION_BOOSTED = 1.1f;
81
Chia-I Wu1e0e7172018-03-14 15:45:29 -070082 private static final int DISPLAY_COLOR_MANAGED = 0;
83 private static final int DISPLAY_COLOR_UNMANAGED = 1;
84 private static final int DISPLAY_COLOR_ENHANCED = 2;
85
Justin Klaassen639214e2016-07-14 21:00:06 -070086 /**
87 * Map of level -> color transformation matrix.
88 */
89 @GuardedBy("mColorMatrix")
Christine Franksc7fb9452019-02-04 08:45:33 -080090 private final SparseArray<float[]> mColorMatrix = new SparseArray<>(5);
Justin Klaassen639214e2016-07-14 21:00:06 -070091 /**
92 * Temporary matrix used internally by {@link #computeColorMatrixLocked()}.
93 */
94 @GuardedBy("mColorMatrix")
95 private final float[][] mTempColorMatrix = new float[2][16];
Justin Klaassen22eb1992016-07-11 20:52:23 -070096
Justin Klaassen639214e2016-07-14 21:00:06 -070097 /**
98 * Lock used for synchronize access to {@link #mDaltonizerMode}.
99 */
100 private final Object mDaltonizerModeLock = new Object();
101 @GuardedBy("mDaltonizerModeLock")
Justin Klaassen22eb1992016-07-11 20:52:23 -0700102 private int mDaltonizerMode = -1;
103
104 /* package */ DisplayTransformManager() {
105 }
106
107 /**
Justin Klaassen639214e2016-07-14 21:00:06 -0700108 * Returns a copy of the color transform matrix set for a given level.
Justin Klaassen22eb1992016-07-11 20:52:23 -0700109 */
110 public float[] getColorMatrix(int key) {
111 synchronized (mColorMatrix) {
Justin Klaassen639214e2016-07-14 21:00:06 -0700112 final float[] value = mColorMatrix.get(key);
113 return value == null ? null : Arrays.copyOf(value, value.length);
Justin Klaassen22eb1992016-07-11 20:52:23 -0700114 }
115 }
116
117 /**
118 * Sets and applies a current color transform matrix for a given level.
119 * <p>
120 * Note: all color transforms are first composed to a single matrix in ascending order based
121 * on level before being applied to the display.
122 *
Justin Klaassen639214e2016-07-14 21:00:06 -0700123 * @param level the level used to identify and compose the color transform (low -> high)
Justin Klaassen22eb1992016-07-11 20:52:23 -0700124 * @param value the 4x4 color transform matrix (in column-major order), or {@code null} to
125 * remove the color transform matrix associated with the provided level
126 */
Justin Klaassen639214e2016-07-14 21:00:06 -0700127 public void setColorMatrix(int level, float[] value) {
Justin Klaassen22eb1992016-07-11 20:52:23 -0700128 if (value != null && value.length != 16) {
129 throw new IllegalArgumentException("Expected length: 16 (4x4 matrix)"
130 + ", actual length: " + value.length);
131 }
132
133 synchronized (mColorMatrix) {
Justin Klaassen639214e2016-07-14 21:00:06 -0700134 final float[] oldValue = mColorMatrix.get(level);
135 if (!Arrays.equals(oldValue, value)) {
136 if (value == null) {
137 mColorMatrix.remove(level);
138 } else if (oldValue == null) {
139 mColorMatrix.put(level, Arrays.copyOf(value, value.length));
140 } else {
141 System.arraycopy(value, 0, oldValue, 0, value.length);
142 }
Justin Klaassen22eb1992016-07-11 20:52:23 -0700143
Justin Klaassen639214e2016-07-14 21:00:06 -0700144 // Update the current color transform.
145 applyColorMatrix(computeColorMatrixLocked());
146 }
Justin Klaassen22eb1992016-07-11 20:52:23 -0700147 }
148 }
149
150 /**
Christine Franksc7fb9452019-02-04 08:45:33 -0800151 * Sets the current Daltonization mode. This adjusts the color space to correct for or simulate
152 * various types of color blindness.
153 *
154 * @param mode the new Daltonization mode, or -1 to disable
155 */
156 public void setDaltonizerMode(int mode) {
157 synchronized (mDaltonizerModeLock) {
158 if (mDaltonizerMode != mode) {
159 mDaltonizerMode = mode;
160 applyDaltonizerMode(mode);
161 }
162 }
163 }
164
165 /**
Justin Klaassen22eb1992016-07-11 20:52:23 -0700166 * Returns the composition of all current color matrices, or {@code null} if there are none.
167 */
Justin Klaassen639214e2016-07-14 21:00:06 -0700168 @GuardedBy("mColorMatrix")
169 private float[] computeColorMatrixLocked() {
170 final int count = mColorMatrix.size();
171 if (count == 0) {
172 return null;
Justin Klaassen22eb1992016-07-11 20:52:23 -0700173 }
Justin Klaassen639214e2016-07-14 21:00:06 -0700174
175 final float[][] result = mTempColorMatrix;
176 Matrix.setIdentityM(result[0], 0);
177 for (int i = 0; i < count; i++) {
178 float[] rhs = mColorMatrix.valueAt(i);
179 Matrix.multiplyMM(result[(i + 1) % 2], 0, result[i % 2], 0, rhs, 0);
180 }
181 return result[count % 2];
Justin Klaassen22eb1992016-07-11 20:52:23 -0700182 }
183
184 /**
Justin Klaassen22eb1992016-07-11 20:52:23 -0700185 * Propagates the provided color transformation matrix to the SurfaceFlinger.
186 */
187 private static void applyColorMatrix(float[] m) {
Christine Franks8ad71492017-10-24 19:04:22 -0700188 final IBinder flinger = ServiceManager.getService(SURFACE_FLINGER);
Justin Klaassen22eb1992016-07-11 20:52:23 -0700189 if (flinger != null) {
190 final Parcel data = Parcel.obtain();
191 data.writeInterfaceToken("android.ui.ISurfaceComposer");
192 if (m != null) {
193 data.writeInt(1);
194 for (int i = 0; i < 16; i++) {
195 data.writeFloat(m[i]);
196 }
197 } else {
198 data.writeInt(0);
199 }
200 try {
Romain Guy26a2b972017-04-17 09:39:51 -0700201 flinger.transact(SURFACE_FLINGER_TRANSACTION_COLOR_MATRIX, data, null, 0);
Justin Klaassen22eb1992016-07-11 20:52:23 -0700202 } catch (RemoteException ex) {
203 Slog.e(TAG, "Failed to set color transform", ex);
204 } finally {
205 data.recycle();
206 }
207 }
208 }
209
210 /**
211 * Propagates the provided Daltonization mode to the SurfaceFlinger.
212 */
213 private static void applyDaltonizerMode(int mode) {
Christine Franks8ad71492017-10-24 19:04:22 -0700214 final IBinder flinger = ServiceManager.getService(SURFACE_FLINGER);
Justin Klaassen22eb1992016-07-11 20:52:23 -0700215 if (flinger != null) {
216 final Parcel data = Parcel.obtain();
217 data.writeInterfaceToken("android.ui.ISurfaceComposer");
218 data.writeInt(mode);
219 try {
Romain Guy26a2b972017-04-17 09:39:51 -0700220 flinger.transact(SURFACE_FLINGER_TRANSACTION_DALTONIZER, data, null, 0);
Justin Klaassen22eb1992016-07-11 20:52:23 -0700221 } catch (RemoteException ex) {
222 Slog.e(TAG, "Failed to set Daltonizer mode", ex);
223 } finally {
224 data.recycle();
225 }
226 }
227 }
Christine Franks8ad71492017-10-24 19:04:22 -0700228
Chia-I Wu1e0e7172018-03-14 15:45:29 -0700229 /**
Chia-I Wu9ec9ea72018-05-07 13:17:04 -0700230 * Return true when the color matrix works in linear space.
Chia-I Wu1e0e7172018-03-14 15:45:29 -0700231 */
Chia-I Wu9ec9ea72018-05-07 13:17:04 -0700232 public static boolean needsLinearColorMatrix() {
Chia-I Wu1e0e7172018-03-14 15:45:29 -0700233 return SystemProperties.getInt(PERSISTENT_PROPERTY_DISPLAY_COLOR,
Chia-I Wu9ec9ea72018-05-07 13:17:04 -0700234 DISPLAY_COLOR_UNMANAGED) != DISPLAY_COLOR_UNMANAGED;
Chia-I Wu1e0e7172018-03-14 15:45:29 -0700235 }
236
237 /**
Chia-I Wu9ec9ea72018-05-07 13:17:04 -0700238 * Return true when the specified colorMode requires the color matrix to
239 * work in linear space.
Chia-I Wu1e0e7172018-03-14 15:45:29 -0700240 */
Chia-I Wu9ec9ea72018-05-07 13:17:04 -0700241 public static boolean needsLinearColorMatrix(int colorMode) {
Christine Franksd154fe52019-01-04 17:17:45 -0800242 return colorMode != ColorDisplayManager.COLOR_MODE_SATURATED;
Christine Franks8ad71492017-10-24 19:04:22 -0700243 }
244
Christine Franks218e6562017-11-27 10:20:14 -0800245 public boolean setColorMode(int colorMode, float[] nightDisplayMatrix) {
Christine Franksd154fe52019-01-04 17:17:45 -0800246 if (colorMode == ColorDisplayManager.COLOR_MODE_NATURAL) {
Christine Franks8ad71492017-10-24 19:04:22 -0700247 applySaturation(COLOR_SATURATION_NATURAL);
Chia-I Wu1e0e7172018-03-14 15:45:29 -0700248 setDisplayColor(DISPLAY_COLOR_MANAGED);
Christine Franksd154fe52019-01-04 17:17:45 -0800249 } else if (colorMode == ColorDisplayManager.COLOR_MODE_BOOSTED) {
Christine Franks8ad71492017-10-24 19:04:22 -0700250 applySaturation(COLOR_SATURATION_BOOSTED);
Chia-I Wu1e0e7172018-03-14 15:45:29 -0700251 setDisplayColor(DISPLAY_COLOR_MANAGED);
Christine Franksd154fe52019-01-04 17:17:45 -0800252 } else if (colorMode == ColorDisplayManager.COLOR_MODE_SATURATED) {
Christine Franks8ad71492017-10-24 19:04:22 -0700253 applySaturation(COLOR_SATURATION_NATURAL);
Chia-I Wu1e0e7172018-03-14 15:45:29 -0700254 setDisplayColor(DISPLAY_COLOR_UNMANAGED);
Christine Franksd154fe52019-01-04 17:17:45 -0800255 } else if (colorMode == ColorDisplayManager.COLOR_MODE_AUTOMATIC) {
Chia-I Wu1e0e7172018-03-14 15:45:29 -0700256 applySaturation(COLOR_SATURATION_NATURAL);
257 setDisplayColor(DISPLAY_COLOR_ENHANCED);
Christine Franks8ad71492017-10-24 19:04:22 -0700258 }
Christine Franks218e6562017-11-27 10:20:14 -0800259 setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, nightDisplayMatrix);
Christine Franks8ad71492017-10-24 19:04:22 -0700260
261 updateConfiguration();
262
263 return true;
264 }
265
266 /**
Christine Franks87e681b2018-11-29 15:03:42 -0800267 * Returns whether the screen is color managed via SurfaceFlinger's
268 * {@link #SURFACE_FLINGER_TRANSACTION_QUERY_COLOR_MANAGED}.
Christine Franks39b03112018-07-03 14:46:07 -0700269 */
270 public boolean isDeviceColorManaged() {
271 final IBinder flinger = ServiceManager.getService(SURFACE_FLINGER);
272 if (flinger != null) {
273 final Parcel data = Parcel.obtain();
274 final Parcel reply = Parcel.obtain();
275 data.writeInterfaceToken("android.ui.ISurfaceComposer");
276 try {
Christine Franks87e681b2018-11-29 15:03:42 -0800277 flinger.transact(SURFACE_FLINGER_TRANSACTION_QUERY_COLOR_MANAGED, data, reply, 0);
Christine Franks39b03112018-07-03 14:46:07 -0700278 return reply.readBoolean();
279 } catch (RemoteException ex) {
Christine Franks87e681b2018-11-29 15:03:42 -0800280 Slog.e(TAG, "Failed to query wide color support", ex);
Christine Franks39b03112018-07-03 14:46:07 -0700281 } finally {
282 data.recycle();
283 reply.recycle();
284 }
285 }
286 return false;
287 }
288
289 /**
Christine Franks8ad71492017-10-24 19:04:22 -0700290 * Propagates the provided saturation to the SurfaceFlinger.
291 */
292 private void applySaturation(float saturation) {
293 SystemProperties.set(PERSISTENT_PROPERTY_SATURATION, Float.toString(saturation));
294 final IBinder flinger = ServiceManager.getService(SURFACE_FLINGER);
295 if (flinger != null) {
296 final Parcel data = Parcel.obtain();
297 data.writeInterfaceToken("android.ui.ISurfaceComposer");
298 data.writeFloat(saturation);
299 try {
300 flinger.transact(SURFACE_FLINGER_TRANSACTION_SATURATION, data, null, 0);
301 } catch (RemoteException ex) {
Christine Franks87e681b2018-11-29 15:03:42 -0800302 Slog.e(TAG, "Failed to set saturation", ex);
Christine Franks8ad71492017-10-24 19:04:22 -0700303 } finally {
304 data.recycle();
305 }
306 }
307 }
308
309 /**
310 * Toggles native mode on/off in SurfaceFlinger.
311 */
Chia-I Wu1e0e7172018-03-14 15:45:29 -0700312 private void setDisplayColor(int color) {
313 SystemProperties.set(PERSISTENT_PROPERTY_DISPLAY_COLOR, Integer.toString(color));
Christine Franks8ad71492017-10-24 19:04:22 -0700314 final IBinder flinger = ServiceManager.getService(SURFACE_FLINGER);
315 if (flinger != null) {
316 final Parcel data = Parcel.obtain();
317 data.writeInterfaceToken("android.ui.ISurfaceComposer");
Chia-I Wu1e0e7172018-03-14 15:45:29 -0700318 data.writeInt(color);
Christine Franks8ad71492017-10-24 19:04:22 -0700319 try {
Chia-I Wu1e0e7172018-03-14 15:45:29 -0700320 flinger.transact(SURFACE_FLINGER_TRANSACTION_DISPLAY_COLOR, data, null, 0);
Christine Franks8ad71492017-10-24 19:04:22 -0700321 } catch (RemoteException ex) {
Christine Franks87e681b2018-11-29 15:03:42 -0800322 Slog.e(TAG, "Failed to set display color", ex);
Christine Franks8ad71492017-10-24 19:04:22 -0700323 } finally {
324 data.recycle();
325 }
326 }
327 }
328
329 private void updateConfiguration() {
330 try {
Wale Ogunwale04d9cb52018-04-30 13:55:07 -0700331 ActivityTaskManager.getService().updateConfiguration(null);
Christine Franks8ad71492017-10-24 19:04:22 -0700332 } catch (RemoteException e) {
Christine Franks87e681b2018-11-29 15:03:42 -0800333 Slog.e(TAG, "Could not update configuration", e);
Christine Franks8ad71492017-10-24 19:04:22 -0700334 }
335 }
Justin Klaassen22eb1992016-07-11 20:52:23 -0700336}