1 /*
2  * Copyright (C) 2020 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 
17 package com.android.server.display;
18 
19 import android.annotation.NonNull;
20 import android.content.Context;
21 import android.content.res.Resources;
22 import android.hardware.display.DisplayManagerInternal;
23 import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation;
24 import android.os.Environment;
25 import android.os.PowerManager;
26 import android.text.TextUtils;
27 import android.util.MathUtils;
28 import android.util.Slog;
29 import android.util.Spline;
30 import android.view.DisplayAddress;
31 
32 import com.android.internal.R;
33 import com.android.internal.display.BrightnessSynchronizer;
34 import com.android.server.display.config.BrightnessThresholds;
35 import com.android.server.display.config.DisplayConfiguration;
36 import com.android.server.display.config.DisplayQuirks;
37 import com.android.server.display.config.HbmTiming;
38 import com.android.server.display.config.HighBrightnessMode;
39 import com.android.server.display.config.NitsMap;
40 import com.android.server.display.config.Point;
41 import com.android.server.display.config.RefreshRateRange;
42 import com.android.server.display.config.SensorDetails;
43 import com.android.server.display.config.ThermalStatus;
44 import com.android.server.display.config.Thresholds;
45 import com.android.server.display.config.XmlParser;
46 
47 import org.xmlpull.v1.XmlPullParserException;
48 
49 import java.io.BufferedInputStream;
50 import java.io.File;
51 import java.io.FileInputStream;
52 import java.io.IOException;
53 import java.io.InputStream;
54 import java.math.BigDecimal;
55 import java.util.ArrayList;
56 import java.util.Arrays;
57 import java.util.List;
58 
59 import javax.xml.datatype.DatatypeConfigurationException;
60 
61 /**
62  * Reads and stores display-specific configurations.
63  */
64 public class DisplayDeviceConfig {
65     private static final String TAG = "DisplayDeviceConfig";
66 
67     public static final float HIGH_BRIGHTNESS_MODE_UNSUPPORTED = Float.NaN;
68 
69     public static final String QUIRK_CAN_SET_BRIGHTNESS_VIA_HWC = "canSetBrightnessViaHwc";
70 
71     private static final float BRIGHTNESS_DEFAULT = 0.5f;
72     private static final String ETC_DIR = "etc";
73     private static final String DISPLAY_CONFIG_DIR = "displayconfig";
74     private static final String CONFIG_FILE_FORMAT = "display_%s.xml";
75     private static final String PORT_SUFFIX_FORMAT = "port_%d";
76     private static final String STABLE_ID_SUFFIX_FORMAT = "id_%d";
77     private static final String NO_SUFFIX_FORMAT = "%d";
78     private static final long STABLE_FLAG = 1L << 62;
79 
80     // Float.NaN (used as invalid for brightness) cannot be stored in config.xml
81     // so -2 is used instead
82     private static final float INVALID_BRIGHTNESS_IN_CONFIG = -2f;
83 
84     private static final float NITS_INVALID = -1;
85 
86     private final Context mContext;
87 
88     // The details of the ambient light sensor associated with this display.
89     private final SensorData mAmbientLightSensor = new SensorData();
90 
91     // The details of the proximity sensor associated with this display.
92     private final SensorData mProximitySensor = new SensorData();
93 
94     private final List<RefreshRateLimitation> mRefreshRateLimitations =
95             new ArrayList<>(2 /*initialCapacity*/);
96 
97     // Nits and backlight values that are loaded from either the display device config file, or
98     // config.xml. These are the raw values and just used for the dumpsys
99     private float[] mRawNits;
100     private float[] mRawBacklight;
101 
102     // These arrays are calculated from the raw arrays, but clamped to contain values equal to and
103     // between mBacklightMinimum and mBacklightMaximum. These three arrays should all be the same
104     // length
105     // Nits array that is used to store the entire range of nits values that the device supports
106     private float[] mNits;
107     // Backlight array holds the values that the HAL uses to display the corresponding nits values
108     private float[] mBacklight;
109     // Purely an array that covers the ranges of values 0.0 - 1.0, indicating the system brightness
110     // for the corresponding values above
111     private float[] mBrightness;
112 
113     private float mBacklightMinimum = Float.NaN;
114     private float mBacklightMaximum = Float.NaN;
115     private float mBrightnessDefault = Float.NaN;
116     private float mBrightnessRampFastDecrease = Float.NaN;
117     private float mBrightnessRampFastIncrease = Float.NaN;
118     private float mBrightnessRampSlowDecrease = Float.NaN;
119     private float mBrightnessRampSlowIncrease = Float.NaN;
120     private float mScreenBrighteningMinThreshold = 0.0f;     // Retain behaviour as though there is
121     private float mScreenDarkeningMinThreshold = 0.0f;       // no minimum threshold for change in
122     private float mAmbientLuxBrighteningMinThreshold = 0.0f; // screen brightness or ambient
123     private float mAmbientLuxDarkeningMinThreshold = 0.0f;   // brightness.
124     private Spline mBrightnessToBacklightSpline;
125     private Spline mBacklightToBrightnessSpline;
126     private Spline mBacklightToNitsSpline;
127     private List<String> mQuirks;
128     private boolean mIsHighBrightnessModeEnabled = false;
129     private HighBrightnessModeData mHbmData;
130     private String mLoadedFrom = null;
131 
DisplayDeviceConfig(Context context)132     private DisplayDeviceConfig(Context context) {
133         mContext = context;
134     }
135 
136     /**
137      * Creates an instance for the specified display.
138      * Tries to find a file with identifier in the following priority order:
139      * <ol>
140      *     <li>physicalDisplayId</li>
141      *     <li>physicalDisplayId without a stable flag (old system)</li>
142      *     <li>portId</li>
143      * </ol>
144      *
145      * @param physicalDisplayId The display ID for which to load the configuration.
146      * @return A configuration instance for the specified display.
147      */
create(Context context, long physicalDisplayId, boolean isDefaultDisplay)148     public static DisplayDeviceConfig create(Context context, long physicalDisplayId,
149             boolean isDefaultDisplay) {
150         DisplayDeviceConfig config;
151 
152         config = loadConfigFromDirectory(context, Environment.getProductDirectory(),
153                 physicalDisplayId);
154         if (config != null) {
155             return config;
156         }
157 
158         config = loadConfigFromDirectory(context, Environment.getVendorDirectory(),
159                 physicalDisplayId);
160         if (config != null) {
161             return config;
162         }
163 
164         // If no config can be loaded from any ddc xml at all,
165         // prepare a whole config using the global config.xml.
166         // Guaranteed not null
167         return create(context, isDefaultDisplay);
168     }
169 
170     /**
171      * Creates an instance using global values since no display device config xml exists.
172      * Uses values from config or PowerManager.
173      *
174      * @param context
175      * @param useConfigXml
176      * @return A configuration instance.
177      */
create(Context context, boolean useConfigXml)178     public static DisplayDeviceConfig create(Context context, boolean useConfigXml) {
179         DisplayDeviceConfig config;
180         if (useConfigXml) {
181             config = getConfigFromGlobalXml(context);
182         } else {
183             config = getConfigFromPmValues(context);
184         }
185         return config;
186     }
187 
loadConfigFromDirectory(Context context, File baseDirectory, long physicalDisplayId)188     private static DisplayDeviceConfig loadConfigFromDirectory(Context context,
189             File baseDirectory, long physicalDisplayId) {
190         DisplayDeviceConfig config;
191         // Create config using filename from physical ID (including "stable" bit).
192         config = getConfigFromSuffix(context, baseDirectory, STABLE_ID_SUFFIX_FORMAT,
193                 physicalDisplayId);
194         if (config != null) {
195             return config;
196         }
197 
198         // Create config using filename from physical ID (excluding "stable" bit).
199         final long withoutStableFlag = physicalDisplayId & ~STABLE_FLAG;
200         config = getConfigFromSuffix(context, baseDirectory, NO_SUFFIX_FORMAT, withoutStableFlag);
201         if (config != null) {
202             return config;
203         }
204 
205         // Create config using filename from port ID.
206         final DisplayAddress.Physical physicalAddress =
207                 DisplayAddress.fromPhysicalDisplayId(physicalDisplayId);
208         int port = physicalAddress.getPort();
209         config = getConfigFromSuffix(context, baseDirectory, PORT_SUFFIX_FORMAT, port);
210         return config;
211     }
212 
213     /**
214      * Return the brightness mapping nits array.
215      *
216      * @return The brightness mapping nits array.
217      */
getNits()218     public float[] getNits() {
219         return mNits;
220     }
221 
222     /**
223      * Return the brightness mapping backlight array.
224      *
225      * @return The backlight mapping value array.
226      */
getBacklight()227     public float[] getBacklight() {
228         return mBacklight;
229     }
230 
231     /**
232      * Calculates the backlight value, as recognised by the HAL, from the brightness value
233      * given that the rest of the system deals with.
234      *
235      * @param brightness value on the framework scale of 0-1
236      * @return backlight value on the HAL scale of 0-1
237      */
getBacklightFromBrightness(float brightness)238     public float getBacklightFromBrightness(float brightness) {
239         return mBrightnessToBacklightSpline.interpolate(brightness);
240     }
241 
242     /**
243      * Calculates the nits value for the specified backlight value if a mapping exists.
244      *
245      * @return The mapped nits or 0 if no mapping exits.
246      */
getNitsFromBacklight(float backlight)247     public float getNitsFromBacklight(float backlight) {
248         if (mBacklightToNitsSpline == null) {
249             Slog.wtf(TAG, "requesting nits when no mapping exists.");
250             return NITS_INVALID;
251         }
252         backlight = Math.max(backlight, mBacklightMinimum);
253         return mBacklightToNitsSpline.interpolate(backlight);
254     }
255 
256     /**
257      * Return an array of equal length to backlight and nits, that covers the entire system
258      * brightness range of 0.0-1.0.
259      *
260      * @return brightness array
261      */
getBrightness()262     public float[] getBrightness() {
263         return mBrightness;
264     }
265 
266     /**
267      * Return the default brightness on a scale of 0.0f - 1.0f
268      *
269      * @return default brightness
270      */
getBrightnessDefault()271     public float getBrightnessDefault() {
272         return mBrightnessDefault;
273     }
274 
getBrightnessRampFastDecrease()275     public float getBrightnessRampFastDecrease() {
276         return mBrightnessRampFastDecrease;
277     }
278 
getBrightnessRampFastIncrease()279     public float getBrightnessRampFastIncrease() {
280         return mBrightnessRampFastIncrease;
281     }
282 
getBrightnessRampSlowDecrease()283     public float getBrightnessRampSlowDecrease() {
284         return mBrightnessRampSlowDecrease;
285     }
286 
getBrightnessRampSlowIncrease()287     public float getBrightnessRampSlowIncrease() {
288         return mBrightnessRampSlowIncrease;
289     }
290 
getScreenBrighteningMinThreshold()291     public float getScreenBrighteningMinThreshold() {
292         return mScreenBrighteningMinThreshold;
293     }
294 
getScreenDarkeningMinThreshold()295     public float getScreenDarkeningMinThreshold() {
296         return mScreenDarkeningMinThreshold;
297     }
298 
getAmbientLuxBrighteningMinThreshold()299     public float getAmbientLuxBrighteningMinThreshold() {
300         return mAmbientLuxBrighteningMinThreshold;
301     }
302 
getAmbientLuxDarkeningMinThreshold()303     public float getAmbientLuxDarkeningMinThreshold() {
304         return mAmbientLuxDarkeningMinThreshold;
305     }
306 
getAmbientLightSensor()307     SensorData getAmbientLightSensor() {
308         return mAmbientLightSensor;
309     }
310 
getProximitySensor()311     SensorData getProximitySensor() {
312         return mProximitySensor;
313     }
314 
315     /**
316      * @param quirkValue The quirk to test.
317      * @return {@code true} if the specified quirk is present in this configuration,
318      * {@code false} otherwise.
319      */
hasQuirk(String quirkValue)320     public boolean hasQuirk(String quirkValue) {
321         return mQuirks != null && mQuirks.contains(quirkValue);
322     }
323 
324     /**
325      * @return high brightness mode configuration data for the display.
326      */
getHighBrightnessModeData()327     public HighBrightnessModeData getHighBrightnessModeData() {
328         if (!mIsHighBrightnessModeEnabled || mHbmData == null) {
329             return null;
330         }
331 
332         HighBrightnessModeData hbmData = new HighBrightnessModeData();
333         mHbmData.copyTo(hbmData);
334         return hbmData;
335     }
336 
getRefreshRateLimitations()337     public List<RefreshRateLimitation> getRefreshRateLimitations() {
338         return mRefreshRateLimitations;
339     }
340 
341     @Override
toString()342     public String toString() {
343         String str = "DisplayDeviceConfig{"
344                 + "mLoadedFrom=" + mLoadedFrom
345                 + ", mBacklight=" + Arrays.toString(mBacklight)
346                 + ", mNits=" + Arrays.toString(mNits)
347                 + ", mRawBacklight=" + Arrays.toString(mRawBacklight)
348                 + ", mRawNits=" + Arrays.toString(mRawNits)
349                 + ", mBrightness=" + Arrays.toString(mBrightness)
350                 + ", mBrightnessToBacklightSpline=" + mBrightnessToBacklightSpline
351                 + ", mBacklightToBrightnessSpline=" + mBacklightToBrightnessSpline
352                 + ", mBacklightMinimum=" + mBacklightMinimum
353                 + ", mBacklightMaximum=" + mBacklightMaximum
354                 + ", mBrightnessDefault=" + mBrightnessDefault
355                 + ", mQuirks=" + mQuirks
356                 + ", isHbmEnabled=" + mIsHighBrightnessModeEnabled
357                 + ", mHbmData=" + mHbmData
358                 + ", mBrightnessRampFastDecrease=" + mBrightnessRampFastDecrease
359                 + ", mBrightnessRampFastIncrease=" + mBrightnessRampFastIncrease
360                 + ", mBrightnessRampSlowDecrease=" + mBrightnessRampSlowDecrease
361                 + ", mBrightnessRampSlowIncrease=" + mBrightnessRampSlowIncrease
362                 + ", mScreenDarkeningMinThreshold=" + mScreenDarkeningMinThreshold
363                 + ", mScreenBrighteningMinThreshold=" + mScreenBrighteningMinThreshold
364                 + ", mAmbientLuxDarkeningMinThreshold=" + mAmbientLuxDarkeningMinThreshold
365                 + ", mAmbientLuxBrighteningMinThreshold=" + mAmbientLuxBrighteningMinThreshold
366                 + ", mAmbientLightSensor=" + mAmbientLightSensor
367                 + ", mProximitySensor=" + mProximitySensor
368                 + ", mRefreshRateLimitations= " + Arrays.toString(mRefreshRateLimitations.toArray())
369                 + "}";
370         return str;
371     }
372 
getConfigFromSuffix(Context context, File baseDirectory, String suffixFormat, long idNumber)373     private static DisplayDeviceConfig getConfigFromSuffix(Context context, File baseDirectory,
374             String suffixFormat, long idNumber) {
375 
376         final String suffix = String.format(suffixFormat, idNumber);
377         final String filename = String.format(CONFIG_FILE_FORMAT, suffix);
378         final File filePath = Environment.buildPath(
379                 baseDirectory, ETC_DIR, DISPLAY_CONFIG_DIR, filename);
380         final DisplayDeviceConfig config = new DisplayDeviceConfig(context);
381         if (config.initFromFile(filePath)) {
382             return config;
383         }
384         return null;
385     }
386 
getConfigFromGlobalXml(Context context)387     private static DisplayDeviceConfig getConfigFromGlobalXml(Context context) {
388         DisplayDeviceConfig config = new DisplayDeviceConfig(context);
389         config.initFromGlobalXml();
390         return config;
391     }
392 
getConfigFromPmValues(Context context)393     private static DisplayDeviceConfig getConfigFromPmValues(Context context) {
394         DisplayDeviceConfig config = new DisplayDeviceConfig(context);
395         config.initFromDefaultValues();
396         return config;
397     }
398 
initFromFile(File configFile)399     private boolean initFromFile(File configFile) {
400         if (!configFile.exists()) {
401             // Display configuration files aren't required to exist.
402             return false;
403         }
404 
405         if (!configFile.isFile()) {
406             Slog.e(TAG, "Display configuration is not a file: " + configFile + ", skipping");
407             return false;
408         }
409 
410         try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) {
411             final DisplayConfiguration config = XmlParser.read(in);
412             if (config != null) {
413                 loadBrightnessDefaultFromDdcXml(config);
414                 loadBrightnessConstraintsFromConfigXml();
415                 loadBrightnessMap(config);
416                 loadHighBrightnessModeData(config);
417                 loadQuirks(config);
418                 loadBrightnessRamps(config);
419                 loadAmbientLightSensorFromDdc(config);
420                 loadProxSensorFromDdc(config);
421                 loadBrightnessChangeThresholds(config);
422             } else {
423                 Slog.w(TAG, "DisplayDeviceConfig file is null");
424             }
425         } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
426             Slog.e(TAG, "Encountered an error while reading/parsing display config file: "
427                     + configFile, e);
428         }
429         mLoadedFrom = configFile.toString();
430         return true;
431     }
432 
initFromGlobalXml()433     private void initFromGlobalXml() {
434         // If no ddc exists, use config.xml
435         loadBrightnessDefaultFromConfigXml();
436         loadBrightnessConstraintsFromConfigXml();
437         loadBrightnessMapFromConfigXml();
438         loadBrightnessRampsFromConfigXml();
439         loadAmbientLightSensorFromConfigXml();
440         setProxSensorUnspecified();
441         mLoadedFrom = "<config.xml>";
442     }
443 
initFromDefaultValues()444     private void initFromDefaultValues() {
445         // Set all to basic values
446         mLoadedFrom = "Static values";
447         mBacklightMinimum = PowerManager.BRIGHTNESS_MIN;
448         mBacklightMaximum = PowerManager.BRIGHTNESS_MAX;
449         mBrightnessDefault = BRIGHTNESS_DEFAULT;
450         mBrightnessRampFastDecrease = PowerManager.BRIGHTNESS_MAX;
451         mBrightnessRampFastIncrease = PowerManager.BRIGHTNESS_MAX;
452         mBrightnessRampSlowDecrease = PowerManager.BRIGHTNESS_MAX;
453         mBrightnessRampSlowIncrease = PowerManager.BRIGHTNESS_MAX;
454         setSimpleMappingStrategyValues();
455         loadAmbientLightSensorFromConfigXml();
456         setProxSensorUnspecified();
457     }
458 
loadBrightnessDefaultFromDdcXml(DisplayConfiguration config)459     private void loadBrightnessDefaultFromDdcXml(DisplayConfiguration config) {
460         // Default brightness values are stored in the displayDeviceConfig file,
461         // Or we fallback standard values if not.
462         // Priority 1: Value in the displayDeviceConfig
463         // Priority 2: Value in the config.xml (float)
464         // Priority 3: Value in the config.xml (int)
465         if (config != null) {
466             BigDecimal configBrightnessDefault = config.getScreenBrightnessDefault();
467             if (configBrightnessDefault != null) {
468                 mBrightnessDefault = configBrightnessDefault.floatValue();
469             } else {
470                 loadBrightnessDefaultFromConfigXml();
471             }
472         }
473     }
474 
loadBrightnessDefaultFromConfigXml()475     private void loadBrightnessDefaultFromConfigXml() {
476         // Priority 1: Value in the config.xml (float)
477         // Priority 2: Value in the config.xml (int)
478         final float def = mContext.getResources().getFloat(com.android.internal.R.dimen
479                 .config_screenBrightnessSettingDefaultFloat);
480         if (def == INVALID_BRIGHTNESS_IN_CONFIG) {
481             mBrightnessDefault = BrightnessSynchronizer.brightnessIntToFloat(
482                     mContext.getResources().getInteger(com.android.internal.R.integer
483                             .config_screenBrightnessSettingDefault));
484         } else {
485             mBrightnessDefault = def;
486         }
487     }
488 
loadBrightnessConstraintsFromConfigXml()489     private void loadBrightnessConstraintsFromConfigXml() {
490         // TODO(b/175373898) add constraints (min / max) to ddc.
491         final float min = mContext.getResources().getFloat(com.android.internal.R.dimen
492                 .config_screenBrightnessSettingMinimumFloat);
493         final float max = mContext.getResources().getFloat(com.android.internal.R.dimen
494                 .config_screenBrightnessSettingMaximumFloat);
495         if (min == INVALID_BRIGHTNESS_IN_CONFIG || max == INVALID_BRIGHTNESS_IN_CONFIG) {
496             mBacklightMinimum = BrightnessSynchronizer.brightnessIntToFloat(
497                     mContext.getResources().getInteger(com.android.internal.R.integer
498                             .config_screenBrightnessSettingMinimum));
499             mBacklightMaximum = BrightnessSynchronizer.brightnessIntToFloat(
500                     mContext.getResources().getInteger(com.android.internal.R.integer
501                             .config_screenBrightnessSettingMaximum));
502         } else {
503             mBacklightMinimum = min;
504             mBacklightMaximum = max;
505         }
506     }
507 
loadBrightnessMap(DisplayConfiguration config)508     private void loadBrightnessMap(DisplayConfiguration config) {
509         final NitsMap map = config.getScreenBrightnessMap();
510         // Map may not exist in display device config
511         if (map == null) {
512             loadBrightnessMapFromConfigXml();
513             return;
514         }
515 
516         // Use the (preferred) display device config mapping
517         final List<Point> points = map.getPoint();
518         final int size = points.size();
519 
520         float[] nits = new float[size];
521         float[] backlight = new float[size];
522 
523         int i = 0;
524         for (Point point : points) {
525             nits[i] = point.getNits().floatValue();
526             backlight[i] = point.getValue().floatValue();
527             if (i > 0) {
528                 if (nits[i] < nits[i - 1]) {
529                     Slog.e(TAG, "screenBrightnessMap must be non-decreasing, ignoring rest "
530                             + " of configuration. Nits: " + nits[i] + " < " + nits[i - 1]);
531                     return;
532                 }
533 
534                 if (backlight[i] < backlight[i - 1]) {
535                     Slog.e(TAG, "screenBrightnessMap must be non-decreasing, ignoring rest "
536                             + " of configuration. Value: " + backlight[i] + " < "
537                             + backlight[i - 1]);
538                     return;
539                 }
540             }
541             ++i;
542         }
543         mRawNits = nits;
544         mRawBacklight = backlight;
545         constrainNitsAndBacklightArrays();
546     }
547 
loadBrightnessMapFromConfigXml()548     private void loadBrightnessMapFromConfigXml() {
549         // Use the config.xml mapping
550         final Resources res = mContext.getResources();
551         final float[] sysNits = BrightnessMappingStrategy.getFloatArray(res.obtainTypedArray(
552                 com.android.internal.R.array.config_screenBrightnessNits));
553         final int[] sysBrightness = res.getIntArray(
554                 com.android.internal.R.array.config_screenBrightnessBacklight);
555         final float[] sysBrightnessFloat = new float[sysBrightness.length];
556 
557         for (int i = 0; i < sysBrightness.length; i++) {
558             sysBrightnessFloat[i] = BrightnessSynchronizer.brightnessIntToFloat(
559                     sysBrightness[i]);
560         }
561 
562         // These arrays are allowed to be empty, we set null values so that
563         // BrightnessMappingStrategy will create a SimpleMappingStrategy instead.
564         if (sysBrightnessFloat.length == 0 || sysNits.length == 0) {
565             setSimpleMappingStrategyValues();
566             return;
567         }
568 
569         mRawNits = sysNits;
570         mRawBacklight = sysBrightnessFloat;
571         constrainNitsAndBacklightArrays();
572     }
573 
setSimpleMappingStrategyValues()574     private void setSimpleMappingStrategyValues() {
575         // No translation from backlight to brightness should occur if we are using a
576         // SimpleMappingStrategy (ie they should be the same) so the splines are
577         // set to be linear, between 0.0 and 1.0
578         mNits = null;
579         mBacklight = null;
580         float[] simpleMappingStrategyArray = new float[]{0.0f, 1.0f};
581         mBrightnessToBacklightSpline = Spline.createSpline(simpleMappingStrategyArray,
582                 simpleMappingStrategyArray);
583         mBacklightToBrightnessSpline = Spline.createSpline(simpleMappingStrategyArray,
584                 simpleMappingStrategyArray);
585     }
586 
587     /**
588      * Change the nits and backlight arrays, so that they cover only the allowed backlight values
589      * Use the brightness minimum and maximum values to clamp these arrays.
590      */
constrainNitsAndBacklightArrays()591     private void constrainNitsAndBacklightArrays() {
592         if (mRawBacklight[0] > mBacklightMinimum
593                 || mRawBacklight[mRawBacklight.length - 1] < mBacklightMaximum
594                 || mBacklightMinimum > mBacklightMaximum) {
595             throw new IllegalStateException("Min or max values are invalid"
596                     + "; raw min=" + mRawBacklight[0]
597                     + "; raw max=" + mRawBacklight[mRawBacklight.length - 1]
598                     + "; backlight min=" + mBacklightMinimum
599                     + "; backlight max=" + mBacklightMaximum);
600         }
601 
602         float[] newNits = new float[mRawBacklight.length];
603         float[] newBacklight = new float[mRawBacklight.length];
604         // Find the starting index of the clamped arrays. This may be less than the min so
605         // we'll need to clamp this value still when actually doing the remapping.
606         int newStart = 0;
607         for (int i = 0; i < mRawBacklight.length - 1; i++) {
608             if (mRawBacklight[i + 1] > mBacklightMinimum) {
609                 newStart = i;
610                 break;
611             }
612         }
613 
614         boolean isLastValue = false;
615         int newIndex = 0;
616         for (int i = newStart; i < mRawBacklight.length && !isLastValue; i++) {
617             newIndex = i - newStart;
618             final float newBacklightVal;
619             final float newNitsVal;
620             isLastValue = mRawBacklight[i] >= mBacklightMaximum
621                     || i >= mRawBacklight.length - 1;
622             // Clamp beginning and end to valid backlight values.
623             if (newIndex == 0) {
624                 newBacklightVal = MathUtils.max(mRawBacklight[i], mBacklightMinimum);
625                 newNitsVal = rawBacklightToNits(i, newBacklightVal);
626             } else if (isLastValue) {
627                 newBacklightVal = MathUtils.min(mRawBacklight[i], mBacklightMaximum);
628                 newNitsVal = rawBacklightToNits(i - 1, newBacklightVal);
629             } else {
630                 newBacklightVal = mRawBacklight[i];
631                 newNitsVal = mRawNits[i];
632             }
633             newBacklight[newIndex] = newBacklightVal;
634             newNits[newIndex] = newNitsVal;
635         }
636         mBacklight = Arrays.copyOf(newBacklight, newIndex + 1);
637         mNits = Arrays.copyOf(newNits, newIndex + 1);
638         createBacklightConversionSplines();
639     }
640 
rawBacklightToNits(int i, float backlight)641     private float rawBacklightToNits(int i, float backlight) {
642         return MathUtils.map(mRawBacklight[i], mRawBacklight[i + 1],
643                 mRawNits[i], mRawNits[i + 1], backlight);
644     }
645 
646     // This method creates a brightness spline that is of equal length with proportional increments
647     // to the backlight spline. The values of this array range from 0.0f to 1.0f instead of the
648     // potential constrained range that the backlight array covers
649     // These splines are used to convert from the system brightness value to the HAL backlight
650     // value
createBacklightConversionSplines()651     private void createBacklightConversionSplines() {
652         mBrightness = new float[mBacklight.length];
653         for (int i = 0; i < mBrightness.length; i++) {
654             mBrightness[i] = MathUtils.map(mBacklight[0],
655                     mBacklight[mBacklight.length - 1],
656                     PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, mBacklight[i]);
657         }
658         mBrightnessToBacklightSpline = Spline.createSpline(mBrightness, mBacklight);
659         mBacklightToBrightnessSpline = Spline.createSpline(mBacklight, mBrightness);
660         mBacklightToNitsSpline = Spline.createSpline(mBacklight, mNits);
661     }
662 
loadQuirks(DisplayConfiguration config)663     private void loadQuirks(DisplayConfiguration config) {
664         final DisplayQuirks quirks = config.getQuirks();
665         if (quirks != null) {
666             mQuirks = new ArrayList<>(quirks.getQuirk());
667         }
668     }
669 
loadHighBrightnessModeData(DisplayConfiguration config)670     private void loadHighBrightnessModeData(DisplayConfiguration config) {
671         final HighBrightnessMode hbm = config.getHighBrightnessMode();
672         if (hbm != null) {
673             mIsHighBrightnessModeEnabled = hbm.getEnabled();
674             mHbmData = new HighBrightnessModeData();
675             mHbmData.minimumLux = hbm.getMinimumLux_all().floatValue();
676             float transitionPointBacklightScale = hbm.getTransitionPoint_all().floatValue();
677             if (transitionPointBacklightScale >= mBacklightMaximum) {
678                 throw new IllegalArgumentException("HBM transition point invalid. "
679                         + mHbmData.transitionPoint + " is not less than "
680                         + mBacklightMaximum);
681             }
682             mHbmData.transitionPoint =
683                     mBacklightToBrightnessSpline.interpolate(transitionPointBacklightScale);
684             final HbmTiming hbmTiming = hbm.getTiming_all();
685             mHbmData.timeWindowMillis = hbmTiming.getTimeWindowSecs_all().longValue() * 1000;
686             mHbmData.timeMaxMillis = hbmTiming.getTimeMaxSecs_all().longValue() * 1000;
687             mHbmData.timeMinMillis = hbmTiming.getTimeMinSecs_all().longValue() * 1000;
688             mHbmData.thermalStatusLimit = convertThermalStatus(hbm.getThermalStatusLimit_all());
689             mHbmData.allowInLowPowerMode = hbm.getAllowInLowPowerMode_all();
690             final RefreshRateRange rr = hbm.getRefreshRate_all();
691             if (rr != null) {
692                 final float min = rr.getMinimum().floatValue();
693                 final float max = rr.getMaximum().floatValue();
694                 mRefreshRateLimitations.add(new RefreshRateLimitation(
695                         DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE, min, max));
696             }
697         }
698     }
699 
loadBrightnessRamps(DisplayConfiguration config)700     private void loadBrightnessRamps(DisplayConfiguration config) {
701         // Priority 1: Value in the display device config (float)
702         // Priority 2: Value in the config.xml (int)
703         final BigDecimal fastDownDecimal = config.getScreenBrightnessRampFastDecrease();
704         final BigDecimal fastUpDecimal = config.getScreenBrightnessRampFastIncrease();
705         final BigDecimal slowDownDecimal = config.getScreenBrightnessRampSlowDecrease();
706         final BigDecimal slowUpDecimal = config.getScreenBrightnessRampSlowIncrease();
707 
708         if (fastDownDecimal != null && fastUpDecimal != null && slowDownDecimal != null
709                 && slowUpDecimal != null) {
710             mBrightnessRampFastDecrease = fastDownDecimal.floatValue();
711             mBrightnessRampFastIncrease = fastUpDecimal.floatValue();
712             mBrightnessRampSlowDecrease = slowDownDecimal.floatValue();
713             mBrightnessRampSlowIncrease = slowUpDecimal.floatValue();
714         } else {
715             if (fastDownDecimal != null || fastUpDecimal != null || slowDownDecimal != null
716                     || slowUpDecimal != null) {
717                 Slog.w(TAG, "Per display brightness ramp values ignored because not all "
718                         + "values are present in display device config");
719             }
720             loadBrightnessRampsFromConfigXml();
721         }
722     }
723 
loadBrightnessRampsFromConfigXml()724     private void loadBrightnessRampsFromConfigXml() {
725         mBrightnessRampFastIncrease = BrightnessSynchronizer.brightnessIntToFloat(
726                 mContext.getResources().getInteger(R.integer.config_brightness_ramp_rate_fast));
727         mBrightnessRampSlowIncrease = BrightnessSynchronizer.brightnessIntToFloat(
728                 mContext.getResources().getInteger(R.integer.config_brightness_ramp_rate_slow));
729         // config.xml uses the same values for both increasing and decreasing brightness
730         // transitions so we assign them to the same values here.
731         mBrightnessRampFastDecrease = mBrightnessRampFastIncrease;
732         mBrightnessRampSlowDecrease = mBrightnessRampSlowIncrease;
733     }
734 
loadAmbientLightSensorFromConfigXml()735     private void loadAmbientLightSensorFromConfigXml() {
736         mAmbientLightSensor.name = "";
737         mAmbientLightSensor.type = mContext.getResources().getString(
738                 com.android.internal.R.string.config_displayLightSensorType);
739     }
740 
loadAmbientLightSensorFromDdc(DisplayConfiguration config)741     private void loadAmbientLightSensorFromDdc(DisplayConfiguration config) {
742         final SensorDetails sensorDetails = config.getLightSensor();
743         if (sensorDetails != null) {
744             mAmbientLightSensor.type = sensorDetails.getType();
745             mAmbientLightSensor.name = sensorDetails.getName();
746             final RefreshRateRange rr = sensorDetails.getRefreshRate();
747             if (rr != null) {
748                 mAmbientLightSensor.minRefreshRate = rr.getMinimum().floatValue();
749                 mAmbientLightSensor.maxRefreshRate = rr.getMaximum().floatValue();
750             }
751         } else {
752             loadAmbientLightSensorFromConfigXml();
753         }
754     }
755 
setProxSensorUnspecified()756     private void setProxSensorUnspecified() {
757         mProximitySensor.name = "";
758         mProximitySensor.type = "";
759     }
760 
loadProxSensorFromDdc(DisplayConfiguration config)761     private void loadProxSensorFromDdc(DisplayConfiguration config) {
762         SensorDetails sensorDetails = config.getProxSensor();
763         if (sensorDetails != null) {
764             mProximitySensor.name = sensorDetails.getName();
765             mProximitySensor.type = sensorDetails.getType();
766             final RefreshRateRange rr = sensorDetails.getRefreshRate();
767             if (rr != null) {
768                 mProximitySensor.minRefreshRate = rr.getMinimum().floatValue();
769                 mProximitySensor.maxRefreshRate = rr.getMaximum().floatValue();
770             }
771         } else {
772             setProxSensorUnspecified();
773         }
774     }
775 
loadBrightnessChangeThresholds(DisplayConfiguration config)776     private void loadBrightnessChangeThresholds(DisplayConfiguration config) {
777         Thresholds displayBrightnessThresholds = config.getDisplayBrightnessChangeThresholds();
778         Thresholds ambientBrightnessThresholds = config.getAmbientBrightnessChangeThresholds();
779 
780         if (displayBrightnessThresholds != null) {
781             BrightnessThresholds brighteningScreen =
782                     displayBrightnessThresholds.getBrighteningThresholds();
783             BrightnessThresholds darkeningScreen =
784                     displayBrightnessThresholds.getDarkeningThresholds();
785 
786             final BigDecimal screenBrighteningThreshold = brighteningScreen.getMinimum();
787             final BigDecimal screenDarkeningThreshold = darkeningScreen.getMinimum();
788 
789             if (screenBrighteningThreshold != null) {
790                 mScreenBrighteningMinThreshold = screenBrighteningThreshold.floatValue();
791             }
792             if (screenDarkeningThreshold != null) {
793                 mScreenDarkeningMinThreshold = screenDarkeningThreshold.floatValue();
794             }
795         }
796 
797         if (ambientBrightnessThresholds != null) {
798             BrightnessThresholds brighteningAmbientLux =
799                     ambientBrightnessThresholds.getBrighteningThresholds();
800             BrightnessThresholds darkeningAmbientLux =
801                     ambientBrightnessThresholds.getDarkeningThresholds();
802 
803             final BigDecimal ambientBrighteningThreshold = brighteningAmbientLux.getMinimum();
804             final BigDecimal ambientDarkeningThreshold =  darkeningAmbientLux.getMinimum();
805 
806             if (ambientBrighteningThreshold != null) {
807                 mAmbientLuxBrighteningMinThreshold = ambientBrighteningThreshold.floatValue();
808             }
809             if (ambientDarkeningThreshold != null) {
810                 mAmbientLuxDarkeningMinThreshold = ambientDarkeningThreshold.floatValue();
811             }
812         }
813     }
814 
convertThermalStatus(ThermalStatus value)815     private @PowerManager.ThermalStatus int convertThermalStatus(ThermalStatus value) {
816         if (value == null) {
817             return PowerManager.THERMAL_STATUS_NONE;
818         }
819         switch (value) {
820             case none:
821                 return PowerManager.THERMAL_STATUS_NONE;
822             case light:
823                 return PowerManager.THERMAL_STATUS_LIGHT;
824             case moderate:
825                 return PowerManager.THERMAL_STATUS_MODERATE;
826             case severe:
827                 return PowerManager.THERMAL_STATUS_SEVERE;
828             case critical:
829                 return PowerManager.THERMAL_STATUS_CRITICAL;
830             case emergency:
831                 return PowerManager.THERMAL_STATUS_EMERGENCY;
832             case shutdown:
833                 return PowerManager.THERMAL_STATUS_SHUTDOWN;
834             default:
835                 Slog.wtf(TAG, "Unexpected Thermal Status: " + value);
836                 return PowerManager.THERMAL_STATUS_NONE;
837         }
838     }
839 
840     static class SensorData {
841         public String type;
842         public String name;
843         public float minRefreshRate = 0.0f;
844         public float maxRefreshRate = Float.POSITIVE_INFINITY;
845 
846         @Override
toString()847         public String toString() {
848             return "Sensor{"
849                     + "type: " + type
850                     + ", name: " + name
851                     + ", refreshRateRange: [" + minRefreshRate + ", " + maxRefreshRate + "]"
852                     + "} ";
853         }
854 
855         /**
856          * @return True if the sensor matches both the specified name and type, or one if only
857          * one is specified (not-empty). Always returns false if both parameters are null or empty.
858          */
matches(String sensorName, String sensorType)859         public boolean matches(String sensorName, String sensorType) {
860             final boolean isNameSpecified = !TextUtils.isEmpty(sensorName);
861             final boolean isTypeSpecified = !TextUtils.isEmpty(sensorType);
862             return (isNameSpecified || isTypeSpecified)
863                     && (!isNameSpecified || sensorName.equals(name))
864                     && (!isTypeSpecified || sensorType.equals(type));
865         }
866     }
867 
868     /**
869      * Container for high brightness mode configuration data.
870      */
871     static class HighBrightnessModeData {
872         /** Minimum lux needed to enter high brightness mode */
873         public float minimumLux;
874 
875         /** Brightness level at which we transition from normal to high-brightness. */
876         public float transitionPoint;
877 
878         /** Enable HBM only if the thermal status is not higher than this. */
879         public @PowerManager.ThermalStatus int thermalStatusLimit;
880 
881         /** Whether HBM is allowed when {@code Settings.Global.LOW_POWER_MODE} is active. */
882         public boolean allowInLowPowerMode;
883 
884         /** Time window for HBM. */
885         public long timeWindowMillis;
886 
887         /** Maximum time HBM is allowed to be during in a {@code timeWindowMillis}. */
888         public long timeMaxMillis;
889 
890         /** Minimum time that HBM can be on before being enabled. */
891         public long timeMinMillis;
892 
HighBrightnessModeData()893         HighBrightnessModeData() {}
894 
HighBrightnessModeData(float minimumLux, float transitionPoint, long timeWindowMillis, long timeMaxMillis, long timeMinMillis, @PowerManager.ThermalStatus int thermalStatusLimit, boolean allowInLowPowerMode)895         HighBrightnessModeData(float minimumLux, float transitionPoint, long timeWindowMillis,
896                 long timeMaxMillis, long timeMinMillis,
897                 @PowerManager.ThermalStatus int thermalStatusLimit, boolean allowInLowPowerMode) {
898             this.minimumLux = minimumLux;
899             this.transitionPoint = transitionPoint;
900             this.timeWindowMillis = timeWindowMillis;
901             this.timeMaxMillis = timeMaxMillis;
902             this.timeMinMillis = timeMinMillis;
903             this.thermalStatusLimit = thermalStatusLimit;
904             this.allowInLowPowerMode = allowInLowPowerMode;
905         }
906 
907         /**
908          * Copies the HBM data to the specified parameter instance.
909          * @param other the instance to copy data to.
910          */
copyTo(@onNull HighBrightnessModeData other)911         public void copyTo(@NonNull HighBrightnessModeData other) {
912             other.minimumLux = minimumLux;
913             other.timeWindowMillis = timeWindowMillis;
914             other.timeMaxMillis = timeMaxMillis;
915             other.timeMinMillis = timeMinMillis;
916             other.transitionPoint = transitionPoint;
917             other.thermalStatusLimit = thermalStatusLimit;
918             other.allowInLowPowerMode = allowInLowPowerMode;
919         }
920 
921         @Override
toString()922         public String toString() {
923             return "HBM{"
924                     + "minLux: " + minimumLux
925                     + ", transition: " + transitionPoint
926                     + ", timeWindow: " + timeWindowMillis + "ms"
927                     + ", timeMax: " + timeMaxMillis + "ms"
928                     + ", timeMin: " + timeMinMillis + "ms"
929                     + ", thermalStatusLimit: " + thermalStatusLimit
930                     + ", allowInLowPowerMode: " + allowInLowPowerMode
931                     + "} ";
932         }
933     }
934 }
935