Add support for sending signals when service list changes
[framework/connectivity/connman.git] / src / profile.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2009  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <glib.h>
27 #include <gdbus.h>
28
29 #include "connman.h"
30
31 #define PROFILE_DEFAULT  "/profile/default"
32
33 enum connman_service_state {
34         CONNMAN_SERVICE_STATE_UNKNOWN = 0,
35         CONNMAN_SERVICE_STATE_IDLE    = 1,
36 };
37
38 struct connman_group {
39         GSequenceIter *iter;
40         char *id;
41         char *path;
42         char *type;
43         char *name;
44         char *mode;
45         char *security;
46         connman_uint8_t strength;
47         connman_bool_t favorite;
48         enum connman_service_state state;
49         struct connman_network *network;
50 };
51
52 static GSequence *groups = NULL;
53
54 static DBusConnection *connection = NULL;
55
56 static const char *state2string(enum connman_service_state state)
57 {
58         switch (state) {
59         case CONNMAN_SERVICE_STATE_UNKNOWN:
60                 break;
61         case CONNMAN_SERVICE_STATE_IDLE:
62                 return "idle";
63         }
64
65         return NULL;
66 }
67
68 static DBusMessage *get_properties(DBusConnection *conn,
69                                         DBusMessage *msg, void *data)
70 {
71         struct connman_group *group = data;
72         DBusMessage *reply;
73         DBusMessageIter array, dict;
74         const char *str;
75
76         DBG("conn %p", conn);
77
78         reply = dbus_message_new_method_return(msg);
79         if (reply == NULL)
80                 return NULL;
81
82         dbus_message_iter_init_append(reply, &array);
83
84         dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
85                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
86                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
87                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
88
89         str = state2string(group->state);
90         if (str != NULL)
91                 connman_dbus_dict_append_variant(&dict, "State",
92                                                 DBUS_TYPE_STRING, &str);
93
94         if (group->type != NULL)
95                 connman_dbus_dict_append_variant(&dict, "Type",
96                                         DBUS_TYPE_STRING, &group->type);
97
98         if (group->name != NULL)
99                 connman_dbus_dict_append_variant(&dict, "Name",
100                                         DBUS_TYPE_STRING, &group->name);
101
102         if (group->mode != NULL)
103                 connman_dbus_dict_append_variant(&dict, "Mode",
104                                         DBUS_TYPE_STRING, &group->mode);
105
106         if (group->security != NULL)
107                 connman_dbus_dict_append_variant(&dict, "Security",
108                                         DBUS_TYPE_STRING, &group->security);
109
110         if (group->strength > 0)
111                 connman_dbus_dict_append_variant(&dict, "Strength",
112                                         DBUS_TYPE_BYTE, &group->strength);
113
114         connman_dbus_dict_append_variant(&dict, "Favorite",
115                                         DBUS_TYPE_BOOLEAN, &group->favorite);
116
117         dbus_message_iter_close_container(&array, &dict);
118
119         return reply;
120 }
121
122 static DBusMessage *connect_service(DBusConnection *conn,
123                                         DBusMessage *msg, void *data)
124 {
125         return __connman_error_not_implemented(msg);
126 }
127
128 static DBusMessage *disconnect_service(DBusConnection *conn,
129                                         DBusMessage *msg, void *data)
130 {
131         return __connman_error_not_implemented(msg);
132 }
133
134 static DBusMessage *remove_service(DBusConnection *conn,
135                                         DBusMessage *msg, void *data)
136 {
137         return __connman_error_not_implemented(msg);
138 }
139
140 static DBusMessage *move_before(DBusConnection *conn,
141                                         DBusMessage *msg, void *data)
142 {
143         return __connman_error_not_implemented(msg);
144 }
145
146 static DBusMessage *move_after(DBusConnection *conn,
147                                         DBusMessage *msg, void *data)
148 {
149         return __connman_error_not_implemented(msg);
150 }
151
152 static GDBusMethodTable service_methods[] = {
153         { "GetProperties", "",  "a{sv}", get_properties     },
154         { "Connect",       "",  "",      connect_service    },
155         { "Disconnect",    "",  "",      disconnect_service },
156         { "Remove",        "",  "",      remove_service     },
157         { "MoveBefore",    "o", "",      move_before        },
158         { "MoveAfter",     "o", "",      move_after         },
159         { },
160 };
161
162 static GDBusSignalTable service_signals[] = {
163         { "PropertyChanged", "sv" },
164         { },
165 };
166
167 const char *__connman_profile_active(void)
168 {
169         DBG("");
170
171         return PROFILE_DEFAULT;
172 }
173
174 static void append_path(gpointer value, gpointer user_data)
175 {
176         struct connman_group *group = value;
177         DBusMessageIter *iter = user_data;
178
179         dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
180                                                         &group->path);
181 }
182
183 void __connman_profile_list_services(DBusMessageIter *iter)
184 {
185         DBG("");
186
187         g_sequence_foreach(groups, append_path, iter);
188 }
189
190 static void append_services(DBusMessageIter *entry)
191 {
192         DBusMessageIter value, iter;
193         const char *key = "Services";
194
195         dbus_message_iter_append_basic(entry, DBUS_TYPE_STRING, &key);
196
197         dbus_message_iter_open_container(entry, DBUS_TYPE_VARIANT,
198                 DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING,
199                                                                 &value);
200
201         dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
202                                 DBUS_TYPE_OBJECT_PATH_AS_STRING, &iter);
203         __connman_profile_list_services(&iter);
204         dbus_message_iter_close_container(&value, &iter);
205
206         dbus_message_iter_close_container(entry, &value);
207 }
208
209 static void emit_services_signal(void)
210 {
211         const char *path = __connman_profile_active();
212         DBusMessage *signal;
213         DBusMessageIter entry;
214
215         signal = dbus_message_new_signal(path,
216                                 CONNMAN_PROFILE_INTERFACE, "PropertyChanged");
217         if (signal == NULL)
218                 return;
219
220         dbus_message_iter_init_append(signal, &entry);
221         append_services(&entry);
222         g_dbus_send_message(connection, signal);
223
224         signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
225                                 CONNMAN_MANAGER_INTERFACE, "PropertyChanged");
226         if (signal == NULL)
227                 return;
228
229         dbus_message_iter_init_append(signal, &entry);
230         append_services(&entry);
231         g_dbus_send_message(connection, signal);
232 }
233
234 static void free_group(gpointer data)
235 {
236         struct connman_group *group = data;
237
238         DBG("group %p", group);
239
240         g_dbus_unregister_interface(connection, group->path,
241                                                 CONNMAN_SERVICE_INTERFACE);
242
243         g_free(group->security);
244         g_free(group->mode);
245         g_free(group->name);
246         g_free(group->type);
247         g_free(group->path);
248         g_free(group->id);
249         g_free(group);
250 }
251
252 static gint compare_group(gconstpointer a, gconstpointer b, gpointer user_data)
253 {
254         struct connman_group *group_a = (void *) a;
255         struct connman_group *group_b = (void *) b;
256
257         if (group_a->favorite == TRUE && group_b->favorite == FALSE)
258                 return -1;
259
260         if (group_a->favorite == FALSE && group_b->favorite == TRUE)
261                 return 1;
262
263         return (gint) group_b->strength - (gint) group_a->strength;
264 }
265
266 static gint search_group(gconstpointer a, gconstpointer b, gpointer user_data)
267 {
268         struct connman_group *group = (void *) a;
269
270         return g_strcmp0(group->id, user_data);
271 }
272
273 static struct connman_group *lookup_group(const char *name)
274 {
275         GSequenceIter *iter;
276         struct connman_group *group;
277
278         DBG("name %s", name);
279
280         if (name == NULL)
281                 return NULL;
282
283         iter = g_sequence_search(groups, NULL, search_group, (char *) name);
284         if (g_sequence_iter_is_begin(iter) == FALSE &&
285                                 g_sequence_iter_is_end(iter) == FALSE) {
286                 group = g_sequence_get(iter);
287                 if (group != NULL)
288                         goto done;
289         }
290
291         group = g_try_new0(struct connman_group, 1);
292         if (group == NULL)
293                 return NULL;
294
295         group->id = g_strdup(name);
296
297         group->type = CONNMAN_ELEMENT_TYPE_UNKNOWN;
298         group->path = g_strdup_printf("%s/%s", PROFILE_DEFAULT, name);
299
300         group->favorite = FALSE;
301
302         group->state = CONNMAN_SERVICE_STATE_IDLE;
303
304         group->iter = g_sequence_insert_sorted(groups, group,
305                                                 compare_group, NULL);
306
307         g_dbus_register_interface(connection, group->path,
308                                         CONNMAN_SERVICE_INTERFACE,
309                                         service_methods, service_signals,
310                                                         NULL, group, NULL);
311
312 done:
313         DBG("group %p", group);
314
315         return group;
316 }
317
318 int __connman_profile_add_device(struct connman_device *device)
319 {
320         struct connman_group *group;
321         char *name;
322
323         DBG("device %p", device);
324
325         name = g_strdup_printf("%s_%d", __connman_device_get_type(device),
326                                         connman_device_get_index(device));
327         group = lookup_group(name);
328         g_free(name);
329
330         if (group == NULL)
331                 return -EINVAL;
332
333         group->type = g_strdup(__connman_device_get_type(device));
334
335         g_sequence_sort_changed(group->iter, compare_group, NULL);
336         emit_services_signal();
337
338         return 0;
339 }
340
341 int __connman_profile_remove_device(struct connman_device *device)
342 {
343         struct connman_group *group;
344         char *name;
345
346         DBG("device %p", device);
347
348         name = g_strdup_printf("%s_%d", __connman_device_get_type(device),
349                                         connman_device_get_index(device));
350         group = lookup_group(name);
351         g_free(name);
352
353         if (group == NULL)
354                 return -EINVAL;
355
356         g_free(group->type);
357         group->type = NULL;
358
359         g_sequence_sort_changed(group->iter, compare_group, NULL);
360         emit_services_signal();
361
362         return 0;
363 }
364
365 int __connman_profile_add_network(struct connman_network *network)
366 {
367         struct connman_group *group;
368         char *name;
369
370         DBG("network %p", network);
371
372         if (__connman_network_get_group(network) == NULL)
373                 return -EINVAL;
374
375         name = g_strdup_printf("%s_%s", __connman_network_get_type(network),
376                                         __connman_network_get_group(network));
377         group = lookup_group(name);
378         g_free(name);
379
380         if (group == NULL)
381                 return -EINVAL;
382
383         g_free(group->type);
384         g_free(group->name);
385
386         group->type = g_strdup(__connman_network_get_type(network));
387         group->name = g_strdup(connman_network_get_string(network, "Name"));
388
389         group->strength = connman_network_get_uint8(network, "Strength");
390
391         if (group->network == NULL) {
392                 group->network = network;
393
394                 group->mode = g_strdup(connman_network_get_string(network,
395                                                                 "WiFi.Mode"));
396                 group->security = g_strdup(connman_network_get_string(network,
397                                                         "WiFi.Security"));
398         }
399
400         g_sequence_sort_changed(group->iter, compare_group, NULL);
401         emit_services_signal();
402
403         return 0;
404 }
405
406 int __connman_profile_remove_network(struct connman_network *network)
407 {
408         struct connman_group *group;
409         char *name;
410
411         DBG("network %p", network);
412
413         if (__connman_network_get_group(network) == NULL)
414                 return -EINVAL;
415
416         name = g_strdup_printf("%s_%s", __connman_network_get_type(network),
417                                         __connman_network_get_group(network));
418         group = lookup_group(name);
419         g_free(name);
420
421         if (group == NULL)
422                 return -EINVAL;
423
424         if (group->network == network) {
425                 g_free(group->security);
426                 group->security = NULL;
427
428                 g_free(group->mode);
429                 group->mode = NULL;
430
431                 group->network = NULL;
432         }
433
434         g_free(group->type);
435         group->type = NULL;
436
437         g_sequence_sort_changed(group->iter, compare_group, NULL);
438         emit_services_signal();
439
440         return 0;
441 }
442
443 void __connman_profile_list(DBusMessageIter *iter)
444 {
445         const char *path = __connman_profile_active();
446
447         DBG("");
448
449         dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
450 }
451
452 static DBusMessage *profile_properties(DBusConnection *conn,
453                                         DBusMessage *msg, void *data)
454 {
455         const char *name = "Default";
456         DBusMessage *reply;
457         DBusMessageIter array, dict, entry;
458
459         DBG("conn %p", conn);
460
461         reply = dbus_message_new_method_return(msg);
462         if (reply == NULL)
463                 return NULL;
464
465         dbus_message_iter_init_append(reply, &array);
466
467         dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
468                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
469                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
470                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
471
472         connman_dbus_dict_append_variant(&dict, "Name",
473                                                 DBUS_TYPE_STRING, &name);
474
475         dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
476                                                                 NULL, &entry);
477         append_services(&entry);
478         dbus_message_iter_close_container(&dict, &entry);
479
480         dbus_message_iter_close_container(&array, &dict);
481
482         return reply;
483 }
484
485 static GDBusMethodTable profile_methods[] = {
486         { "GetProperties", "", "a{sv}", profile_properties },
487         { },
488 };
489
490 static GDBusSignalTable profile_signals[] = {
491         { "PropertyChanged", "sv" },
492         { },
493 };
494
495 int __connman_profile_init(DBusConnection *conn)
496 {
497         DBG("conn %p", conn);
498
499         connection = dbus_connection_ref(conn);
500         if (connection == NULL)
501                 return -1;
502
503         groups = g_sequence_new(free_group);
504
505         g_dbus_register_interface(connection, PROFILE_DEFAULT,
506                                         CONNMAN_PROFILE_INTERFACE,
507                                         profile_methods, profile_signals,
508                                                         NULL, NULL, NULL);
509
510         return 0;
511 }
512
513 void __connman_profile_cleanup(void)
514 {
515         DBG("conn %p", connection);
516
517         g_dbus_unregister_interface(connection, PROFILE_DEFAULT,
518                                                 CONNMAN_PROFILE_INTERFACE);
519
520         g_sequence_free(groups);
521         groups = NULL;
522
523         if (connection == NULL)
524                 return;
525
526         dbus_connection_unref(connection);
527 }