tizen 2.3 release
[framework/system/deviced.git] / src / logd_grabber / battery.c
1 #include <Eina.h>
2 #include <errno.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include <stdlib.h>
6 #include <unistd.h>
7 #include <vconf.h>
8
9 #include "core/log.h"
10 #include "battery.h"
11 #include "config.h"
12 #include "devices.h"
13 #include "logd.h"
14 #include "logd-db.h"
15 #include "logd-grabber.h"
16 #include "macro.h"
17
18 static const char *vconf_power_mode_path = "db/setting/psmode";
19 static int min_term_predict_lifetime;
20 static time_t last_pm_time;
21 static int last_pm_level;
22 static enum logd_power_mode curr_power_mode;
23
24 static Eina_List *battery_check_points = NULL;
25
26 static int on_charge()
27 {
28         const char *charge_status_file = "/sys/class/power_supply/battery/status";
29         FILE *fp;
30         int ret;
31         char *buf;
32
33         fp = fopen(charge_status_file, "r");
34         if (!fp) {
35                 ret = -errno;
36                 _E("fopen failed: %s", strerror(errno));
37                 return ret;
38         }
39
40         errno = 0;
41         if (fscanf(fp, "%ms", &buf) < 0) {
42                 ret = -errno;
43                 _E("fscanf failed: %s", strerror(errno));
44                 goto out;
45         }
46
47         if (strcmp(buf, "Charging"))
48                 ret = 1;
49         else
50                 ret = 0;
51         free(buf);
52 out:
53         if (fclose(fp) < 0) {
54                 _E("fopen failed: %s", strerror(errno));
55         }
56
57         return ret;
58 }
59
60 static void power_mode_changed_cb(keynode_t *key, void *data)
61 {
62         int new_power_mode = (enum logd_power_mode)vconf_keynode_get_int(key);
63
64         if (logd_event(LOGD_POWER_MODE, LOGD_CHANGED, new_power_mode) !=
65                 LOGD_ERROR_OK) {
66                 _E("logd_event failed");
67         }
68 }
69
70 int battery_init()
71 {
72         last_pm_time = getSecTime();
73         last_pm_level = get_current_battery_level();
74         struct logd_battery_level *b;
75         int is_on_charge;
76         int ret;
77
78         min_term_predict_lifetime = config_get_int("min_term_predict_lifetime", 10800, NULL);
79
80         if (last_pm_level < 0) {
81                 _E("get_current_battery_level failed, level set as 0");
82         }
83
84         b = (struct logd_battery_level*) calloc(1, sizeof(struct logd_battery_level));
85         if (!b) {
86                 ret = -errno;
87                 _E("calloc failed: %s", strerror(errno));
88                 return ret;
89         }
90
91         b->date = last_pm_time;
92         b->level = last_pm_level;
93
94         battery_check_points = eina_list_append(battery_check_points, b);
95         if (eina_error_get()) {
96                 _E("eina_list_append failed: %s", eina_error_msg_get(eina_error_get()));
97                 free(b);
98                 return -ENOMEM;
99         }
100
101         is_on_charge = on_charge();
102         if (is_on_charge < 0) {
103                 _E("on_charge failed");
104                 return is_on_charge;
105         } else if (is_on_charge > 0) {
106                 curr_power_mode = LOGD_POWER_MODE_CHARGED;
107         } else {
108                 if (vconf_get_int(vconf_power_mode_path, (int*)&curr_power_mode) < 0) {
109                         _E("vconf_get_int failed: %s. Current power mode set as 0",
110                                 vconf_power_mode_path);
111                 }
112         }
113
114         if (vconf_notify_key_changed(vconf_power_mode_path,
115                 power_mode_changed_cb, NULL) < 0) {
116                 _E("vconf_notify_key_changed failed: %s", vconf_power_mode_path);
117         }
118
119         return 0;
120 }
121
122 int battery_level_changed_event_handler(struct logd_grabber_event *event)
123 {
124         struct logd_battery_level *b;
125         struct logd_battery_level *last_point;
126         int ret;
127         int level = get_current_battery_level();
128         Eina_List *last_point_list = eina_list_last(battery_check_points);
129         time_t monotonic_time = getSecTime();
130
131         if (level < 0) {
132                 _E("get_current_battery_level failed");
133                 return level;
134         }
135
136         last_point = (struct logd_battery_level*)eina_list_data_get(last_point_list);
137         if (level == last_point->level)
138                 return 0;
139         if (monotonic_time != last_point->date)
140                 last_point->k = (float)(level - last_point->level) /
141                         (monotonic_time - last_point->date);
142         else
143                 last_point->k = level - last_point->level;
144
145
146         b = (struct logd_battery_level*) calloc(1, sizeof(struct logd_battery_level));
147         if (!b) {
148                 ret = -errno;
149                 _E("calloc failed: %s", strerror(errno));
150                 return ret;
151         }
152
153         b->date = monotonic_time;
154         b->level = level;
155
156         battery_check_points = eina_list_append(battery_check_points, b);
157         if (eina_error_get()) {
158                 _E("eina_list_append failed: %s", eina_error_msg_get(eina_error_get()));
159                 free(b);
160                 return -ENOMEM;
161         }
162
163         return 0;
164 }
165
166 int battery_charger_event_handler(struct logd_grabber_event *event)
167 {
168         Eina_List *last_point_list = eina_list_last(battery_check_points);
169         struct logd_battery_level *last_point;
170         time_t monotonic_time = getSecTime();
171         time_t time_diff = monotonic_time - last_pm_time;
172         int level_diff;
173         enum logd_power_mode new_power_mode;
174
175         last_point = (struct logd_battery_level*)eina_list_data_get(last_point_list);
176         level_diff = last_pm_level - last_point->level;
177
178         if (event->action == LOGD_ON)
179                 new_power_mode = LOGD_POWER_MODE_CHARGED;
180         else if (event->action == LOGD_OFF) {
181                 if (vconf_get_int(vconf_power_mode_path, (int*)&new_power_mode) < 0) {
182                         _E("vconf_get_int failed");
183                 }
184
185                 store_new_power_mode(event->date, curr_power_mode, new_power_mode,
186                         time_diff, level_diff);
187                 curr_power_mode = new_power_mode;
188                 last_pm_time = monotonic_time;
189                 last_pm_level = last_point->level;
190         }
191
192         return 0;
193 }
194
195 int battery_power_mode_changed_event_handler(struct logd_grabber_event *event)
196 {
197         Eina_List *last_point_list = eina_list_last(battery_check_points);
198         struct logd_battery_level *last_point;
199         enum logd_power_mode new_power_mode;
200         time_t time_diff;
201         int level_diff;
202         int ret;
203         time_t monotonic_time = getSecTime();
204
205         time_diff = monotonic_time - last_pm_time;
206         last_point = (struct logd_battery_level*)eina_list_data_get(last_point_list);
207         level_diff = last_pm_level - last_point->level;
208         new_power_mode = (enum logd_power_mode) atoi(event->message);
209
210         ret = store_new_power_mode(event->date, curr_power_mode, new_power_mode,
211                 time_diff, level_diff);
212         if (ret < 0) {
213                 _E("store_new_power_mode failed");
214                 return ret;
215         }
216
217         curr_power_mode = new_power_mode;
218         last_pm_time = monotonic_time;
219         last_pm_level = last_point->level;
220
221         return 0;
222 }
223
224 int battery_level_at(time_t date)
225 {
226         Eina_List *l;
227         struct logd_battery_level *data;
228         struct logd_battery_level *nearest = NULL;
229         time_t diff = INT_MAX;
230
231         EINA_LIST_FOREACH(battery_check_points, l, data) {
232                 int d = date - data->date;
233
234                 if (d < diff && d >= 0) {
235                         nearest = data;
236                         diff = d;
237                 }
238         }
239
240         if (!nearest) {
241                 _E("have no enough data to return battery level at %d", date);
242                 return -1;
243         }
244
245         return (nearest->k * (date - nearest->date) + nearest->level);
246 }
247
248 int battery_send_check_points(int socket)
249 {
250         Eina_List *l;
251         void *data;
252         int ret;
253         int count;
254
255         count = eina_list_count(battery_check_points);
256         if (write(socket, &count, sizeof(count)) != sizeof(count)) {
257                 ret = -errno;
258                 _E("write failed: %s", strerror(errno));
259                 return ret;
260         }
261
262
263         EINA_LIST_FOREACH(battery_check_points, l, data) {
264                 if (write(socket, data, sizeof(struct logd_battery_level)) !=
265                         sizeof(struct logd_battery_level)) {
266                         ret = -errno;
267                         _E("write failed: %s", strerror(errno));
268                         return ret;
269                 }
270         }
271
272         return 0;
273 }
274
275 int battery_send_estimate_lifetime(int socket)
276 {
277         int est_time[LOGD_POWER_MODE_MAX];
278         float curr_speed = -1;
279         float *avg_discharge_speed = NULL;
280         struct logd_battery_level *last_point = NULL;
281         struct logd_battery_level *pre_last_point = NULL;
282         int points_number;
283         int level;
284         int ret;
285         int i;
286
287         points_number = eina_list_count(battery_check_points);
288         last_point = (struct logd_battery_level*)
289                 eina_list_nth(battery_check_points, points_number - 1);
290         if (points_number > 1)
291                 pre_last_point = (struct logd_battery_level*)
292                         eina_list_nth(battery_check_points, points_number - 2);
293
294         for (i = 0; i < LOGD_POWER_MODE_MAX; ++i)
295                 est_time[i] = -1;
296
297         avg_discharge_speed = load_discharging_speed(min_term_predict_lifetime);
298         if (!avg_discharge_speed) {
299                 _E("load_discharging_speed failed");
300                 goto send;
301         }
302
303         level = last_point->level;
304
305         if (points_number > 2 && curr_power_mode != LOGD_POWER_MODE_CHARGED &&
306                 last_point->level < pre_last_point->level) {
307                 curr_speed = -pre_last_point->k;
308         }
309
310         if (curr_speed > 0) {
311                 for (i = 0; i < LOGD_POWER_MODE_MAX; ++i) {
312                         est_time[i] = level / curr_speed;
313                         if (avg_discharge_speed[curr_power_mode] > 0 && avg_discharge_speed[i] > 0)
314                                 est_time[i] *= avg_discharge_speed[i] /
315                                         avg_discharge_speed[curr_power_mode];
316
317                         est_time[i] -= getSecTime() - last_point->date;
318                         if (est_time[i] < 0)
319                                 est_time[i] = 0;
320                 }
321         } else {
322                 for (i = 0; i < LOGD_POWER_MODE_MAX; ++i) {
323                         if (avg_discharge_speed[i] > 0)
324                                 est_time[i] = level * avg_discharge_speed[i];
325                 }
326         }
327
328         free(avg_discharge_speed);
329
330 send:
331         for (i = 0; i < LOGD_POWER_MODE_MAX; ++i) {
332                 if (write(socket, &est_time[i], sizeof(est_time[i])) !=
333                         sizeof(est_time[i])) {
334                         ret = -errno;
335                         _E("write failed: %s", strerror(errno));
336                         return ret;
337                 }
338         }
339
340         return 0;
341 }
342
343 int battery_exit()
344 {
345         Eina_List *l;
346         void *data;
347
348         if (battery_check_points) {
349                 EINA_LIST_FOREACH(battery_check_points, l, data)
350                         free(data);
351                 eina_list_free(battery_check_points);
352         }
353
354         return 0;
355 }