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