f9856f70042b47bacb3f595d454d16b4b84264e4
[platform/core/system/deviced.git] / plugins / mobile / battery / battery-notification.c
1 /*
2  * deviced
3  *
4  * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd.
5  *
6  * Licensed under the Apache License, Version 2.0 (the License);
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 #include <stdio.h>
20 #include <stdbool.h>
21 #include <string.h>
22 #include <libsyscommon/libgdbus.h>
23 #include <libsyscommon/resource-manager.h>
24 #include <system/syscommon-plugin-deviced-common-interface.h>
25 #include <system/syscommon-plugin-deviced-display-interface.h>
26
27 #include "core/log.h"
28 #include "battery.h"
29 #include "battery-parser.h"
30 #include "power-supply.h"
31 #include "lowbat-handler.h"
32 #include "battery-ops.h"
33 #include "core.h"
34 #include "display-lock.h"
35 #include "display-ops.h"
36 #include "display-state-transition.h"
37 #include "poll.h"
38 #include "shared/eventsystem.h"
39 #include "shared/plugin.h"
40 #include "shared/device-notifier.h"
41 #include "shared/apps.h"
42
43
44 #define METHOD_LOW_NOTI_ON          "BatteryLowNotiOn"
45 #define METHOD_LOW_NOTI_UPDATE      "BatteryLowNotiUpdate"
46 #define METHOD_LOW_NOTI_OFF         "BatteryLowNotiOff"
47 #define METHOD_CRITICAL_NOTI_ON     "BatteryCriticalNotiOn"
48 #define METHOD_CRITICAL_NOTI_UPDATE "BatteryCriticalNotiUpdate"
49 #define METHOD_CRITICAL_NOTI_OFF    "BatteryCriticalNotiOff"
50
51 #define REMOVE_POPUP        "remove_battery_popups"
52 #define DISCONNECT_POPUP    "battdisconnect"
53
54 enum event_noti_type {
55         NOTI_NONE = -1,
56         NOTI_UPDATE = 0,
57 };
58
59 static struct display_plugin *disp_plgn;
60 static struct battery_status *battery;
61 static guint abnormal_timer;
62
63 static int noti_low = NOTI_NONE;
64 static int noti_crit = NOTI_NONE;
65
66 static bool check_remove_condition(const char *prev, const char *cur)
67 {
68         int prev_len;
69         int cur_len;
70         bool ret = false;
71
72         prev_len = strlen(prev);
73         cur_len = strlen(cur);
74
75         if (!prev_len || !cur_len)
76                 return false;
77
78         if (!strncmp(prev, EVT_VAL_BATTERY_LEVEL_CRITICAL,prev_len)) {
79                 if (!strncmp(cur, EVT_VAL_BATTERY_LEVEL_LOW, cur_len)
80                         || !strncmp(cur, EVT_VAL_BATTERY_LEVEL_HIGH, cur_len)
81                         || !strncmp(cur, EVT_VAL_BATTERY_LEVEL_FULL, cur_len))
82                    ret = true;
83         } else if (!strncmp(prev, EVT_VAL_BATTERY_LEVEL_LOW, prev_len)) {
84                 if (!strncmp(cur, EVT_VAL_BATTERY_LEVEL_HIGH, cur_len)
85                         || !strncmp(cur, EVT_VAL_BATTERY_LEVEL_FULL, cur_len))
86                         ret = true;
87         }
88
89         return ret;
90 }
91
92 static void low_noti_cb(GVariant *var, void *user_data, GError *err)
93 {
94
95         if (!var) {
96                 _E("Invalid parameter.");
97                 return;
98         }
99
100         if (!g_variant_get_safe(var, "(i)", &noti_low)) {
101                 _E("Failed to notify low: no message(%s)", g_variant_get_type_string(var));
102                 goto out;
103         }
104
105         _D("Inserted battery low noti : %d", noti_low);
106
107 out:
108         g_variant_unref(var);
109 }
110
111 static void critical_noti_cb(GVariant *var, void *user_data, GError *err)
112 {
113         int id = 0;
114
115         if (!var)
116                 return;
117
118         if (!g_variant_get_safe(var, "(i)", &id)) {
119                 _E("Failed to notify critical: no message(%s)", g_variant_get_type_string(var));
120                 goto out;
121         }
122
123         noti_crit = id;
124         _D("Inserted battery critical noti : %d", noti_crit);
125
126 out:
127         g_variant_unref(var);
128 }
129
130 static int launch_lowbat_noti(int capacity, int option)
131 {
132         int ret = -EINVAL;
133         char batcap[10] = {0,};
134         char notiid[10] = {0,};
135         int noti_id;
136         const char *pa[2];
137         int retry;
138         char *noti_type = NULL;
139         static int prev_level = BATTERY_NORMAL;
140
141         snprintf(batcap, sizeof(batcap), "%d", capacity);
142         pa[0] = batcap;
143
144         if (option == BAT_OPT_WARNING) {
145                 if (noti_crit > 0) {
146                         /* remove waring notiid*/
147                         noti_id = noti_crit;
148                         for (retry = RETRY_MAX; retry > 0; retry--) {
149                                 ret = gdbus_call_async(POPUP_BUS_NAME,
150                                                 POPUP_PATH_BATTERY,
151                                                 POPUP_INTERFACE_BATTERY,
152                                                 METHOD_LOW_NOTI_OFF,
153                                                 g_variant_new("(i)", noti_id));
154                                 if (ret == 0) {
155                                         _D("Deleted battery critical noti.");
156                                         break;
157                                 } else
158                                         _E("Failed to call dbus method (err: %d)", ret);
159                         }
160                 }
161                 memset(notiid, 0x0, sizeof(notiid));
162                 snprintf(notiid, sizeof(notiid), "%d", noti_low);
163                 pa[1] = notiid;
164                 if (prev_level == battery_info.critical || noti_low == NOTI_UPDATE)
165                         noti_type = METHOD_LOW_NOTI_UPDATE;
166                 else {
167                         noti_type = METHOD_LOW_NOTI_ON;
168                         syscommon_resman_set_resource_attr_uint64_2(SYSCOMMON_RESOURCE_ID(DEVICED_RESOURCE_TYPE_DISPLAY),
169                                 DEVICED_DISPLAY_ATTR_TUPLE2_SET_CURRENT_STATE,
170                                 DEVICED_DISPLAY_STATE_ON, DEVICED_EVENT_BATTERY_CAPACITY_LOW);
171                 }
172                 prev_level = battery_info.warning;
173                 if (battery->charge_now)
174                         ret = gdbus_call_async(POPUP_BUS_NAME,
175                                 POPUP_PATH_BATTERY,
176                                 POPUP_INTERFACE_BATTERY,
177                                 noti_type,
178                                 g_variant_new("(ss)", pa[0], pa[1]));
179                 else
180                         ret = gdbus_call_async_with_reply(POPUP_BUS_NAME,
181                                 POPUP_PATH_BATTERY,
182                                 POPUP_INTERFACE_BATTERY,
183                                 noti_type,
184                                 g_variant_new("(ss)", pa[0], pa[1]),
185                                 low_noti_cb, -1, NULL);
186         } else if (option == BAT_OPT_CRITICAL) {
187                 if (noti_low > 0) {
188                         /* remove waring notiid*/
189                         noti_id = noti_low;
190                         for (retry = RETRY_MAX; retry > 0; retry--) {
191                                 ret = gdbus_call_async(POPUP_BUS_NAME,
192                                                 POPUP_PATH_BATTERY,
193                                                 POPUP_INTERFACE_BATTERY,
194                                                 METHOD_CRITICAL_NOTI_OFF,
195                                                 g_variant_new("(i)", noti_id));
196                                 if (ret == 0) {
197                                         _D("Deleted battery low noti");
198                                         break;
199                                 } else
200                                         _E("Failed to call dbus method (err: %d)", ret);
201                         }
202                 }
203                 memset(notiid, 0x0, sizeof(notiid));
204                 snprintf(notiid, sizeof(notiid), "%d", noti_crit);
205                 pa[1] = notiid;
206                 if (noti_crit == NOTI_UPDATE)
207                         noti_type = METHOD_CRITICAL_NOTI_UPDATE;
208                 else {
209                         noti_type = METHOD_CRITICAL_NOTI_ON;
210                         syscommon_resman_set_resource_attr_uint64_2(SYSCOMMON_RESOURCE_ID(DEVICED_RESOURCE_TYPE_DISPLAY),
211                                 DEVICED_DISPLAY_ATTR_TUPLE2_SET_CURRENT_STATE,
212                                 DEVICED_DISPLAY_STATE_ON, DEVICED_EVENT_BATTERY_CAPACITY_LOW);
213                 }
214                 prev_level = battery_info.critical;
215                 if (battery->charge_now)
216                         ret = gdbus_call_async(POPUP_BUS_NAME,
217                                 POPUP_PATH_BATTERY,
218                                 POPUP_INTERFACE_BATTERY,
219                                 noti_type,
220                                 g_variant_new("(ss)", pa[0], pa[1]));
221                 else
222                         ret = gdbus_call_async_with_reply(POPUP_BUS_NAME,
223                                 POPUP_PATH_BATTERY,
224                                 POPUP_INTERFACE_BATTERY,
225                                 noti_type,
226                                 g_variant_new("(ss)", pa[0], pa[1]),
227                                 critical_noti_cb, -1, NULL);
228
229         } else
230                 prev_level = battery_info.normal;
231
232         _D("Requested type(%s) id(%s)", noti_type, notiid);
233         return ret;
234 }
235
236 static void clean_lowbat_noti(const char *prev, const char *str)
237 {
238         int noti_id;
239         int retry;
240         int ret;
241         bool condition;
242
243         if (!prev || !str) {
244                 _E("There is no data.");
245                 return;
246         }
247
248         if (prev == str && !battery->charge_now)
249                 return;
250
251         condition = check_remove_condition(prev, str);
252         _D("Condition: %d", condition);
253
254         if (condition || battery->charge_now) {
255                 if (noti_crit > 1) {
256                         /* remove critical notiid */
257                         noti_id = noti_crit;
258                         for (retry = RETRY_MAX; retry > 0; retry--) {
259                                 ret = gdbus_call_async(POPUP_BUS_NAME,
260                                                 POPUP_PATH_BATTERY,
261                                                 POPUP_INTERFACE_BATTERY,
262                                                 METHOD_CRITICAL_NOTI_OFF,
263                                                 g_variant_new("(i)", noti_id));
264                                 if (ret == 0) {
265                                         _D("Deleted battery critical noti.");
266                                         noti_crit = NOTI_NONE;
267                                         break;
268                                 } else
269                                         _E("Failed to call dbus method: %d", ret);
270                         }
271                 }
272
273                 if (noti_low > 1) {
274                         /* remove waring notiid*/
275                         noti_id = noti_low;
276                         for (retry = RETRY_MAX; retry > 0; retry--) {
277                                 ret = gdbus_call_async(POPUP_BUS_NAME,
278                                                 POPUP_PATH_BATTERY,
279                                                 POPUP_INTERFACE_BATTERY,
280                                                 METHOD_LOW_NOTI_OFF,
281                                                 g_variant_new("(i)", noti_id));
282                                 if (ret == 0) {
283                                         _D("Deleted battery low noti.");
284                                         noti_low = NOTI_NONE;
285                                         break;
286                                 } else
287                                         _E("Failed to call dbus method: %d", ret);
288                         }
289                 }
290         }
291         _D("Noti_crit(%d) noti_low(%d).", noti_crit, noti_low);
292 }
293
294 static int changed_battery_cf(int status)
295 {
296         if (status == PRESENT_ABNORMAL)
297                 return launch_system_app(APP_ABNORMAL, 2, APP_KEY_TYPE, DISCONNECT_POPUP);
298         return launch_system_app(APP_REMOVE, 2, APP_KEY_TYPE, REMOVE_POPUP);
299 }
300
301 static void remove_health_popup(void)
302 {
303         int ret;
304
305         ret = launch_system_app(APP_REMOVE, 2, APP_KEY_TYPE, REMOVE_POPUP);
306         if (ret < 0)
307                 _E("Failed to launch remove battery popup(%d)", ret);
308 }
309
310 static int check_power_supply_noti(void)
311 {
312         return 1;
313 }
314
315 static void update_ovp(enum battery_noti_status status)
316 {
317         if (status == DEVICE_NOTI_ON)
318                 battery_pm_change_internal(DEVICED_EVENT_MISC_POPUP, LCD_DIM);
319         else
320                 battery_pm_change_internal(DEVICED_EVENT_MISC_POPUP, LCD_NORMAL);
321 }
322
323 static void health_timer_reset(void)
324 {
325         abnormal_timer = 0;
326 }
327
328 static gboolean health_timer_cb(void *data)
329 {
330         health_timer_reset();
331
332         if (battery->health != HEALTH_LOW && battery->health != HEALTH_HIGH)
333                 return G_SOURCE_REMOVE;
334
335         CRITICAL_LOG("Popup: Battery health status is not good, %s.", battery->health_s);
336         syscommon_notifier_emit_notify(DEVICE_NOTIFIER_BATTERY_HEALTH, (void *)&battery->health);
337         battery_pm_change_internal(DEVICED_EVENT_MISC_POPUP, LCD_DIM);
338         display_lock_request_unlock_with_option(DEVICED_EVENT_MISC_POPUP, LCD_OFF, PM_SLEEP_MARGIN);
339         display_lock_request_lock_with_option(DEVICED_EVENT_MISC_POPUP, LCD_DIM, STAY_CUR_STATE, 0);
340         if (battery->health == HEALTH_LOW)
341                 battery_charge_err_low_act(NULL);
342         else if (battery->health == HEALTH_HIGH)
343                 battery_charge_err_high_act(NULL);
344         return G_SOURCE_REMOVE;
345 }
346
347 static void abnormal_popup_dbus_signal_handler(GDBusConnection  *conn,
348                 const gchar      *sender,
349                 const gchar      *path,
350                 const gchar      *iface,
351                 const gchar      *name,
352                 GVariant         *param,
353                 gpointer          user_data)
354
355 {
356         if (battery->health == HEALTH_GOOD)
357                 return;
358
359         _I("Restart health timer.");
360         abnormal_timer = g_timeout_add_seconds(ABNORMAL_CHECK_TIMER_INTERVAL,
361                                                 health_timer_cb, NULL);
362         if (abnormal_timer == 0)
363                 _E("Failed to add abnormal check timer.");
364 }
365
366 static void battery_notification_init(void *data)
367 {
368         struct battery_plugin *plugin = (struct battery_plugin *)data;
369         int ret;
370
371         if (!plugin)
372                 return;
373
374         _D("Add plugins for battery notification.");
375         plugin->lowbat_noti_launch = launch_lowbat_noti;
376         plugin->lowbat_noti_clean = clean_lowbat_noti;
377
378         plugin->changed_battery_cf = changed_battery_cf;
379         plugin->remove_health_popup = remove_health_popup;
380         plugin->check_power_supply_noti = check_power_supply_noti;
381         plugin->update_ovp = update_ovp;
382
383         ret = gdbus_signal_subscribe(NULL, DEVICED_PATH_SYSNOTI,
384                 DEVICED_INTERFACE_SYSNOTI, SIGNAL_CHARGEERR_RESPONSE, abnormal_popup_dbus_signal_handler, NULL, NULL);
385         if (ret <= 0)
386                 _E("Failed to init dbus signal: %d", ret);
387 }
388
389 static const struct battery_ops battery_notification_ops = {
390         .name     = "battery_notification",
391         .init     = battery_notification_init,
392 };
393
394 BATTERY_OPS_REGISTER(&battery_notification_ops)
395
396 static void __CONSTRUCTOR__ initialize(void)
397 {
398         disp_plgn = get_var_display_plugin();
399         if (!disp_plgn)
400                 _E("Failed to get display plugin variable.");
401
402         battery = get_var_battery_status();
403         if (!battery)
404                 _E("Failed to get battery status structure.");
405 }