Fix TC-2372 Dialer crashes when BT phone is offline
[profile/ivi/lemolo.git] / utils / amb.c
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4 #include <Elementary.h>
5 #include <Eldbus.h>
6
7 #include "amb.h"
8 #include "log.h"
9
10 typedef struct _AMB_Manager AMB_Manager;
11 typedef struct _AMB_Bus_Object AMB_Bus_Object;
12
13 static const char bus_name[] = "org.automotive.message.broker";
14
15 static Eldbus_Connection *bus_conn = NULL;
16 static char *bus_id = NULL;
17 static AMB_Manager *amb_manager = NULL;
18 static Eldbus_Signal_Handler *sig_amb_properties_changed = NULL;
19
20 struct _AMB_Callback_List_Node
21 {
22         EINA_INLIST;
23         void (*cb)(void *data);
24         const void *cb_data;
25 };
26
27 static Eina_Inlist *cbs_amb_connected = NULL;
28 static Eina_Inlist *cbs_amb_disconnected = NULL;
29 static Eina_Inlist *cbs_amb_properties_changed = NULL;
30
31 #define AMB_MANAGER_IFACE                       "org.automotive.Manager"
32 #define AMB_PROPERTIES_IFACE                    "org.freedesktop.DBus.Properties"
33 #define AMB_NIGHT_MODE_IFACE                    "org.automotive.NightMode"
34 #define PROP_NIGHT_MODE_STRING                  "NightMode"
35
36 struct _AMB_Bus_Object
37 {
38         const char *path;
39         Eina_List *dbus_signals; /* of Eldbus_Signal_Handler */
40 };
41
42 static void _bus_object_free(AMB_Bus_Object *o)
43 {
44         Eldbus_Signal_Handler *sh;
45
46         eina_stringshare_del(o->path);
47
48         EINA_LIST_FREE(o->dbus_signals, sh)
49                 eldbus_signal_handler_del(sh);
50 }
51
52 struct _AMB_Manager
53 {
54         AMB_Bus_Object base;
55         const char *name;
56         unsigned int interfaces;
57         Eina_Bool night_mode;
58 };
59
60 static AMB_Manager *_amb_get(void)
61 {
62         return amb_manager;
63 }
64
65 static void _notify_amb_callbacks_list(Eina_Inlist *list)
66 {
67         AMB_Callback_List_Node *node;
68
69         EINA_INLIST_FOREACH(list, node)
70                 node->cb((void *) node->cb_data);
71 }
72
73 static void _amb_get_property_reply(void *data, const Eldbus_Message *msg,
74                                                 Eldbus_Pending *pending __UNUSED__)
75 {
76         Eldbus_Message_Iter *variant;
77         const char *err_name, *err_message;
78
79         EINA_SAFETY_ON_NULL_RETURN(data);
80
81         if (!msg) {
82                 ERR("No message");
83                 return;
84         }
85
86         if (eldbus_message_error_get(msg, &err_name, &err_message)) {
87                 ERR("Failed to get property: %s: %s", err_name, err_message);
88                 return;
89         }
90
91         if (!eldbus_message_arguments_get(msg, "v", &variant)) {
92                 ERR("Could not get arguments");
93                 return;
94         }
95
96         char *property_name = (char*) data;
97         AMB_Manager *m = _amb_get();
98
99         if (strcmp(property_name, PROP_NIGHT_MODE_STRING) == 0) {
100                 eldbus_message_iter_arguments_get(variant, "b", &m->night_mode);
101                 DBG("NightMode is set to: %d", m->night_mode);
102         } else {
103                 return;
104         }
105
106         _notify_amb_callbacks_list(cbs_amb_properties_changed);
107 }
108
109 static void _amb_manager_property_get(const char *property, const char *path, Eldbus_Message_Cb cb, void *data)
110 {
111         Eldbus_Message *msg;
112         char *interface_name;
113
114         EINA_SAFETY_ON_NULL_RETURN(property);
115         EINA_SAFETY_ON_NULL_RETURN(path);
116
117         if (strcmp(property, PROP_NIGHT_MODE_STRING) == 0) {
118                 interface_name = AMB_NIGHT_MODE_IFACE;
119         } else {
120                 return;
121         }
122
123         msg = eldbus_message_method_call_new(
124                 bus_id, path, AMB_PROPERTIES_IFACE, "Get");
125
126         eldbus_message_arguments_append(msg,"ss", interface_name, property);
127         eldbus_connection_send(bus_conn, msg, cb, data, -1);
128 }
129
130 static void _amb_get_property_path_reply(void *data, Eldbus_Message *msg,
131                                                 Eldbus_Pending *pending __UNUSED__)
132 {
133         Eldbus_Message_Iter *ao;
134         const char *path, *err_name, *err_message;
135
136         EINA_SAFETY_ON_NULL_RETURN(data);
137
138         if (!msg) {
139                 ERR("No message");
140                 return;
141         }
142
143         if (eldbus_message_error_get(msg, &err_name, &err_message)) {
144                 ERR("Failed to get property path: %s: %s", err_name, err_message);
145                 return;
146         }
147
148         if (!eldbus_message_arguments_get(msg, "ao", &ao)) {
149                 ERR("Could not get arguments");
150                 return;
151         }
152
153         eldbus_message_iter_get_and_next(ao, 'o', &path);
154
155         if(!path) { 
156                 ERR("Could not get object path");
157                 return;
158         }
159
160         _amb_manager_property_get((const char*) data, path, _amb_get_property_reply, data);
161 }
162
163 static void _amb_get_property_path(const char *property, Eldbus_Message_Cb cb)
164 {
165         Eldbus_Message *msg = eldbus_message_method_call_new(
166                                 bus_id, "/", AMB_MANAGER_IFACE, "FindObject");
167
168         eldbus_message_arguments_append(msg, "s", property);
169         eldbus_connection_send(bus_conn, msg, cb, (void*) property, -1);
170 }
171
172 static void _amb_get_property(const char *property)
173 {
174         _amb_get_property_path(property, _amb_get_property_path_reply);
175 }
176
177 static AMB_Manager *_amb_new(const char *path)
178 {
179         AMB_Manager *m = calloc(1, sizeof(AMB_Manager));
180         EINA_SAFETY_ON_NULL_RETURN_VAL(m, NULL);
181
182         m->base.path = eina_stringshare_add(path);
183         EINA_SAFETY_ON_NULL_GOTO(m->base.path, error_path);
184
185         return m;
186
187 error_path:
188         free(m);
189         return NULL;
190 }
191
192 static void _amb_free(AMB_Manager *m)
193 {
194         eina_stringshare_del(m->name);
195
196         _bus_object_free(&m->base);
197         free(m);
198 }
199
200 static void _amb_properties_changed(void *data __UNUSED__, Eldbus_Message *msg)
201 {
202         Eina_Bool changed = EINA_FALSE;
203         Eldbus_Message_Iter *array1, *array2, *dict;
204         const char *interface, *err_name, *err_message;
205
206         if (eldbus_message_error_get(msg, &err_name, &err_message)) {
207                 ERR("Failed to get signal: %s: %s", err_name, err_message);
208                 return;
209         }
210
211         if (!eldbus_message_arguments_get(msg, "sa{sv}as", &interface, &array1, &array2)) {
212                 ERR("Could not get PropertiesChanged arguments");
213                 return;
214         }
215
216         AMB_Manager *m = _amb_get();
217
218         while (eldbus_message_iter_get_and_next(array1, 'e', &dict)) {
219                 Eldbus_Message_Iter *variant;
220                 const char *property;
221
222                 if (!eldbus_message_iter_arguments_get(dict, "sv", &property, &variant))
223                         continue;
224
225                 DBG("AMB property changed at %s: %s", interface, property);
226                 if (strcmp(interface, AMB_NIGHT_MODE_IFACE) == 0 &&
227                         strcmp(property, PROP_NIGHT_MODE_STRING) == 0) {
228                         eldbus_message_iter_arguments_get(variant, "b", &m->night_mode);
229                         changed = EINA_TRUE;
230                         DBG("NightMode updated: %d", m->night_mode);
231                 }
232         }
233
234         if (changed)
235                 _notify_amb_callbacks_list(cbs_amb_properties_changed);
236 }
237
238 static void _amb_load(void)
239 {
240         amb_manager = _amb_new("/");
241         EINA_SAFETY_ON_NULL_RETURN(amb_manager);
242
243         _amb_get_property(PROP_NIGHT_MODE_STRING);
244 }
245
246 static void _amb_connected(const char *id)
247 {
248         free(bus_id);
249         bus_id = strdup(id);
250
251         sig_amb_properties_changed = eldbus_signal_handler_add(
252                 bus_conn, bus_id, NULL,
253                 AMB_PROPERTIES_IFACE,
254                 "PropertiesChanged",
255                 _amb_properties_changed, NULL);
256
257         _amb_load();
258
259         _notify_amb_callbacks_list(cbs_amb_connected);
260 }
261
262 static void _amb_disconnected(void)
263 {
264         if (sig_amb_properties_changed) {
265                 eldbus_signal_handler_del(sig_amb_properties_changed);
266                 sig_amb_properties_changed = NULL;
267         }
268
269         if (bus_id) {
270                 _notify_amb_callbacks_list(cbs_amb_disconnected);
271                 free(bus_id);
272                 bus_id = NULL;
273         }
274 }
275
276 static void _name_owner_changed(void *data __UNUSED__, Eldbus_Message *msg)
277 {
278         const char *name, *from, *to;
279
280         if (!eldbus_message_arguments_get(msg, "sss", &name, &from, &to)) {
281                 ERR("Could not get NameOwnerChanged arguments");
282                 return;
283         }
284
285         if (strcmp(name, bus_name) != 0)
286                 return;
287
288         DBG("NameOwnerChanged %s from=%s to=%s", name, from, to);
289
290         if (from[0] == '\0' && to[0] != '\0') {
291                 INF("amb appeared as %s", to);
292                 _amb_connected(to);
293         } else if (from[0] != '\0' && to[0] == '\0') {
294                 INF("amb disappeared from %s", from);
295                 _amb_disconnected();
296         }
297 }
298
299 static void _amb_get_name_owner(void *data __UNUSED__, Eldbus_Message *msg,
300                                                 Eldbus_Pending *pending __UNUSED__)
301 {
302         const char *id, *err_name, *err_message;
303
304         if (!msg) {
305                 ERR("No message");
306                 return;
307         }
308
309         if (eldbus_message_error_get(msg, &err_name, &err_message)) {
310                 ERR("Failed to get name owner: %s: %s", err_name, err_message);
311                 return;
312         }
313
314         if (!eldbus_message_arguments_get(msg, "s", &id)) {
315                 ERR("Could not get arguments");
316                 return;
317         }
318
319         if (!id || id[0] == '\0') {
320                 ERR("No name owner fo %s!", bus_name);
321                 return;
322         }
323
324         INF("amb bus id: %s", id);
325         _amb_connected(id);
326 }
327
328 Eina_Bool amb_night_mode_get(void)
329 {
330         AMB_Manager *m = _amb_get();
331         EINA_SAFETY_ON_NULL_RETURN_VAL(m, EINA_FALSE);
332         return m->night_mode;
333 }
334
335 Eina_Bool amb_init(void)
336 {
337         if (!elm_need_eldbus()) {
338                 CRITICAL("Elementary does not support DBus.");
339                 return EINA_FALSE;
340         }
341
342         bus_conn = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SYSTEM);
343         if (!bus_conn) {
344                 CRITICAL("Could not get DBus System Bus");
345                 return EINA_FALSE;
346         }
347
348         eldbus_signal_handler_add(bus_conn, ELDBUS_FDO_BUS, ELDBUS_FDO_PATH,
349                                         ELDBUS_FDO_INTERFACE,
350                                         "NameOwnerChanged",
351                                         _name_owner_changed, NULL);
352
353         eldbus_name_owner_get(bus_conn, bus_name, _amb_get_name_owner, NULL);
354
355         return EINA_TRUE;
356 }
357
358 void amb_shutdown(void)
359 {
360         if (amb_manager) {
361                 _amb_free(amb_manager);
362                 amb_manager = NULL;
363         }       
364         _amb_disconnected();
365 }
366
367 static AMB_Callback_List_Node * _amb_callback_list_node_create(
368         void (*cb)(void *data),const void *data)
369 {
370         AMB_Callback_List_Node  *node_new;
371
372         node_new = calloc(1, sizeof(AMB_Callback_List_Node));
373         EINA_SAFETY_ON_NULL_RETURN_VAL(node_new, NULL);
374
375         node_new->cb_data = data;
376         node_new->cb = cb;
377
378         return node_new;
379 }
380
381 static void _amb_callback_list_delete(Eina_Inlist **list,
382                                         AMB_Callback_List_Node *node)
383 {
384         EINA_SAFETY_ON_NULL_RETURN(*list);
385         *list = eina_inlist_remove(*list, EINA_INLIST_GET(node));
386         free(node);
387 }
388
389 AMB_Callback_List_Node *
390 amb_properties_changed_cb_add(void (*cb)(void *data), const void *data)
391 {
392         AMB_Callback_List_Node *node;
393
394         EINA_SAFETY_ON_NULL_RETURN_VAL(cb, NULL);
395         node = _amb_callback_list_node_create(cb, data);
396         EINA_SAFETY_ON_NULL_RETURN_VAL(node, NULL);
397
398         cbs_amb_properties_changed = eina_inlist_append(cbs_amb_properties_changed,
399                                                 EINA_INLIST_GET(node));
400
401         return node;
402 }
403
404 void amb_properties_changed_cb_del(AMB_Callback_List_Node *node)
405 {
406         EINA_SAFETY_ON_NULL_RETURN(node);
407         _amb_callback_list_delete(&cbs_amb_properties_changed, node);
408 }