1 /*
2  * Copyright (C) 2019 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.whitebalance;
18 
19 import android.content.res.Resources;
20 import android.content.res.TypedArray;
21 import android.hardware.SensorManager;
22 import android.os.Handler;
23 import android.util.TypedValue;
24 
25 import com.android.internal.annotations.VisibleForTesting;
26 
27 /**
28  * The DisplayWhiteBalanceFactory creates and configures an DisplayWhiteBalanceController.
29  */
30 public class DisplayWhiteBalanceFactory {
31 
32     private static final String BRIGHTNESS_FILTER_TAG = "AmbientBrightnessFilter";
33     private static final String COLOR_TEMPERATURE_FILTER_TAG = "AmbientColorTemperatureFilter";
34 
35     /**
36      * Create and configure an DisplayWhiteBalanceController.
37      *
38      * @param handler
39      *      The handler used to determine which thread to run on.
40      * @param sensorManager
41      *      The sensor manager used to acquire necessary sensors.
42      * @param resources
43      *      The resources used to configure the various components.
44      *
45      * @return A DisplayWhiteBalanceController.
46      *
47      * @throws NullPointerException
48      *      - handler is null;
49      *      - sensorManager is null.
50      * @throws Resources.NotFoundException
51      *      - Configurations are missing.
52      * @throws IllegalArgumentException
53      *      - Configurations are invalid.
54      * @throws IllegalStateException
55      *      - Cannot find the necessary sensors.
56      */
create(Handler handler, SensorManager sensorManager, Resources resources)57     public static DisplayWhiteBalanceController create(Handler handler,
58             SensorManager sensorManager, Resources resources) {
59         final AmbientSensor.AmbientBrightnessSensor brightnessSensor =
60                 createBrightnessSensor(handler, sensorManager, resources);
61         final AmbientFilter brightnessFilter = createBrightnessFilter(resources);
62         final AmbientSensor.AmbientColorTemperatureSensor colorTemperatureSensor =
63                 createColorTemperatureSensor(handler, sensorManager, resources);
64         final AmbientFilter colorTemperatureFilter = createColorTemperatureFilter(resources);
65         final DisplayWhiteBalanceThrottler throttler = createThrottler(resources);
66         final float[] displayWhiteBalanceLowLightAmbientBrightnesses = getFloatArray(resources,
67                 com.android.internal.R.array
68                 .config_displayWhiteBalanceLowLightAmbientBrightnesses);
69         final float[] displayWhiteBalanceLowLightAmbientBiases = getFloatArray(resources,
70                 com.android.internal.R.array
71                 .config_displayWhiteBalanceLowLightAmbientBiases);
72         final float lowLightAmbientColorTemperature = getFloat(resources,
73                 com.android.internal.R.dimen
74                 .config_displayWhiteBalanceLowLightAmbientColorTemperature);
75         final float[] displayWhiteBalanceHighLightAmbientBrightnesses = getFloatArray(resources,
76                 com.android.internal.R.array
77                 .config_displayWhiteBalanceHighLightAmbientBrightnesses);
78         final float[] displayWhiteBalanceHighLightAmbientBiases = getFloatArray(resources,
79                 com.android.internal.R.array
80                 .config_displayWhiteBalanceHighLightAmbientBiases);
81         final float highLightAmbientColorTemperature = getFloat(resources,
82                 com.android.internal.R.dimen
83                 .config_displayWhiteBalanceHighLightAmbientColorTemperature);
84         final float[] ambientColorTemperatures = getFloatArray(resources,
85                 com.android.internal.R.array.config_displayWhiteBalanceAmbientColorTemperatures);
86         final float[] displayColorTempeartures = getFloatArray(resources,
87                 com.android.internal.R.array.config_displayWhiteBalanceDisplayColorTemperatures);
88         final DisplayWhiteBalanceController controller = new DisplayWhiteBalanceController(
89                 brightnessSensor, brightnessFilter, colorTemperatureSensor, colorTemperatureFilter,
90                 throttler, displayWhiteBalanceLowLightAmbientBrightnesses,
91                 displayWhiteBalanceLowLightAmbientBiases, lowLightAmbientColorTemperature,
92                 displayWhiteBalanceHighLightAmbientBrightnesses,
93                 displayWhiteBalanceHighLightAmbientBiases, highLightAmbientColorTemperature,
94                 ambientColorTemperatures, displayColorTempeartures);
95         brightnessSensor.setCallbacks(controller);
96         colorTemperatureSensor.setCallbacks(controller);
97         return controller;
98     }
99 
100     // Instantiation is disabled.
DisplayWhiteBalanceFactory()101     private DisplayWhiteBalanceFactory() { }
102 
103     /**
104      * Creates a brightness sensor instance to redirect sensor data to callbacks.
105      */
106     @VisibleForTesting
createBrightnessSensor(Handler handler, SensorManager sensorManager, Resources resources)107     public static AmbientSensor.AmbientBrightnessSensor createBrightnessSensor(Handler handler,
108             SensorManager sensorManager, Resources resources) {
109         final int rate = resources.getInteger(
110                 com.android.internal.R.integer.config_displayWhiteBalanceBrightnessSensorRate);
111         return new AmbientSensor.AmbientBrightnessSensor(handler, sensorManager, rate);
112     }
113 
114     /**
115      * Creates a BrightnessFilter which functions as a weighted moving average buffer for recent
116      * brightness values.
117      */
createBrightnessFilter(Resources resources)118     public static AmbientFilter createBrightnessFilter(Resources resources) {
119         final int horizon = resources.getInteger(
120                 com.android.internal.R.integer.config_displayWhiteBalanceBrightnessFilterHorizon);
121         final float intercept = getFloat(resources,
122                 com.android.internal.R.dimen.config_displayWhiteBalanceBrightnessFilterIntercept);
123         if (!Float.isNaN(intercept)) {
124             return new AmbientFilter.WeightedMovingAverageAmbientFilter(
125                     BRIGHTNESS_FILTER_TAG, horizon, intercept);
126         }
127         throw new IllegalArgumentException("missing configurations: "
128                 + "expected config_displayWhiteBalanceBrightnessFilterIntercept");
129     }
130 
131     /**
132      * Creates an ambient color sensor instance to redirect sensor data to callbacks.
133      */
134     @VisibleForTesting
createColorTemperatureSensor( Handler handler, SensorManager sensorManager, Resources resources)135     public static AmbientSensor.AmbientColorTemperatureSensor createColorTemperatureSensor(
136             Handler handler, SensorManager sensorManager, Resources resources) {
137         final String name = resources.getString(
138                 com.android.internal.R.string
139                 .config_displayWhiteBalanceColorTemperatureSensorName);
140         final int rate = resources.getInteger(
141                 com.android.internal.R.integer
142                 .config_displayWhiteBalanceColorTemperatureSensorRate);
143         return new AmbientSensor.AmbientColorTemperatureSensor(handler, sensorManager, name, rate);
144     }
145 
createColorTemperatureFilter(Resources resources)146     private static AmbientFilter createColorTemperatureFilter(Resources resources) {
147         final int horizon = resources.getInteger(
148                 com.android.internal.R.integer
149                 .config_displayWhiteBalanceColorTemperatureFilterHorizon);
150         final float intercept = getFloat(resources,
151                 com.android.internal.R.dimen
152                 .config_displayWhiteBalanceColorTemperatureFilterIntercept);
153         if (!Float.isNaN(intercept)) {
154             return new AmbientFilter.WeightedMovingAverageAmbientFilter(
155                     COLOR_TEMPERATURE_FILTER_TAG, horizon, intercept);
156         }
157         throw new IllegalArgumentException("missing configurations: "
158                 + "expected config_displayWhiteBalanceColorTemperatureFilterIntercept");
159     }
160 
createThrottler(Resources resources)161     private static DisplayWhiteBalanceThrottler createThrottler(Resources resources) {
162         final int increaseDebounce = resources.getInteger(
163                 com.android.internal.R.integer.config_displayWhiteBalanceDecreaseDebounce);
164         final int decreaseDebounce = resources.getInteger(
165                 com.android.internal.R.integer.config_displayWhiteBalanceIncreaseDebounce);
166         final float[] baseThresholds = getFloatArray(resources,
167                 com.android.internal.R.array.config_displayWhiteBalanceBaseThresholds);
168         final float[] increaseThresholds = getFloatArray(resources,
169                 com.android.internal.R.array.config_displayWhiteBalanceIncreaseThresholds);
170         final float[] decreaseThresholds = getFloatArray(resources,
171                 com.android.internal.R.array.config_displayWhiteBalanceDecreaseThresholds);
172         return new DisplayWhiteBalanceThrottler(increaseDebounce, decreaseDebounce, baseThresholds,
173                 increaseThresholds, decreaseThresholds);
174     }
175 
getFloat(Resources resources, int id)176     private static float getFloat(Resources resources, int id) {
177         TypedValue value = new TypedValue();
178         resources.getValue(id, value, true /* resolveRefs */);
179         if (value.type != TypedValue.TYPE_FLOAT) {
180             return Float.NaN;
181         }
182         return value.getFloat();
183     }
184 
getFloatArray(Resources resources, int id)185     private static float[] getFloatArray(Resources resources, int id) {
186         TypedArray array = resources.obtainTypedArray(id);
187         try {
188             if (array.length() == 0) {
189                 return null;
190             }
191             float[] values = new float[array.length()];
192             for (int i = 0; i < values.length; i++) {
193                 values[i] = array.getFloat(i, Float.NaN);
194                 if (Float.isNaN(values[i])) {
195                     return null;
196                 }
197             }
198             return values;
199         } finally {
200             array.recycle();
201         }
202     }
203 
204 }
205