Merge lp:~sforshee/powerd/autobrightness into lp:powerd

Proposed by Seth Forshee
Status: Merged
Approved by: Michael Frey
Approved revision: 99
Merged at revision: 87
Proposed branch: lp:~sforshee/powerd/autobrightness
Merge into: lp:powerd
Diff against target: 1762 lines (+1467/-17)
16 files modified
CMakeLists.txt (+13/-0)
data/device_configs/config-default.xml (+69/-0)
data/device_configs/config-grouper.xml (+75/-0)
data/device_configs/config-maguro.xml (+88/-0)
data/device_configs/config-mako.xml (+75/-0)
data/device_configs/config-manta.xml (+74/-0)
debian/control (+1/-0)
src/autobrightness.c (+357/-0)
src/device-config.c (+407/-0)
src/device-config.h (+34/-0)
src/display.c (+57/-16)
src/powerd-internal.h (+9/-0)
src/powerd-sensors.cpp (+31/-1)
src/powerd.cpp (+9/-0)
src/spline.c (+140/-0)
src/spline.h (+28/-0)
To merge this branch: bzr merge lp:~sforshee/powerd/autobrightness
Reviewer Review Type Date Requested Status
Michael Frey (community) Approve
PS Jenkins bot continuous-integration Approve
Review via email: mp+182729@code.launchpad.net

Commit message

Add support for automatically adjusting the display backlight in response to ambient brightness

Description of the change

Add support for automatically adjusting the display backlight in response to ambient brightness

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Michael Frey (mfrey) wrote :

tested and works nicely.

Comment.

Please rename "android-config" to "device-config"
I don't think the config is tied to android.

review: Needs Fixing
Revision history for this message
Seth Forshee (sforshee) wrote :

It _is_ using android config files to get the data, but it doesn't matter much to me. I have the name changed locally but can't build due to package dependency problems in my pbuilder, so I'll repush as soon as someone gets that fixed.

lp:~sforshee/powerd/autobrightness updated
97. By Seth Forshee

s/android_config/device_config/

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
lp:~sforshee/powerd/autobrightness updated
98. By Seth Forshee

Use tuna config file for maguro

It turns out that in Android maguro has two device configuration
overlay files, one under device/samsung/maguro and the other
under device/samsung/tuna. The tuna file is common to all Galaxy
Nexus devices.

For powerd's use the tuna file has all the information we really
care about, so just use that one for maguro.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
lp:~sforshee/powerd/autobrightness updated
99. By Seth Forshee

Strip down device configurations

Remove all variables except those which powerd is already using
or is likely to use soon.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Michael Frey (mfrey) wrote :

ok, works great.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2013-08-09 19:53:58 +0000
3+++ CMakeLists.txt 2013-08-29 21:23:26 +0000
4@@ -33,11 +33,17 @@
5 )
6 SET_SOURCE_FILES_PROPERTIES(${CMAKE_CURRENT_BINARY_DIR}/src/${POWERD_GENERATED_SOURCES} PROPERTIES GENERATED 1)
7
8+set(POWERD_DEVICE_CONFIGS_PATH "${CMAKE_INSTALL_PREFIX}/share/powerd/device_configs")
9+FILE(GLOB device_config_files "${CMAKE_CURRENT_SOURCE_DIR}/data/device_configs/*.xml")
10+add_definitions(-DPOWERD_DEVICE_CONFIGS_PATH=\"${POWERD_DEVICE_CONFIGS_PATH}\")
11+
12 set(
13 SRCS
14
15 src/powerd.cpp
16+ src/autobrightness.c
17 src/backlight.c
18+ src/device-config.c
19 src/display.c
20 src/display-request.c
21 src/log.c
22@@ -46,6 +52,7 @@
23 src/powerd-client.c
24 src/powerd-object.c
25 src/powerd-sensors.cpp
26+ src/spline.c
27 src/stats.c
28 src/util.c
29 src/${GDBUS_NAME}.c
30@@ -104,6 +111,7 @@
31 ${GIO-UNIX_LIBRARIES}
32 ${PHABLET_LIBRARIES}
33 "-lubuntu_application_api"
34+ "-landroid-properties"
35 ${LIBUDEV_LIBRARIES}
36 ${UPOWER_GLIB_LIBRARIES}
37 ${UUID_LIBRARIES}
38@@ -115,3 +123,8 @@
39 LIBRARY DESTINATION lib
40 ARCHIVE DESTINATION lib/static
41 )
42+
43+install(
44+ FILES ${device_config_files}
45+ DESTINATION ${POWERD_DEVICE_CONFIGS_PATH}
46+)
47
48=== added directory 'data/device_configs'
49=== added file 'data/device_configs/config-default.xml'
50--- data/device_configs/config-default.xml 1970-01-01 00:00:00 +0000
51+++ data/device_configs/config-default.xml 2013-08-29 21:23:26 +0000
52@@ -0,0 +1,69 @@
53+<?xml version="1.0" encoding="utf-8"?>
54+<!--
55+/*
56+** Copyright 2009, The Android Open Source Project
57+**
58+** Licensed under the Apache License, Version 2.0 (the "License");
59+** you may not use this file except in compliance with the License.
60+** You may obtain a copy of the License at
61+**
62+** http://www.apache.org/licenses/LICENSE-2.0
63+**
64+** Unless required by applicable law or agreed to in writing, software
65+** distributed under the License is distributed on an "AS IS" BASIS,
66+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
67+** See the License for the specific language governing permissions and
68+** limitations under the License.
69+*/
70+-->
71+
72+<!-- These resources are around just to allow their values to be customized
73+ for different hardware and product builds. Do not translate. -->
74+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
75+ <!-- Flag indicating whether the we should enable the automatic brightness in Settings.
76+ Software implementation will be used if config_hardware_auto_brightness_available is not set -->
77+ <bool name="config_automatic_brightness_available">false</bool>
78+
79+ <!-- Display low battery warning when battery level dips to this value.
80+ Also, the battery stats are flushed to disk when we hit this level. -->
81+ <integer name="config_criticalBatteryWarningLevel">4</integer>
82+
83+ <!-- Shutdown if the battery temperature exceeds (this value * 0.1) Celsius. -->
84+ <integer name="config_shutdownBatteryTemperature">680</integer>
85+
86+ <!-- Array of light sensor LUX values to define our levels for auto backlight brightness support.
87+ The N entries of this array define N + 1 control points as follows:
88+ (1-based arrays)
89+
90+ Point 1: (0, value[1]): lux <= 0
91+ Point 2: (level[1], value[2]): 0 < lux <= level[1]
92+ Point 3: (level[2], value[3]): level[2] < lux <= level[3]
93+ ...
94+ Point N+1: (level[N], value[N+1]): level[N] < lux
95+
96+ The control points must be strictly increasing. Each control point
97+ corresponds to an entry in the brightness backlight values arrays.
98+ For example, if LUX == level[1] (first element of the levels array)
99+ then the brightness will be determined by value[2] (second element
100+ of the brightness values array).
101+
102+ Spline interpolation is used to determine the auto-brightness
103+ backlight values for LUX levels between these control points.
104+
105+ Must be overridden in platform specific overlays -->
106+ <integer-array name="config_autoBrightnessLevels">
107+ </integer-array>
108+
109+ <!-- Screen brightness used to dim the screen when the user activity
110+ timeout expires. May be less than the minimum allowed brightness setting
111+ that can be set by the user. -->
112+ <integer name="config_screenBrightnessDim">10</integer>
113+
114+ <!-- Array of output values for LCD backlight corresponding to the LUX values
115+ in the config_autoBrightnessLevels array. This array should have size one greater
116+ than the size of the config_autoBrightnessLevels array.
117+ The brightness values must be between 0 and 255 and be non-decreasing.
118+ This must be overridden in platform specific overlays -->
119+ <integer-array name="config_autoBrightnessLcdBacklightValues">
120+ </integer-array>
121+</resources>
122
123=== added file 'data/device_configs/config-grouper.xml'
124--- data/device_configs/config-grouper.xml 1970-01-01 00:00:00 +0000
125+++ data/device_configs/config-grouper.xml 2013-08-29 21:23:26 +0000
126@@ -0,0 +1,75 @@
127+<?xml version="1.0" encoding="utf-8"?>
128+<!--
129+/*
130+** Copyright 2009, The Android Open Source Project
131+**
132+** Licensed under the Apache License, Version 2.0 (the "License");
133+** you may not use this file except in compliance with the License.
134+** You may obtain a copy of the License at
135+**
136+** http://www.apache.org/licenses/LICENSE-2.0
137+**
138+** Unless required by applicable law or agreed to in writing, software
139+** distributed under the License is distributed on an "AS IS" BASIS,
140+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
141+** See the License for the specific language governing permissions and
142+** limitations under the License.
143+*/
144+-->
145+
146+<!-- These resources are around just to allow their values to be customized
147+ for different hardware and product builds. -->
148+<resources>
149+ <!-- Flag indicating whether the we should enable the automatic brightness in Settings.
150+ Software implementation will be used if config_hardware_auto_brightness_available is not set -->
151+ <bool name="config_automatic_brightness_available">true</bool>
152+
153+ <!-- Array of light sensor LUX values to define our levels for auto backlight brightness support.
154+ The N entries of this array define N 1 zones as follows:
155+
156+ Zone 0: 0 <= LUX < array[0]
157+ Zone 1: array[0] <= LUX < array[1]
158+ ...
159+ Zone N: array[N - 1] <= LUX < array[N]
160+ Zone N + 1 array[N] <= LUX < infinity
161+
162+ Must be overridden in platform specific overlays -->
163+ <integer-array name="config_autoBrightnessLevels">
164+ <item>5</item>
165+ <item>15</item>
166+ <item>50</item>
167+ <item>100</item>
168+ <item>200</item>
169+ <item>400</item>
170+ <item>1000</item>
171+ <item>2000</item>
172+ <item>3000</item>
173+ <item>5000</item>
174+ <item>10000</item>
175+ <item>30000</item>
176+ </integer-array>
177+
178+
179+ <!-- Array of output values for LCD backlight corresponding to the LUX values
180+ in the config_autoBrightnessLevels array. This array should have size one greater
181+ than the size of the config_autoBrightnessLevels array.
182+ This must be overridden in platform specific overlays -->
183+ <integer-array name="config_autoBrightnessLcdBacklightValues">
184+ <item>5</item> <!-- 0-5 -->
185+ <item>20</item> <!-- 5-15 -->
186+ <item>30</item> <!-- 15-50 -->
187+ <item>40</item> <!-- 50-100 -->
188+ <item>50</item> <!-- 100-200 -->
189+ <item>60</item> <!-- 200-400 -->
190+ <item>70</item> <!-- 400-1000 -->
191+ <item>80</item> <!-- 1000-2000 -->
192+ <item>130</item> <!-- 2000-3000 -->
193+ <item>180</item> <!-- 3000-5000 -->
194+ <item>255</item> <!-- 5000-10000 -->
195+ <item>255</item> <!-- 10000-30000 -->
196+ <item>255</item> <!-- 30000+ -->
197+ </integer-array>
198+
199+ <!-- Minimum screen brightness allowed by the power manager. -->
200+ <integer name="config_screenBrightnessDim">5</integer>
201+</resources>
202
203=== added file 'data/device_configs/config-maguro.xml'
204--- data/device_configs/config-maguro.xml 1970-01-01 00:00:00 +0000
205+++ data/device_configs/config-maguro.xml 2013-08-29 21:23:26 +0000
206@@ -0,0 +1,88 @@
207+<?xml version="1.0" encoding="utf-8"?>
208+<!--
209+/*
210+** Copyright 2011, The Android Open Source Project
211+**
212+** Licensed under the Apache License, Version 2.0 (the "License");
213+** you may not use this file except in compliance with the License.
214+** You may obtain a copy of the License at
215+**
216+** http://www.apache.org/licenses/LICENSE-2.0
217+**
218+** Unless required by applicable law or agreed to in writing, software
219+** distributed under the License is distributed on an "AS IS" BASIS,
220+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
221+** See the License for the specific language governing permissions and
222+** limitations under the License.
223+*/
224+-->
225+
226+<!-- These resources are around just to allow their values to be customized
227+ for different hardware and product builds. -->
228+<resources>
229+ <!-- Flag indicating whether we should enable the automatic brightness in Settings.
230+ config_hardware_automatic_brightness_available is not set, so we will use software implementation -->
231+ <bool name="config_automatic_brightness_available">true</bool>
232+
233+ <!-- Array of light sensor LUX values to define our levels for auto backlight brightness support.
234+ The N entries of this array define N + 1 zones as follows:
235+
236+ Zone 0: 0 <= LUX < array[0]
237+ Zone 1: array[0] <= LUX < array[1]
238+ ...
239+ Zone N: array[N - 1] <= LUX < array[N]
240+ Zone N + 1: array[N] <= LUX < infinity
241+
242+ Must be overridden in platform specific overlays -->
243+ <integer-array name="config_autoBrightnessLevels">
244+ <item>6</item>
245+ <item>9</item>
246+ <item>14</item>
247+ <item>20</item>
248+ <item>30</item>
249+ <item>46</item>
250+ <item>68</item>
251+ <item>103</item>
252+ <item>154</item>
253+ <item>231</item>
254+ <item>346</item>
255+ <item>519</item>
256+ <item>778</item>
257+ <item>1168</item>
258+ <item>1752</item>
259+ <item>2627</item>
260+ <item>3941</item>
261+ <item>5912</item>
262+ <item>8867</item>
263+ </integer-array>
264+
265+ <!-- Array of output values for LCD backlight corresponding to the LUX values
266+ in the config_autoBrightnessLevels array. This array should have size one greater
267+ than the size of the config_autoBrightnessLevels array.
268+ -->
269+ <integer-array name="config_autoBrightnessLcdBacklightValues">
270+ <item>19</item>
271+ <item>23</item>
272+ <item>26</item>
273+ <item>30</item>
274+ <item>34</item>
275+ <item>39</item>
276+ <item>45</item>
277+ <item>51</item>
278+ <item>59</item>
279+ <item>67</item>
280+ <item>77</item>
281+ <item>88</item>
282+ <item>101</item>
283+ <item>116</item>
284+ <item>133</item>
285+ <item>152</item>
286+ <item>174</item>
287+ <item>199</item>
288+ <item>228</item>
289+ <item>250</item>
290+ </integer-array>
291+
292+ <!-- Minimum screen brightness allowed by the power manager. -->
293+ <integer name="config_screenBrightnessDim">10</integer>
294+</resources>
295
296=== added file 'data/device_configs/config-mako.xml'
297--- data/device_configs/config-mako.xml 1970-01-01 00:00:00 +0000
298+++ data/device_configs/config-mako.xml 2013-08-29 21:23:26 +0000
299@@ -0,0 +1,75 @@
300+<?xml version="1.0" encoding="utf-8"?>
301+<!--
302+/*
303+** Copyright 2012, The Android Open Source Project
304+**
305+** Licensed under the Apache License, Version 2.0 (the "License");
306+** you may not use this file except in compliance with the License.
307+** You may obtain a copy of the License at
308+**
309+** http://www.apache.org/licenses/LICENSE-2.0
310+**
311+** Unless required by applicable law or agreed to in writing, software
312+** distributed under the License is distributed on an "AS IS" BASIS,
313+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
314+** See the License for the specific language governing permissions and
315+** limitations under the License.
316+*/
317+-->
318+
319+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
320+
321+ <!-- Flag indicating whether the we should enable the automatic brightness in Settings.
322+ Software implementation will be used if config_hardware_auto_brightness_available is not set -->
323+ <bool name="config_automatic_brightness_available">true</bool>
324+
325+ <!-- Array of light sensor LUX values to define our levels for auto backlight brightness support.
326+ The N entries of this array define N 1 zones as follows:
327+
328+ Zone 0: 0 <= LUX < array[0]
329+ Zone 1: array[0] <= LUX < array[1]
330+ ...
331+ Zone N: array[N - 1] <= LUX < array[N]
332+ Zone N + 1 array[N] <= LUX < infinity
333+
334+ Must be overridden in platform specific overlays -->
335+ <integer-array name="config_autoBrightnessLevels">
336+ <item>10</item>
337+ <item>50</item>
338+ <item>100</item>
339+ <item>200</item>
340+ <item>400</item>
341+ <item>500</item>
342+ <item>800</item>
343+ <item>1000</item>
344+ <item>1600</item>
345+ <item>3000</item>
346+ <item>10000</item>
347+ </integer-array>
348+
349+ <!-- Array of output values for LCD backlight corresponding to the LUX values
350+ in the config_autoBrightnessLevels array. This array should have size one greater
351+ than the size of the config_autoBrightnessLevels array.
352+ This must be overridden in platform specific overlays -->
353+ <integer-array name="config_autoBrightnessLcdBacklightValues">
354+ <item>14</item> <!-- 0-10 -->
355+ <item>28</item> <!-- 10-50 -->
356+ <item>37</item> <!-- 50-100 -->
357+ <item>51</item> <!-- 100-200 -->
358+ <item>71</item> <!-- 200-400 -->
359+ <item>80</item> <!-- 400-500 -->
360+ <item>96</item> <!-- 500-800 -->
361+ <item>108</item> <!-- 800-1000 -->
362+ <item>144</item> <!-- 1000-1600 -->
363+ <item>181</item> <!-- 1600-3000 -->
364+ <item>254</item> <!-- 3000-10000 -->
365+ <item>255</item> <!-- 10000+ -->
366+ </integer-array>
367+
368+ <!-- Minimum screen brightness allowed by the power manager. -->
369+ <integer name="config_screenBrightnessDim">1</integer>
370+
371+ <!-- Shutdown if the battery temperature exceeds (this value * 0.1) Celsius. -->
372+ <integer name="config_shutdownBatteryTemperature">600</integer>
373+
374+</resources>
375
376=== added file 'data/device_configs/config-manta.xml'
377--- data/device_configs/config-manta.xml 1970-01-01 00:00:00 +0000
378+++ data/device_configs/config-manta.xml 2013-08-29 21:23:26 +0000
379@@ -0,0 +1,74 @@
380+<?xml version="1.0" encoding="utf-8"?>
381+<!--
382+/*
383+** Copyright 2009, The Android Open Source Project
384+**
385+** Licensed under the Apache License, Version 2.0 (the "License");
386+** you may not use this file except in compliance with the License.
387+** You may obtain a copy of the License at
388+**
389+** http://www.apache.org/licenses/LICENSE-2.0
390+**
391+** Unless required by applicable law or agreed to in writing, software
392+** distributed under the License is distributed on an "AS IS" BASIS,
393+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
394+** See the License for the specific language governing permissions and
395+** limitations under the License.
396+*/
397+-->
398+
399+<!-- These resources are around just to allow their values to be customized
400+ for different hardware and product builds. -->
401+<resources>
402+ <!-- Flag indicating whether we should enable the automatic brightness in Settings.
403+ config_hardware_automatic_brightness_available is not set, so we will use software implementation -->
404+ <bool name="config_automatic_brightness_available">true</bool>
405+
406+ <!-- Array of light sensor LUX values to define our levels for auto backlight brightness support.
407+ The N entries of this array define N + 1 control points as follows:
408+ (1-based arrays)
409+
410+ Point 1: (0, value[1]): lux <= 0
411+ Point 2: (level[1], value[2]): 0 < lux <= level[1]
412+ Point 3: (level[2], value[3]): level[2] < lux <= level[3]
413+ ...
414+ Point N+1: (level[N], value[N+1]): level[N] < lux
415+
416+ The control points must be strictly increasing. Each control point
417+ corresponds to an entry in the brightness backlight values arrays.
418+ For example, if LUX == level[1] (first element of the levels array)
419+ then the brightness will be determined by value[2] (second element
420+ of the brightness values array).
421+
422+ Spline interpolation is used to determine the auto-brightness
423+ backlight values for LUX levels between these control points.
424+
425+ Must be overridden in platform specific overlays -->
426+ <integer-array name="config_autoBrightnessLevels">
427+ <item>10</item> <!-- Pitch black 0, and 9 is next possible value. -->
428+ <item>30</item> <!-- Sensor in dark office: 60, well lit: 220 -->
429+ <item>90</item> <!-- Sensor in dark office: 60, well lit: 220 -->
430+ <item>15000</item> <!-- Screen under direct sunlight, but not sensor. -->
431+ <item>225000</item> <!-- Sensor under direct sunlight. -->
432+ </integer-array>
433+
434+ <!-- Array of output values for LCD backlight corresponding to the LUX values
435+ in the config_autoBrightnessLevels array. This array should have size one greater
436+ than the size of the config_autoBrightnessLevels array.
437+ The brightness values must be between 0 and 255 and be non-decreasing.
438+ This must be overridden in platform specific overlays -->
439+ <integer-array name="config_autoBrightnessLcdBacklightValues">
440+ <item>10</item>
441+ <item>13</item>
442+ <item>65</item>
443+ <item>85</item>
444+ <item>220</item>
445+ <item>255</item>
446+ </integer-array>
447+
448+ <!-- Screen brightness used to dim the screen when the user activity
449+ timeout expires. May be less than the minimum allowed brightness setting
450+ that can be set by the user. -->
451+ <integer name="config_screenBrightnessDim">2</integer>
452+
453+</resources>
454
455=== modified file 'debian/control'
456--- debian/control 2013-08-03 08:21:09 +0000
457+++ debian/control 2013-08-29 21:23:26 +0000
458@@ -8,6 +8,7 @@
459 libhybris-dev,
460 libudev-dev,
461 python,
462+ libandroid-properties-dev,
463 libplatform-api1-dev,
464 libupower-glib-dev,
465 uuid-dev,
466
467=== added file 'src/autobrightness.c'
468--- src/autobrightness.c 1970-01-01 00:00:00 +0000
469+++ src/autobrightness.c 2013-08-29 21:23:26 +0000
470@@ -0,0 +1,357 @@
471+/*
472+ * Copyright 2013 Canonical Ltd.
473+ *
474+ * This file is part of powerd.
475+ *
476+ * powerd is free software; you can redistribute it and/or modify
477+ * it under the terms of the GNU General Public License as published by
478+ * the Free Software Foundation; version 3.
479+ *
480+ * powerd is distributed in the hope that it will be useful,
481+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
482+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
483+ * GNU General Public License for more details.
484+ *
485+ * You should have received a copy of the GNU General Public License
486+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
487+ */
488+
489+#include <stdlib.h>
490+#include <errno.h>
491+#include <math.h>
492+#include <glib.h>
493+
494+#include "powerd-internal.h"
495+#include "device-config.h"
496+#include "log.h"
497+#include "spline.h"
498+
499+/*
500+ * Autobrightness Control Algorithm
501+ *
502+ * Automatic backlight adjustments should respond quickly to changes in
503+ * the ambient brightness without constantly adjusting for minor or
504+ * transient changes in ambient brightness. The following mechanisms are
505+ * employed to maintain this balance.
506+ *
507+ * EXPONENTIAL SMOOTHING
508+ *
509+ * Rather than processing incoming lux values directly, an exponential
510+ * moving average is calculated using an algorithm based off of the
511+ * formulation known as "Brown's simple exponential smoothing" [1]:
512+ *
513+ * S(n) = a * x(n) + (1 - a) * S(n - 1)
514+ * = S(n - 1) + a * (x(n) - S(n - 1))
515+ *
516+ * where 0 <= a <= 1. To make smoothing time-based, a is calculated
517+ * based on the amount of time spent at a given lux value.
518+ *
519+ * delta_t = t(n) - t(n - 1)
520+ * a(n) = delta_t / (C + delta_t)
521+ *
522+ * This gives decreasing weight to older samples. Smaller values of C
523+ * will cause faster decay of old samples, giving a faster response
524+ * time, while larger values of C create slower decay and give more
525+ * filtering of transient events.
526+ *
527+ * This code usese two moving averages, one which responds slowly to
528+ * changes and another which responds quickly. The slow average is used
529+ * to detect the trend with less sensitivity to transient events, while
530+ * the fast average is used for picking the brightness once we've
531+ * decided to change it.
532+ *
533+ * HYSTERESIS
534+ *
535+ * To avoid making constant adjustments to screen brightness in response
536+ * to small changes in ambient lighting conditions, the change in lux is
537+ * required to exceed some threshold before changing screen brightness.
538+ *
539+ * DEBOUNCING
540+ *
541+ * Changes in ambient brightness which exceed the hysteresis threshold
542+ * must do so for some minimum amount of time before any changes in
543+ * screen brightness will be made. This "debounces" the incoming lux
544+ * values and also helps to filter out transient changes in ambient
545+ * conditions.
546+ *
547+ * [1] http://en.wikipedia.org/wiki/Exponential_smoothing
548+ */
549+
550+#define SMOOTHING_FACTOR_SLOW 2000.0f
551+#define SMOOTHING_FACTOR_FAST 200.0f
552+#define HYSTERESIS_FACTOR 0.10f
553+#define DEBOUNCE_MS 4000
554+
555+enum ab_state {
556+ AB_STATE_DISABLED,
557+ AB_STATE_INITIALIZING,
558+ AB_STATE_IDLE, /* Idle, nothing to do */
559+ AB_STATE_DEBOUNCE, /* Debouncing lux change */
560+};
561+
562+struct ab_status {
563+ enum ab_state state;
564+ guint source_id;
565+ double last_lux, applied_lux;
566+ double average_slow, average_fast;
567+ gint64 last_lux_ms;
568+ struct ab_spline *spline;
569+};
570+
571+static gboolean ab_supported = FALSE;
572+static struct ab_status ab_status;
573+struct spline *ab_spline;
574+
575+static gint64 get_ms(void)
576+{
577+ return g_get_monotonic_time() / 1000;
578+}
579+
580+static double aggregate_lux(double old_average, double new_lux,
581+ double smoothing_factor, double time_delta)
582+{
583+ double alpha;
584+ if (time_delta < 0.0f)
585+ time_delta = 0.0f;
586+ alpha = time_delta / (smoothing_factor + time_delta);
587+ return old_average + alpha * (new_lux - old_average);
588+}
589+
590+static enum ab_state process_state_debounce(guint *delay_ms)
591+{
592+ gint64 now;
593+ double time_delta;
594+ double slow_lux, fast_lux;
595+ double hysteresis, slow_delta, fast_delta;
596+ enum ab_state next;
597+
598+ next = AB_STATE_IDLE;
599+ *delay_ms = 0;
600+
601+ now = get_ms();
602+ time_delta = (double)(now - ab_status.last_lux_ms);
603+ slow_lux = aggregate_lux(ab_status.average_slow, ab_status.last_lux,
604+ SMOOTHING_FACTOR_SLOW, time_delta);
605+ fast_lux = aggregate_lux(ab_status.average_fast, ab_status.last_lux,
606+ SMOOTHING_FACTOR_FAST, time_delta);
607+ powerd_debug("%lld slow avg %f fast avg %f last %f",
608+ now, slow_lux, fast_lux, ab_status.last_lux);
609+
610+ /*
611+ * Require that both slow and fast averages exceed hysteresis,
612+ * and in the same direction.
613+ */
614+ hysteresis = ab_status.applied_lux * HYSTERESIS_FACTOR;
615+ if (hysteresis < 2)
616+ hysteresis = 2;
617+ slow_delta = slow_lux - ab_status.applied_lux;
618+ fast_delta = fast_lux - ab_status.applied_lux;
619+ if ((slow_delta >= hysteresis && fast_delta >= hysteresis) ||
620+ (-slow_delta >= hysteresis && -fast_delta >= hysteresis)) {
621+ int brightness = (int)(spline_interpolate(ab_spline, fast_lux) + 0.5);
622+ powerd_debug("set brightness %d", brightness);
623+ powerd_set_brightness(brightness);
624+ ab_status.applied_lux = fast_lux;
625+ }
626+
627+ hysteresis = ab_status.last_lux * HYSTERESIS_FACTOR;
628+ if (hysteresis < 2)
629+ hysteresis = 2;
630+ if (fabs(fast_lux - ab_status.last_lux) >= hysteresis) {
631+ /*
632+ * Average should settle near the last reported lux. If
633+ * it hasn't made it there yet, continue debouncing.
634+ */
635+ next = AB_STATE_DEBOUNCE;
636+ *delay_ms = DEBOUNCE_MS;
637+ }
638+
639+ return next;
640+}
641+
642+static gboolean ab_process(gpointer unused)
643+{
644+ guint delay = 0;
645+
646+ ab_status.source_id = 0;
647+
648+ switch (ab_status.state) {
649+ case AB_STATE_DISABLED:
650+ case AB_STATE_INITIALIZING:
651+ case AB_STATE_IDLE:
652+ /* Nothing to do */
653+ break;
654+ case AB_STATE_DEBOUNCE:
655+ ab_status.state = process_state_debounce(&delay);
656+ break;
657+ default:
658+ powerd_warn("Unexpected autobrightness state %d\n", ab_status.state);
659+ ab_status.state = AB_STATE_IDLE;
660+ break;
661+ }
662+
663+ if (delay != 0 && ab_status.state != AB_STATE_IDLE)
664+ ab_status.source_id = g_timeout_add(delay, ab_process, NULL);
665+ return FALSE;
666+}
667+
668+static gboolean handle_new_lux(gpointer data)
669+{
670+ gint64 now;
671+ double lux;
672+
673+ if (!ab_supported)
674+ return FALSE;
675+
676+ now = get_ms();
677+ lux = *(double *)data;
678+ g_free(data);
679+
680+ if (ab_status.state == AB_STATE_DISABLED)
681+ return FALSE;
682+
683+ if (ab_status.state == AB_STATE_INITIALIZING) {
684+ int brightness = (int)(spline_interpolate(ab_spline, lux) + 0.5);
685+ powerd_debug("set brightness %d", brightness);
686+ powerd_set_brightness(brightness);
687+
688+ ab_status.average_slow = lux;
689+ ab_status.average_fast = lux;
690+ ab_status.applied_lux = lux;
691+ ab_status.state = AB_STATE_IDLE;
692+ } else {
693+ double time_delta;
694+ /* Ignore duplicates */
695+ if (lux == ab_status.last_lux)
696+ return FALSE;
697+ time_delta = (double)(now - ab_status.last_lux_ms);
698+ ab_status.average_slow = aggregate_lux(ab_status.average_slow, lux,
699+ SMOOTHING_FACTOR_SLOW,
700+ time_delta);
701+ ab_status.average_fast = aggregate_lux(ab_status.average_fast, lux,
702+ SMOOTHING_FACTOR_FAST,
703+ time_delta);
704+ }
705+ ab_status.last_lux = lux;
706+ ab_status.last_lux_ms = now;
707+
708+ if (ab_status.state == AB_STATE_IDLE) {
709+ ab_status.state = AB_STATE_DEBOUNCE;
710+ ab_status.source_id = g_timeout_add(DEBOUNCE_MS, ab_process, NULL);
711+ }
712+
713+ return FALSE;
714+}
715+
716+void powerd_new_als_event(double lux)
717+{
718+ double *p_lux = g_memdup(&lux, sizeof(lux));
719+ g_timeout_add(0, handle_new_lux, p_lux);
720+}
721+
722+/* Must be run from main loop */
723+void powerd_autobrightness_enable(void)
724+{
725+ /* Must not leave disabled state if we can't map lux to brightness */
726+ if (!ab_supported)
727+ return;
728+
729+ if (ab_status.state == AB_STATE_DISABLED) {
730+ ab_status.state = AB_STATE_INITIALIZING;
731+ powerd_sensors_als_enable();
732+ }
733+}
734+
735+/* Must be run from main loop */
736+void powerd_autobrightness_disable(void)
737+{
738+ if (ab_status.state != AB_STATE_DISABLED) {
739+ ab_status.state = AB_STATE_DISABLED;
740+ powerd_sensors_als_disable();
741+ if (ab_status.source_id != 0)
742+ g_source_remove(ab_status.source_id);
743+ }
744+}
745+
746+int powerd_autobrightness_init(void)
747+{
748+ GValue v = G_VALUE_INIT;
749+ GArray *levels = NULL, *lux = NULL;
750+ double (*mappings)[2] = NULL;
751+ int i;
752+ int ret = -ENODEV;
753+
754+ if (device_config_get("automatic_brightness_available", &v)) {
755+ powerd_info("Could not determine platform autobrightness capability, disabling");
756+ goto error;
757+ }
758+
759+ if (!G_VALUE_HOLDS_BOOLEAN(&v) || !g_value_get_boolean(&v)) {
760+ powerd_info("Platform does not support autobrightness, disabling");
761+ goto error;
762+ }
763+ g_value_unset(&v);
764+
765+ if (device_config_get("autoBrightnessLevels", &v)) {
766+ powerd_info("No device-specific autobrightness lux table defined, disabling");
767+ goto error;
768+ }
769+ if (G_VALUE_HOLDS_BOXED(&v))
770+ lux = g_value_dup_boxed(&v);
771+ g_value_unset(&v);
772+ if (!lux || lux->len == 0) {
773+ powerd_info("Invalid autobrighness lux levels, disabling");
774+ goto error;
775+ }
776+
777+ if (device_config_get("autoBrightnessLcdBacklightValues", &v)) {
778+ powerd_info("No device-specific autobrightness backlight levels defined, disabling");
779+ goto error;
780+ }
781+ if (G_VALUE_HOLDS_BOXED(&v))
782+ levels = g_value_dup_boxed(&v);
783+ g_value_unset(&v);
784+ if (!levels || levels->len == 0) {
785+ powerd_info("Invalid autobrighness backlight levels, disabling");
786+ goto error;
787+ }
788+
789+ /* We should have one more backlight level than lux values */
790+ if (levels->len != lux->len + 1) {
791+ powerd_info("Invalid lux->brightness mappings, autobrightness disabled");
792+ goto error;
793+ }
794+
795+ mappings = g_malloc(2 * sizeof(double) * levels->len);
796+ for (i = 0; i < levels->len; i++) {
797+ mappings[i][0] = (double)((i == 0) ? 0 : g_array_index(lux, guint, i-1));
798+ mappings[i][1] = (double)g_array_index(levels, guint, i);
799+ }
800+
801+ ab_status.state = AB_STATE_DISABLED;
802+ ab_spline = spline_new(mappings, levels->len);
803+ if (!ab_spline) {
804+ ret = -ENOMEM;
805+ goto error;
806+ }
807+ g_free(mappings);
808+ ab_supported = TRUE;
809+ return 0;
810+
811+error:
812+ if (levels)
813+ g_array_unref(levels);
814+ if (lux)
815+ g_array_unref(lux);
816+ if (mappings)
817+ g_free(mappings);
818+ return ret;
819+}
820+
821+void powerd_autobrightness_deinit(void)
822+{
823+ powerd_autobrightness_disable();
824+ ab_supported = FALSE;
825+ if (ab_spline)
826+ spline_free(ab_spline);
827+}
828
829=== added file 'src/device-config.c'
830--- src/device-config.c 1970-01-01 00:00:00 +0000
831+++ src/device-config.c 2013-08-29 21:23:26 +0000
832@@ -0,0 +1,407 @@
833+/*
834+ * Copyright 2013 Canonical Ltd.
835+ *
836+ * This file is part of powerd.
837+ *
838+ * powerd is free software; you can redistribute it and/or modify
839+ * it under the terms of the GNU General Public License as published by
840+ * the Free Software Foundation; version 3.
841+ *
842+ * powerd is distributed in the hope that it will be useful,
843+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
844+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
845+ * GNU General Public License for more details.
846+ *
847+ * You should have received a copy of the GNU General Public License
848+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
849+ */
850+
851+#include <stdlib.h>
852+#include <string.h>
853+#include <errno.h>
854+#include <sys/types.h>
855+#include <sys/stat.h>
856+#include <fcntl.h>
857+#include <unistd.h>
858+
859+#include <glib.h>
860+#include <glib-object.h>
861+#include <hybris/properties/properties.h>
862+
863+#include "powerd-internal.h"
864+#include "device-config.h"
865+#include "log.h"
866+
867+/*
868+ * Android defines various device-specific parameters in files named
869+ * config.xml. The general structure of the tags is:
870+ *
871+ * <type name="config_foo">...</type>
872+ *
873+ * This will define a config option named "foo" of type "type". Types
874+ * for arrays are included. For example, the following defines an
875+ * array of integers:
876+ *
877+ * <integer-array name="config_myIntArray">
878+ * <item>0</item>
879+ * <item>1</item>
880+ * </integer-array>
881+ *
882+ * In powerd we are only interested in a few specific variables. The
883+ * code here offers fairly general parsing of the config files, but
884+ * handling of only a few specific types is present.
885+ */
886+
887+enum elem_type {
888+ ELEM_TYPE_NONE,
889+ ELEM_TYPE_BOOL,
890+ ELEM_TYPE_INT,
891+ ELEM_TYPE_STRING,
892+ ELEM_TYPE_FRACTION,
893+ ELEM_TYPE_DIMENSION,
894+ ELEM_TYPE_INT_ARRAY,
895+ ELEM_TYPE_STRING_ARRAY,
896+ ELEM_TYPE_ITEM,
897+
898+ NUM_ELEM_TYPES
899+};
900+
901+static const char *elem_strs[NUM_ELEM_TYPES] = {
902+ [ELEM_TYPE_BOOL] = "bool",
903+ [ELEM_TYPE_INT] = "integer",
904+ [ELEM_TYPE_STRING] = "string",
905+ [ELEM_TYPE_FRACTION] = "fraction",
906+ [ELEM_TYPE_DIMENSION] = "dimen",
907+ [ELEM_TYPE_INT_ARRAY] = "integer-array",
908+ [ELEM_TYPE_STRING_ARRAY] = "string-array",
909+ [ELEM_TYPE_ITEM] = "item",
910+};
911+
912+struct parser_state {
913+ enum elem_type type;
914+ gboolean in_item;
915+ gchar *name;
916+ GValue value;
917+};
918+
919+static struct parser_state state = {
920+ .value = G_VALUE_INIT,
921+};
922+static GHashTable *config_hash;
923+
924+static enum elem_type get_elem_type(const gchar *name)
925+{
926+ int i;
927+ for (i = 0; i < NUM_ELEM_TYPES; i++) {
928+ if (elem_strs[i] && !strcmp(name, elem_strs[i]))
929+ break;
930+ }
931+ if (i >= NUM_ELEM_TYPES)
932+ return ELEM_TYPE_NONE;
933+ return i;
934+}
935+
936+static const gchar *get_elem_name(const gchar **attr_names,
937+ const gchar **attr_values)
938+{
939+ int i;
940+ for (i = 0; attr_names[i]; i++) {
941+ if (!strcmp(attr_names[i], "name"))
942+ break;
943+ }
944+ if (!attr_names[i])
945+ return NULL;
946+ return attr_values[i];
947+}
948+
949+static void on_start_elem(GMarkupParseContext *context, const gchar *name,
950+ const gchar **attr_names, const gchar **attr_values,
951+ gpointer user_data, GError **error)
952+{
953+ const gchar *config_name;
954+ enum elem_type type;
955+
956+ type = get_elem_type(name);
957+ if (type == ELEM_TYPE_NONE)
958+ return;
959+ if (type == ELEM_TYPE_ITEM) {
960+ if (state.type == ELEM_TYPE_INT_ARRAY ||
961+ state.type == ELEM_TYPE_STRING_ARRAY)
962+ state.in_item = TRUE;
963+ return;
964+ }
965+ if (state.type != ELEM_TYPE_NONE) {
966+ g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
967+ "Nested elements not supported");
968+ return;
969+ }
970+
971+ config_name = get_elem_name(attr_names, attr_values);
972+ if (!config_name)
973+ return;
974+ /* Strip leading "config_" if present, not all vars have it */
975+ if (!strncmp(config_name, "config_", 7))
976+ config_name += 7;
977+
978+ switch (type) {
979+ case ELEM_TYPE_BOOL:
980+ g_value_init(&state.value, G_TYPE_BOOLEAN);
981+ break;
982+ case ELEM_TYPE_INT:
983+ g_value_init(&state.value, G_TYPE_UINT);
984+ break;
985+ case ELEM_TYPE_INT_ARRAY:
986+ {
987+ GArray *a;
988+ g_value_init(&state.value, G_TYPE_ARRAY);
989+ a = g_array_new(FALSE, FALSE, sizeof(guint32));
990+ g_value_set_boxed(&state.value, a);
991+ break;
992+ }
993+ default:
994+ /* Type not supported at this time */
995+ return;
996+ }
997+
998+ state.type = type;
999+ state.in_item = FALSE;
1000+ state.name = g_strdup(config_name);
1001+}
1002+
1003+static void on_end_elem(GMarkupParseContext *context, const gchar *name,
1004+ gpointer user_data, GError **error)
1005+{
1006+ enum elem_type type;
1007+ GValue *value;
1008+
1009+ if (state.type == ELEM_TYPE_NONE)
1010+ return;
1011+
1012+ type = get_elem_type(name);
1013+ if (type == ELEM_TYPE_NONE)
1014+ return;
1015+ if (type == ELEM_TYPE_ITEM) {
1016+ state.in_item = FALSE;
1017+ return;
1018+ }
1019+
1020+ if (type == state.type) {
1021+ value = g_new0(GValue, 1);
1022+ g_value_init(value, G_VALUE_TYPE(&state.value));
1023+ g_value_copy(&state.value, value);
1024+ g_hash_table_replace(config_hash, state.name, value);
1025+ /* Note: state.name is not freed as it is used for hash table key */
1026+ } else {
1027+ g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
1028+ "Start and end element types don't match");
1029+ g_free(state.name);
1030+ }
1031+
1032+ state.type = ELEM_TYPE_NONE;
1033+ state.name = NULL;
1034+ g_value_unset(&state.value);
1035+}
1036+
1037+/* WARNING: pt_text is not nul-terminated */
1038+static void on_text(GMarkupParseContext *context, const gchar *pt_text,
1039+ gsize text_len, gpointer user_data, GError **error)
1040+{
1041+ gchar *text = g_strndup(pt_text, text_len);
1042+ switch (state.type) {
1043+ case ELEM_TYPE_BOOL:
1044+ {
1045+ gboolean value;
1046+ if (!G_VALUE_HOLDS_BOOLEAN(&state.value)) {
1047+ g_warning("Incorrect GValue type");
1048+ break;
1049+ }
1050+ value = !g_ascii_strcasecmp(text, "true");
1051+ g_value_set_boolean(&state.value, value);
1052+ break;
1053+ }
1054+ case ELEM_TYPE_INT:
1055+ {
1056+ guint value;
1057+ gchar *endp;
1058+ if (!G_VALUE_HOLDS_UINT(&state.value)) {
1059+ g_warning("Incorrect GValue type");
1060+ break;
1061+ }
1062+ value = (guint)g_ascii_strtoll(text, &endp, 0);
1063+ if (endp - text == text_len)
1064+ g_value_set_uint(&state.value, value);
1065+ break;
1066+ }
1067+ case ELEM_TYPE_INT_ARRAY:
1068+ {
1069+ GArray *a;
1070+ guint value;
1071+ gchar *endp;
1072+ if (!state.in_item)
1073+ break;
1074+ if (!G_VALUE_HOLDS_BOXED(&state.value)) {
1075+ g_warning("Incorrect GValue type");
1076+ break;
1077+ }
1078+ value = (guint)g_ascii_strtoll(text, &endp, 0);
1079+ if (endp - text == text_len) {
1080+ a = g_value_get_boxed(&state.value);
1081+ g_array_append_val(a, value);
1082+ }
1083+ }
1084+ break;
1085+ default:
1086+ break;
1087+ }
1088+ g_free(text);
1089+}
1090+
1091+static GMarkupParser parser = {
1092+ .start_element = on_start_elem,
1093+ .end_element = on_end_elem,
1094+ .text = on_text,
1095+};
1096+
1097+static void config_hash_destroy_key(gpointer data)
1098+{
1099+ g_free(data);
1100+}
1101+
1102+static void config_hash_destroy_value(gpointer data)
1103+{
1104+ GValue *v = data;
1105+ if (G_VALUE_HOLDS_BOXED(v)) {
1106+ /* Currently only boxed data is arrays */
1107+ GArray *a = g_value_get_boxed(v);
1108+ g_array_free(a, TRUE);
1109+ }
1110+ g_value_unset(v);
1111+ g_free(v);
1112+}
1113+
1114+static int process_config(const char *fname)
1115+{
1116+ int fd, ret = 0;
1117+ gchar *buf = NULL;
1118+ int buf_len;
1119+ ssize_t len;
1120+ GMarkupParseContext *context = NULL;
1121+ GError *error;
1122+
1123+ fd = open(fname, O_RDONLY);
1124+ if (fd == -1) {
1125+ powerd_warn("Could not open device config %s: %s",
1126+ fname, strerror(errno));
1127+ return -errno;
1128+ }
1129+
1130+ buf_len = (int)sysconf(_SC_PAGESIZE);
1131+ buf = malloc(buf_len);
1132+ if (!buf) {
1133+ ret = -ENOMEM;
1134+ goto out;
1135+ }
1136+
1137+ context = g_markup_parse_context_new(&parser, 0, NULL, NULL);
1138+ do {
1139+ len = read(fd, buf, buf_len);
1140+ if (len == 0)
1141+ break;
1142+ if (len == -1) {
1143+ if (errno == EINTR)
1144+ continue;
1145+ powerd_warn("Error reading %s: %s", fname, strerror(errno));
1146+ ret = -errno;
1147+ break;
1148+ }
1149+
1150+ error = NULL;
1151+ if (!g_markup_parse_context_parse(context, buf, len, &error)) {
1152+ powerd_warn("Failed parsing device config %s\n", fname);
1153+ if (error) {
1154+ powerd_warn("%s", error->message);
1155+ g_error_free(error);
1156+ }
1157+ ret = -EIO;
1158+ }
1159+ } while (!ret);
1160+
1161+out:
1162+ if (context)
1163+ g_markup_parse_context_free(context);
1164+ if (buf)
1165+ free(buf);
1166+ close(fd);
1167+ return ret;
1168+}
1169+
1170+/*
1171+ * Get the device config option specified by @name. The leading
1172+ * "config_" characters should be stripped from the name if present.
1173+ * Returns 0 if the given variable is found.
1174+ *
1175+ * @value should be an initialized GValue. On success, the value of
1176+ * the variable will be copied to the GValue, and the caller is
1177+ * responsible for freeing the memory. E.g.:
1178+ *
1179+ * GValue v = G_VALUE_INIT;
1180+ * if (!device_config_get("myVar", &v)) {
1181+ * do_something_with_value(&v);
1182+ * g_value_unset(&v);
1183+ * }
1184+ */
1185+int device_config_get(const char *name, GValue *value)
1186+{
1187+ GValue *v;
1188+
1189+ if (!config_hash)
1190+ return -ENODEV;
1191+
1192+ v = g_hash_table_lookup(config_hash, name);
1193+ if (!v)
1194+ return -ENOENT;
1195+
1196+ g_value_init(value, G_VALUE_TYPE(v));
1197+ g_value_copy(v, value);
1198+ return 0;
1199+}
1200+
1201+void device_config_init(void)
1202+{
1203+ char device[PROP_VALUE_MAX];
1204+ char *xml_path;
1205+
1206+ if (!property_get("ro.product.device", device, NULL)) {
1207+ powerd_warn("Could not determine device, running without config");
1208+ return;
1209+ }
1210+ powerd_info("Running on %s\n", device);
1211+
1212+ config_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1213+ config_hash_destroy_key,
1214+ config_hash_destroy_value);
1215+
1216+ /*
1217+ * Always start with config-default.xml for defaults, then
1218+ * the device-specific config
1219+ */
1220+ if (process_config(POWERD_DEVICE_CONFIGS_PATH "/config-default.xml"))
1221+ goto error;
1222+ xml_path = g_strdup_printf("%s/config-%s.xml",
1223+ POWERD_DEVICE_CONFIGS_PATH, device);
1224+ if (process_config(xml_path))
1225+ goto error;
1226+ g_free(xml_path);
1227+ return;
1228+
1229+error:
1230+ device_config_deinit();
1231+}
1232+
1233+void device_config_deinit(void)
1234+{
1235+ if (config_hash) {
1236+ g_hash_table_destroy(config_hash);
1237+ config_hash = NULL;
1238+ }
1239+}
1240
1241=== added file 'src/device-config.h'
1242--- src/device-config.h 1970-01-01 00:00:00 +0000
1243+++ src/device-config.h 2013-08-29 21:23:26 +0000
1244@@ -0,0 +1,34 @@
1245+/*
1246+ * Copyright 2013 Canonical Ltd.
1247+ *
1248+ * This file is part of powerd.
1249+ *
1250+ * powerd is free software; you can redistribute it and/or modify
1251+ * it under the terms of the GNU General Public License as published by
1252+ * the Free Software Foundation; version 3.
1253+ *
1254+ * powerd is distributed in the hope that it will be useful,
1255+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1256+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1257+ * GNU General Public License for more details.
1258+ *
1259+ * You should have received a copy of the GNU General Public License
1260+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1261+ */
1262+
1263+#ifndef __DEVICE_CONFIG_H__
1264+#define __DEVICE_CONFIG_H__
1265+
1266+#ifdef __cplusplus
1267+extern "C" {
1268+#endif
1269+
1270+void device_config_init(void);
1271+void device_config_deinit(void);
1272+int device_config_get(const char *name, GValue *value);
1273+
1274+#ifdef __cplusplus
1275+}
1276+#endif
1277+
1278+#endif /* __DEVICE_CONFIG_H__ */
1279
1280=== modified file 'src/display.c'
1281--- src/display.c 2013-07-29 16:05:34 +0000
1282+++ src/display.c 2013-08-29 21:23:26 +0000
1283@@ -36,11 +36,17 @@
1284
1285 #include "powerd.h"
1286 #include "powerd-internal.h"
1287+#include "device-config.h"
1288 #include "log.h"
1289
1290 /* Treat "don't care" display state as off */
1291 #define DISPLAY_STATE_OFF POWERD_DISPLAY_STATE_DONT_CARE
1292
1293+/* Autobrightness only enabled when bright && !disabled */
1294+#define AB_ENABLED_MASK (POWERD_DISPLAY_FLAG_BRIGHT | \
1295+ POWERD_DISPLAY_FLAG_DISABLE_AUTOBRIGHTNESS)
1296+#define AB_ENABLED POWERD_DISPLAY_FLAG_BRIGHT
1297+
1298 static gint saved_brightness = 255;
1299 static gint target_brightness;
1300 static gint dim_brightness;
1301@@ -92,10 +98,18 @@
1302 return display_state_strs[state];
1303 }
1304
1305-static void turn_on_display(void) {
1306+static gboolean ab_enabled(guint32 flags)
1307+{
1308+ return (flags & AB_ENABLED_MASK) == AB_ENABLED;
1309+}
1310+
1311+static void turn_on_display(gboolean autobrightness) {
1312 powerd_debug("turning on display");
1313 sf_unblank(0);
1314- powerd_set_brightness(target_brightness);
1315+ if (autobrightness)
1316+ powerd_autobrightness_enable();
1317+ else
1318+ powerd_set_brightness(target_brightness);
1319 }
1320
1321 gboolean powerd_display_enabled(void)
1322@@ -127,19 +141,25 @@
1323 static void update_display_state(struct powerd_display_request *req)
1324 {
1325 enum powerd_display_state state = req->state;
1326+ gboolean use_ab, using_ab;
1327 int applied_state;
1328 int ret;
1329 int hw_brightness;
1330
1331+ use_ab = ab_enabled(req->flags);
1332+ using_ab = ab_enabled(internal_state.flags);
1333+
1334 hw_brightness = powerd_get_brightness();
1335 if (hw_brightness < 0)
1336 hw_brightness = saved_brightness;
1337
1338- if ((internal_state.flags & POWERD_DISPLAY_FLAG_BRIGHT) &&
1339- (hw_brightness != 0))
1340+ if (!using_ab && hw_brightness != 0 &&
1341+ (internal_state.flags & POWERD_DISPLAY_FLAG_BRIGHT))
1342 saved_brightness = hw_brightness;
1343- target_brightness = get_target_brightness((!!(req->flags & POWERD_DISPLAY_FLAG_BRIGHT)),
1344- hw_brightness);
1345+ if (!use_ab)
1346+ target_brightness = get_target_brightness(
1347+ !!(req->flags & POWERD_DISPLAY_FLAG_BRIGHT),
1348+ hw_brightness);
1349
1350 applied_state = screen_off_overrides ? DISPLAY_STATE_OFF : state;
1351
1352@@ -151,6 +171,8 @@
1353 }
1354
1355 powerd_debug("turning off display");
1356+ if (using_ab)
1357+ powerd_autobrightness_disable();
1358 powerd_set_brightness(0);
1359 sf_blank(0);
1360
1361@@ -179,12 +201,21 @@
1362 * Otherwise we can turn on the screen right away.
1363 */
1364 if (fb_state == FB_AWAKE)
1365- turn_on_display();
1366+ turn_on_display(use_ab);
1367 } else {
1368 /* Only changing brightness */
1369- if ((req->flags & POWERD_DISPLAY_FLAG_BRIGHT) !=
1370- (internal_state.flags & POWERD_DISPLAY_FLAG_BRIGHT))
1371- powerd_set_brightness(target_brightness);
1372+ if (use_ab) {
1373+ if (!using_ab)
1374+ powerd_autobrightness_enable();
1375+ } else {
1376+ if (using_ab) {
1377+ powerd_autobrightness_disable();
1378+ powerd_set_brightness(target_brightness);
1379+ } else if ((req->flags & POWERD_DISPLAY_FLAG_BRIGHT) !=
1380+ (internal_state.flags & POWERD_DISPLAY_FLAG_BRIGHT)) {
1381+ powerd_set_brightness(target_brightness);
1382+ }
1383+ }
1384 }
1385 break;
1386 default:
1387@@ -198,7 +229,6 @@
1388 static void update_flags(guint32 flags)
1389 {
1390 int prox_enabled, internal_prox_enabled;
1391- /* XXX: Handle autobrightness flag */
1392
1393 prox_enabled = (flags & POWERD_DISPLAY_FLAG_USE_PROXIMITY);
1394 internal_prox_enabled = (internal_state.flags & POWERD_DISPLAY_FLAG_USE_PROXIMITY);
1395@@ -323,7 +353,7 @@
1396 fb_state = (enum fb_state)data;
1397 powerd_debug("fb state %s", fb_state == FB_SLEEP ? "sleep" : "awake");
1398 if (fb_state == FB_AWAKE && powerd_display_enabled())
1399- turn_on_display();
1400+ turn_on_display(ab_enabled(internal_state.flags));
1401 return FALSE;
1402 }
1403
1404@@ -350,14 +380,17 @@
1405 int powerd_display_init(void)
1406 {
1407 GError *error;
1408+ GValue v = G_VALUE_INIT;
1409 int brightness, max_brightness;
1410
1411 /*
1412 * Warning: much hand waving follows
1413 *
1414 * Right now there's no coherent system brightness policy, so we
1415- * just do the best we can. For "dim" brightness we just go with
1416- * 5% of maximum. For "bright" brightness, it's more complicated.
1417+ * just do the best we can. For "dim" brightness we try to read
1418+ * the value from the Android config files, otherwise we just go
1419+ * with 5% of maximum. For "bright" brightness, it's more
1420+ * complicated.
1421 *
1422 * Our first preference for the bright value is whatever the screen
1423 * is currently set to. However, the screen could be off or dimmed
1424@@ -365,15 +398,23 @@
1425 * happens.
1426 */
1427
1428+ if (!device_config_get("screenBrightnessDim", &v)) {
1429+ if (G_VALUE_HOLDS_UINT(&v))
1430+ dim_brightness = (int)g_value_get_uint(&v);
1431+ g_value_unset(&v);
1432+ }
1433+
1434 max_brightness = powerd_get_max_brightness();
1435
1436 if (max_brightness <= 0) {
1437 powerd_warn("Could not read maximum brightness, guessing at dim/bright values");
1438- dim_brightness = 5;
1439 saved_brightness = 100;
1440+ if (dim_brightness == 0)
1441+ dim_brightness = 5;
1442 } else {
1443- dim_brightness = max_brightness / 20;
1444 saved_brightness = (3 * max_brightness) / 4;
1445+ if (dim_brightness == 0)
1446+ dim_brightness = max_brightness / 20;
1447 }
1448
1449 brightness = powerd_get_brightness();
1450
1451=== modified file 'src/powerd-internal.h'
1452--- src/powerd-internal.h 2013-08-12 04:45:17 +0000
1453+++ src/powerd-internal.h 2013-08-29 21:23:26 +0000
1454@@ -57,6 +57,13 @@
1455 void powerd_dbus_init_complete(void);
1456 int powerd_is_mainloop(void);
1457
1458+/* Autobrightness functions */
1459+void powerd_new_als_event(double lux);
1460+void powerd_autobrightness_enable(void);
1461+void powerd_autobrightness_disable(void);
1462+int powerd_autobrightness_init(void);
1463+void powerd_autobrightness_deinit(void);
1464+
1465 /* Backlight functions */
1466 int powerd_backlight_init(void);
1467 void powerd_backlight_deinit(void);
1468@@ -147,6 +154,8 @@
1469 /* Sensor functions */
1470 void powerd_sensors_proximity_enable(void);
1471 void powerd_sensors_proximity_disable(void);
1472+void powerd_sensors_als_enable(void);
1473+void powerd_sensors_als_disable(void);
1474
1475 /* Power source functions */
1476 int powerd_ps_init(void);
1477
1478=== modified file 'src/powerd-sensors.cpp'
1479--- src/powerd-sensors.cpp 2013-06-07 23:02:23 +0000
1480+++ src/powerd-sensors.cpp 2013-08-29 21:23:26 +0000
1481@@ -21,10 +21,13 @@
1482
1483 #include "powerd-internal.h"
1484 #include "powerd-sensors.h"
1485+#include "log.h"
1486
1487+#include <ubuntu/application/sensors/light.h>
1488 #include <ubuntu/application/sensors/proximity.h>
1489
1490 UASensorsProximity* prox_sensor;
1491+UASensorsLight* light_sensor;
1492
1493 void on_new_proximity_event(UASProximityEvent *event, void *context)
1494 {
1495@@ -53,13 +56,40 @@
1496 ua_sensors_proximity_disable(prox_sensor);
1497 }
1498
1499+void on_new_als_event(UASLightEvent *event, void *context)
1500+{
1501+ float lux = uas_light_event_get_light(event);
1502+ powerd_new_als_event(lux);
1503+}
1504+
1505+void powerd_sensors_als_enable(void)
1506+{
1507+ if (light_sensor)
1508+ ua_sensors_light_enable(light_sensor);
1509+}
1510+
1511+void powerd_sensors_als_disable(void)
1512+{
1513+ if (light_sensor)
1514+ ua_sensors_light_disable(light_sensor);
1515+}
1516+
1517 void powerd_sensors_init(void)
1518 {
1519 prox_sensor = ua_sensors_proximity_new();
1520-
1521 if (prox_sensor != NULL) {
1522 ua_sensors_proximity_set_reading_cb(prox_sensor,
1523 on_new_proximity_event,
1524 NULL);
1525+ } else {
1526+ powerd_warn("Failed to allocate proximity sensor");
1527+ }
1528+
1529+ light_sensor = ua_sensors_light_new();
1530+ if (light_sensor) {
1531+ ua_sensors_light_set_reading_cb(light_sensor, on_new_als_event,
1532+ NULL);
1533+ } else {
1534+ powerd_warn("Failed to allocate ambient light sensor");
1535 }
1536 }
1537
1538=== modified file 'src/powerd.cpp'
1539--- src/powerd.cpp 2013-08-09 19:53:58 +0000
1540+++ src/powerd.cpp 2013-08-29 21:23:26 +0000
1541@@ -40,6 +40,7 @@
1542 #include "powerd-object.h"
1543 #include "powerd-dbus.h"
1544 #include "powerd-sensors.h"
1545+#include "device-config.h"
1546 #include "log.h"
1547
1548 #include <hybris/input/input_stack_compatibility_layer.h>
1549@@ -471,6 +472,9 @@
1550 powerd_debug("Activity Timeout is %d seconds\n", activity_timeout);
1551 powerd_debug("Auto-dim Timeout is %d seconds\n", dim_timeout);
1552
1553+ /* Init this first, data is used by other inits */
1554+ device_config_init();
1555+
1556 libsuspend_init(0);
1557 powerd_stats_init();
1558 powerd_client_init();
1559@@ -478,6 +482,7 @@
1560 display_request_init();
1561 dbus_name_watch_init();
1562 powerd_backlight_init();
1563+ powerd_autobrightness_init();
1564 powerd_display_init();
1565 powerd_sensors_init();
1566 powerd_ps_init();
1567@@ -497,6 +502,9 @@
1568 android_input_stack_initialize(&listener, &config);
1569 android_input_stack_start();
1570
1571+ /* Config use should be done during init, okay to free now */
1572+ device_config_deinit();
1573+
1574 /* This needs to be the first thing to run on the main loop */
1575 g_idle_add_full(G_PRIORITY_HIGH, main_init, NULL, NULL);
1576
1577@@ -509,6 +517,7 @@
1578
1579 powerd_ps_deinit();
1580 dbus_name_watch_deinit();
1581+ powerd_autobrightness_deinit();
1582 powerd_backlight_deinit();
1583 display_request_deinit();
1584 power_request_deinit();
1585
1586=== added file 'src/spline.c'
1587--- src/spline.c 1970-01-01 00:00:00 +0000
1588+++ src/spline.c 2013-08-29 21:23:26 +0000
1589@@ -0,0 +1,140 @@
1590+/*
1591+ * Copyright 2013 Canonical Ltd.
1592+ *
1593+ * This file is part of powerd.
1594+ *
1595+ * powerd is free software; you can redistribute it and/or modify
1596+ * it under the terms of the GNU General Public License as published by
1597+ * the Free Software Foundation; version 3.
1598+ *
1599+ * powerd is distributed in the hope that it will be useful,
1600+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1601+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1602+ * GNU General Public License for more details.
1603+ *
1604+ * You should have received a copy of the GNU General Public License
1605+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1606+ */
1607+
1608+#include <stdio.h>
1609+#include <stdlib.h>
1610+#include <string.h>
1611+
1612+struct spline_params {
1613+ double k, m;
1614+};
1615+
1616+struct spline {
1617+ double (*points)[2];
1618+ int n;
1619+ struct spline_params params[0];
1620+};
1621+
1622+int spline_sort_comp(const void *a, const void *b)
1623+{
1624+ double x1 = *(double *)a;
1625+ double x2 = *(double *)b;
1626+ return (int)(x1 - x2);
1627+}
1628+
1629+/*
1630+ * Initializes a monotone cubic Hermite spline for interpolating
1631+ * between the input points, using the Fritsch–Carlson method.
1632+ *
1633+ * http://en.wikipedia.org/wiki/Monotone_cubic_interpolation
1634+ *
1635+ * The interpolated points are guaranteed to have piecewise
1636+ * monotonicity, and will be fully monotone if the input points
1637+ * are monotone.
1638+ */
1639+struct spline *spline_new(double (*points)[2], int num_points)
1640+{
1641+ struct spline *s;
1642+ struct spline_params *param;
1643+ double (*p)[2];
1644+ int i;
1645+ double alpha, beta, prev_beta;
1646+
1647+ s = malloc(sizeof(struct spline) +
1648+ sizeof(struct spline_params) * num_points);
1649+ if (!s)
1650+ return NULL;
1651+ s->points = malloc(2 * sizeof(double) * num_points);
1652+ if (!s->points) {
1653+ free(s);
1654+ return NULL;
1655+ }
1656+ s->n = num_points;
1657+
1658+ /*
1659+ * Input points are likely to already be in order of increasing
1660+ * x, but sort them just in case.
1661+ */
1662+ memcpy(s->points, points, 2 * sizeof(double) * num_points);
1663+ qsort(s->points, num_points, 2 * sizeof(double), spline_sort_comp);
1664+
1665+ p = s->points;
1666+ param = s->params;
1667+
1668+ for (i = 0; i < s->n - 1; i++)
1669+ param[i].k = (p[i+1][1] - p[i][1]) / (p[i+1][0] - p[i][0]);
1670+
1671+ param[0].m = param[0].k;
1672+ for (i = 1; i < s->n; i++)
1673+ param[i].m = (param[i-1].k + param[i].k) / 2;
1674+ param[s->n - 1].m = param[s->n - 2].k;
1675+
1676+ beta = 0;
1677+ for (i = 0; i < s->n - 1; i++) {
1678+ if (p[i][1] == p[i+1][1]) {
1679+ param[i].m = 0;
1680+ param[++i].m = 0;
1681+ beta = 0;
1682+ continue;
1683+ }
1684+ prev_beta = beta;
1685+ alpha = param[i].m / param[i].k;
1686+ beta = param[i+1].m / param[i].k;
1687+ if (alpha < 0 || prev_beta < 0) {
1688+ param[i].m = 0;
1689+ continue;
1690+ }
1691+ if (alpha > 3)
1692+ param[i].m = 3 * param[i].k;
1693+ if (beta > 3)
1694+ param[i+1].m = 3 * param[i].k;
1695+ }
1696+
1697+ return s;
1698+}
1699+
1700+void spline_free(struct spline *s)
1701+{
1702+ free(s->points);
1703+ free(s);
1704+}
1705+
1706+double spline_interpolate(struct spline *s, double x)
1707+{
1708+ int i;
1709+ double h, t, ret;
1710+
1711+ for (i = 0; i < s->n - 1; i++) {
1712+ if (x >= s->points[i][0] && x < s->points[i+1][0])
1713+ break;
1714+ }
1715+
1716+ /* Handle out-of-bounds and boundary cases */
1717+ if (i == 0 && x <= s->points[0][0])
1718+ return s->points[0][1];
1719+ if (i >= s->n - 1)
1720+ return s->points[s->n - 1][1];
1721+
1722+ h = s->points[i+1][0] - s->points[i][0];
1723+ t = (x - s->points[i][0]) / h;
1724+ ret = s->points[i][1] * (1 + t * t * (2 * t - 3));
1725+ ret += h * s->params[i].m * t * (1 + t * (t - 2));
1726+ ret += s->points[i+1][1] * t * t * (3 - 2 * t);
1727+ ret += h * s->params[i+1].m * t * t * (t - 1);
1728+ return ret;
1729+}
1730
1731=== added file 'src/spline.h'
1732--- src/spline.h 1970-01-01 00:00:00 +0000
1733+++ src/spline.h 2013-08-29 21:23:26 +0000
1734@@ -0,0 +1,28 @@
1735+/*
1736+ * Copyright 2013 Canonical Ltd.
1737+ *
1738+ * This file is part of powerd.
1739+ *
1740+ * powerd is free software; you can redistribute it and/or modify
1741+ * it under the terms of the GNU General Public License as published by
1742+ * the Free Software Foundation; version 3.
1743+ *
1744+ * powerd is distributed in the hope that it will be useful,
1745+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1746+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1747+ * GNU General Public License for more details.
1748+ *
1749+ * You should have received a copy of the GNU General Public License
1750+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1751+ */
1752+
1753+#ifndef __SPLINE_H__
1754+#define __SPLINE_H__
1755+
1756+struct spline;
1757+
1758+struct spline *spline_new(double (*points)[2], int num_points);
1759+void spline_free(struct spline *s);
1760+double spline_interpolate(struct spline *s, double x);
1761+
1762+#endif

Subscribers

People subscribed via source and target branches