cf98d88e28ff482c9994b79905746fd0b9f68ee5
[connman] / 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_type {
34         CONNMAN_SERVICE_TYPE_UNKNOWN  = 0,
35         CONNMAN_SERVICE_TYPE_ETHERNET = 1,
36         CONNMAN_SERVICE_TYPE_WIFI     = 2,
37         CONNMAN_SERVICE_TYPE_WIMAX    = 3,
38 };
39
40 enum connman_service_state {
41         CONNMAN_SERVICE_STATE_UNKNOWN       = 0,
42         CONNMAN_SERVICE_STATE_IDLE          = 1,
43         CONNMAN_SERVICE_STATE_ASSOCIATION   = 2,
44         CONNMAN_SERVICE_STATE_CONFIGURATION = 3,
45         CONNMAN_SERVICE_STATE_READY         = 4,
46         CONNMAN_SERVICE_STATE_DISCONNECT    = 5,
47         CONNMAN_SERVICE_STATE_FAILURE       = 6,
48 };
49
50 struct connman_group {
51         GSequenceIter *iter;
52         char *id;
53         char *path;
54         char *name;
55         char *mode;
56         char *security;
57         connman_uint8_t strength;
58         connman_bool_t favorite;
59         enum connman_service_type type;
60         enum connman_service_state state;
61         struct connman_network *network;
62 };
63
64 static GSequence *groups = NULL;
65
66 static DBusConnection *connection = NULL;
67
68 static const char *type2string(enum connman_service_type type)
69 {
70         switch (type) {
71         case CONNMAN_SERVICE_TYPE_UNKNOWN:
72                 break;
73         case CONNMAN_SERVICE_TYPE_ETHERNET:
74                 return "ethernet";
75         case CONNMAN_SERVICE_TYPE_WIFI:
76                 return "wifi";
77         case CONNMAN_SERVICE_TYPE_WIMAX:
78                 return "wimax";
79         }
80
81         return NULL;
82 }
83
84 static const char *state2string(enum connman_service_state state)
85 {
86         switch (state) {
87         case CONNMAN_SERVICE_STATE_UNKNOWN:
88                 break;
89         case CONNMAN_SERVICE_STATE_IDLE:
90                 return "idle";
91         case CONNMAN_SERVICE_STATE_ASSOCIATION:
92                 return "association";
93         case CONNMAN_SERVICE_STATE_CONFIGURATION:
94                 return "configuration";
95         case CONNMAN_SERVICE_STATE_READY:
96                 return "ready";
97         case CONNMAN_SERVICE_STATE_DISCONNECT:
98                 return "disconnect";
99         case CONNMAN_SERVICE_STATE_FAILURE:
100                 return "failure";
101         }
102
103         return NULL;
104 }
105
106 static DBusMessage *get_properties(DBusConnection *conn,
107                                         DBusMessage *msg, void *data)
108 {
109         struct connman_group *group = data;
110         DBusMessage *reply;
111         DBusMessageIter array, dict;
112         const char *str;
113
114         DBG("conn %p", conn);
115
116         reply = dbus_message_new_method_return(msg);
117         if (reply == NULL)
118                 return NULL;
119
120         dbus_message_iter_init_append(reply, &array);
121
122         dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
123                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
124                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
125                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
126
127         str = type2string(group->type);
128         if (str != NULL)
129                 connman_dbus_dict_append_variant(&dict, "Type",
130                                                 DBUS_TYPE_STRING, &str);
131
132         str = state2string(group->state);
133         if (str != NULL)
134                 connman_dbus_dict_append_variant(&dict, "State",
135                                                 DBUS_TYPE_STRING, &str);
136
137         if (group->name != NULL)
138                 connman_dbus_dict_append_variant(&dict, "Name",
139                                         DBUS_TYPE_STRING, &group->name);
140
141         if (group->mode != NULL)
142                 connman_dbus_dict_append_variant(&dict, "Mode",
143                                         DBUS_TYPE_STRING, &group->mode);
144
145         if (group->security != NULL)
146                 connman_dbus_dict_append_variant(&dict, "Security",
147                                         DBUS_TYPE_STRING, &group->security);
148
149         if (group->strength > 0)
150                 connman_dbus_dict_append_variant(&dict, "Strength",
151                                         DBUS_TYPE_BYTE, &group->strength);
152
153         connman_dbus_dict_append_variant(&dict, "Favorite",
154                                         DBUS_TYPE_BOOLEAN, &group->favorite);
155
156         dbus_message_iter_close_container(&array, &dict);
157
158         return reply;
159 }
160
161 static DBusMessage *connect_service(DBusConnection *conn,
162                                         DBusMessage *msg, void *data)
163 {
164         struct connman_group *group = data;
165
166         if (group->type == CONNMAN_SERVICE_TYPE_ETHERNET)
167                 return __connman_error_not_supported(msg);
168
169         return __connman_error_not_implemented(msg);
170 }
171
172 static DBusMessage *disconnect_service(DBusConnection *conn,
173                                         DBusMessage *msg, void *data)
174 {
175         struct connman_group *group = data;
176
177         if (group->type == CONNMAN_SERVICE_TYPE_ETHERNET)
178                 return __connman_error_not_supported(msg);
179
180         return __connman_error_not_implemented(msg);
181 }
182
183 static DBusMessage *remove_service(DBusConnection *conn,
184                                         DBusMessage *msg, void *data)
185 {
186         struct connman_group *group = data;
187
188         if (group->type == CONNMAN_SERVICE_TYPE_ETHERNET)
189                 return __connman_error_not_supported(msg);
190
191         group->favorite = FALSE;
192
193         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
194 }
195
196 static DBusMessage *move_before(DBusConnection *conn,
197                                         DBusMessage *msg, void *data)
198 {
199         struct connman_group *group = data;
200
201         if (group->favorite == FALSE)
202                 return __connman_error_not_supported(msg);
203
204         return __connman_error_not_implemented(msg);
205 }
206
207 static DBusMessage *move_after(DBusConnection *conn,
208                                         DBusMessage *msg, void *data)
209 {
210         struct connman_group *group = data;
211
212         if (group->favorite == FALSE)
213                 return __connman_error_not_supported(msg);
214
215         return __connman_error_not_implemented(msg);
216 }
217
218 static GDBusMethodTable service_methods[] = {
219         { "GetProperties", "",  "a{sv}", get_properties     },
220         { "Connect",       "",  "",      connect_service    },
221         { "Disconnect",    "",  "",      disconnect_service },
222         { "Remove",        "",  "",      remove_service     },
223         { "MoveBefore",    "o", "",      move_before        },
224         { "MoveAfter",     "o", "",      move_after         },
225         { },
226 };
227
228 static GDBusSignalTable service_signals[] = {
229         { "PropertyChanged", "sv" },
230         { },
231 };
232
233 const char *__connman_profile_active(void)
234 {
235         DBG("");
236
237         return PROFILE_DEFAULT;
238 }
239
240 static void append_path(gpointer value, gpointer user_data)
241 {
242         struct connman_group *group = value;
243         DBusMessageIter *iter = user_data;
244
245         dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
246                                                         &group->path);
247 }
248
249 void __connman_profile_list_services(DBusMessageIter *iter)
250 {
251         DBG("");
252
253         g_sequence_foreach(groups, append_path, iter);
254 }
255
256 static void append_services(DBusMessageIter *entry)
257 {
258         DBusMessageIter value, iter;
259         const char *key = "Services";
260
261         dbus_message_iter_append_basic(entry, DBUS_TYPE_STRING, &key);
262
263         dbus_message_iter_open_container(entry, DBUS_TYPE_VARIANT,
264                 DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING,
265                                                                 &value);
266
267         dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
268                                 DBUS_TYPE_OBJECT_PATH_AS_STRING, &iter);
269         __connman_profile_list_services(&iter);
270         dbus_message_iter_close_container(&value, &iter);
271
272         dbus_message_iter_close_container(entry, &value);
273 }
274
275 static void emit_services_signal(void)
276 {
277         const char *path = __connman_profile_active();
278         DBusMessage *signal;
279         DBusMessageIter entry;
280
281         signal = dbus_message_new_signal(path,
282                                 CONNMAN_PROFILE_INTERFACE, "PropertyChanged");
283         if (signal == NULL)
284                 return;
285
286         dbus_message_iter_init_append(signal, &entry);
287         append_services(&entry);
288         g_dbus_send_message(connection, signal);
289
290         signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
291                                 CONNMAN_MANAGER_INTERFACE, "PropertyChanged");
292         if (signal == NULL)
293                 return;
294
295         dbus_message_iter_init_append(signal, &entry);
296         append_services(&entry);
297         g_dbus_send_message(connection, signal);
298 }
299
300 static void free_group(gpointer data)
301 {
302         struct connman_group *group = data;
303
304         DBG("group %p", group);
305
306         g_dbus_unregister_interface(connection, group->path,
307                                                 CONNMAN_SERVICE_INTERFACE);
308
309         g_free(group->security);
310         g_free(group->mode);
311         g_free(group->name);
312         g_free(group->path);
313         g_free(group->id);
314         g_free(group);
315 }
316
317 static gint compare_group(gconstpointer a, gconstpointer b, gpointer user_data)
318 {
319         struct connman_group *group_a = (void *) a;
320         struct connman_group *group_b = (void *) b;
321
322         if (group_a->favorite == TRUE && group_b->favorite == FALSE)
323                 return -1;
324
325         if (group_a->favorite == FALSE && group_b->favorite == TRUE)
326                 return 1;
327
328         return (gint) group_b->strength - (gint) group_a->strength;
329 }
330
331 static struct connman_group *lookup_group(const char *name)
332 {
333         GSequenceIter *iter;
334         struct connman_group *group;
335
336         DBG("name %s", name);
337
338         if (name == NULL)
339                 return NULL;
340
341         iter = g_sequence_get_begin_iter(groups);
342         while (g_sequence_iter_is_end(iter) == FALSE) {
343                 group = g_sequence_get(iter);
344
345                 if (g_strcmp0(group->id, name) == 0)
346                         goto done;
347         }
348
349         group = g_try_new0(struct connman_group, 1);
350         if (group == NULL)
351                 return NULL;
352
353         group->id = g_strdup(name);
354
355         group->type = CONNMAN_SERVICE_TYPE_UNKNOWN;
356         group->path = g_strdup_printf("%s/%s", PROFILE_DEFAULT, name);
357
358         group->favorite = FALSE;
359
360         group->state = CONNMAN_SERVICE_STATE_IDLE;
361
362         group->iter = g_sequence_insert_sorted(groups, group,
363                                                 compare_group, NULL);
364
365         g_dbus_register_interface(connection, group->path,
366                                         CONNMAN_SERVICE_INTERFACE,
367                                         service_methods, service_signals,
368                                                         NULL, group, NULL);
369
370 done:
371         DBG("group %p", group);
372
373         return group;
374 }
375
376 static enum connman_service_type convert_device_type(struct connman_device *device)
377 {
378         enum connman_device_type type = connman_device_get_type(device);
379
380         switch (type) {
381         case CONNMAN_DEVICE_TYPE_UNKNOWN:
382         case CONNMAN_DEVICE_TYPE_VENDOR:
383         case CONNMAN_DEVICE_TYPE_WIFI:
384         case CONNMAN_DEVICE_TYPE_WIMAX:
385         case CONNMAN_DEVICE_TYPE_BLUETOOTH:
386         case CONNMAN_DEVICE_TYPE_GPS:
387         case CONNMAN_DEVICE_TYPE_HSO:
388         case CONNMAN_DEVICE_TYPE_NOZOMI:
389         case CONNMAN_DEVICE_TYPE_HUAWEI:
390         case CONNMAN_DEVICE_TYPE_NOVATEL:
391                 break;
392         case CONNMAN_DEVICE_TYPE_ETHERNET:
393                 return CONNMAN_SERVICE_TYPE_ETHERNET;
394         }
395
396         return CONNMAN_SERVICE_TYPE_UNKNOWN;
397 }
398
399 int __connman_profile_add_device(struct connman_device *device)
400 {
401         struct connman_group *group;
402         char *name;
403
404         DBG("device %p", device);
405
406         name = g_strdup_printf("%s_%d", __connman_device_get_type(device),
407                                         connman_device_get_index(device));
408         group = lookup_group(name);
409         g_free(name);
410
411         if (group == NULL)
412                 return -EINVAL;
413
414         group->type = convert_device_type(device);
415
416         g_sequence_sort_changed(group->iter, compare_group, NULL);
417         emit_services_signal();
418
419         return 0;
420 }
421
422 int __connman_profile_remove_device(struct connman_device *device)
423 {
424         struct connman_group *group;
425         char *name;
426
427         DBG("device %p", device);
428
429         name = g_strdup_printf("%s_%d", __connman_device_get_type(device),
430                                         connman_device_get_index(device));
431         group = lookup_group(name);
432         g_free(name);
433
434         if (group == NULL)
435                 return -EINVAL;
436
437         group->type = CONNMAN_SERVICE_TYPE_UNKNOWN;
438
439         g_sequence_sort_changed(group->iter, compare_group, NULL);
440         emit_services_signal();
441
442         return 0;
443 }
444
445 int __connman_profile_set_carrier(struct connman_device *device,
446                                                 connman_bool_t carrier)
447 {
448         struct connman_group *group;
449         char *name;
450
451         DBG("device %p carrier %d", device, carrier);
452
453         name = g_strdup_printf("%s_%d", __connman_device_get_type(device),
454                                         connman_device_get_index(device));
455         group = lookup_group(name);
456         g_free(name);
457
458         if (group == NULL)
459                 return -EINVAL;
460
461         if (group->favorite == carrier)
462                 return -EALREADY;
463
464         group->favorite = carrier;
465
466         g_sequence_sort_changed(group->iter, compare_group, NULL);
467         emit_services_signal();
468
469         return 0;
470 }
471
472 static enum connman_service_type convert_network_type(struct connman_network *network)
473 {
474         enum connman_network_type type = connman_network_get_type(network);
475
476         switch (type) {
477         case CONNMAN_NETWORK_TYPE_UNKNOWN:
478         case CONNMAN_NETWORK_TYPE_VENDOR:
479         case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
480         case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
481         case CONNMAN_NETWORK_TYPE_HSO:
482                 break;
483         case CONNMAN_NETWORK_TYPE_WIFI:
484                 return CONNMAN_SERVICE_TYPE_WIFI;
485         case CONNMAN_NETWORK_TYPE_WIMAX:
486                 return CONNMAN_SERVICE_TYPE_WIMAX;
487         }
488
489         return CONNMAN_SERVICE_TYPE_UNKNOWN;
490 }
491
492 int __connman_profile_add_network(struct connman_network *network)
493 {
494         struct connman_group *group;
495         char *name;
496
497         DBG("network %p", network);
498
499         if (__connman_network_get_group(network) == NULL)
500                 return -EINVAL;
501
502         name = g_strdup_printf("%s_%s", __connman_network_get_type(network),
503                                         __connman_network_get_group(network));
504         group = lookup_group(name);
505         g_free(name);
506
507         if (group == NULL)
508                 return -EINVAL;
509
510         group->type = convert_network_type(network);
511
512         g_free(group->name);
513         group->name = g_strdup(connman_network_get_string(network, "Name"));
514
515         group->strength = connman_network_get_uint8(network, "Strength");
516
517         if (group->network == NULL) {
518                 group->network = network;
519
520                 group->mode = g_strdup(connman_network_get_string(network,
521                                                                 "WiFi.Mode"));
522                 group->security = g_strdup(connman_network_get_string(network,
523                                                         "WiFi.Security"));
524         }
525
526         g_sequence_sort_changed(group->iter, compare_group, NULL);
527         emit_services_signal();
528
529         return 0;
530 }
531
532 int __connman_profile_remove_network(struct connman_network *network)
533 {
534         struct connman_group *group;
535         char *name;
536
537         DBG("network %p", network);
538
539         if (__connman_network_get_group(network) == NULL)
540                 return -EINVAL;
541
542         name = g_strdup_printf("%s_%s", __connman_network_get_type(network),
543                                         __connman_network_get_group(network));
544         group = lookup_group(name);
545         g_free(name);
546
547         if (group == NULL)
548                 return -EINVAL;
549
550         if (group->network == network) {
551                 g_free(group->security);
552                 group->security = NULL;
553
554                 g_free(group->mode);
555                 group->mode = NULL;
556
557                 group->network = NULL;
558         }
559
560         group->type = CONNMAN_SERVICE_TYPE_UNKNOWN;
561
562         g_sequence_sort_changed(group->iter, compare_group, NULL);
563         emit_services_signal();
564
565         return 0;
566 }
567
568 void __connman_profile_list(DBusMessageIter *iter)
569 {
570         const char *path = __connman_profile_active();
571
572         DBG("");
573
574         dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
575 }
576
577 static DBusMessage *profile_properties(DBusConnection *conn,
578                                         DBusMessage *msg, void *data)
579 {
580         const char *name = "Default";
581         DBusMessage *reply;
582         DBusMessageIter array, dict, entry;
583
584         DBG("conn %p", conn);
585
586         reply = dbus_message_new_method_return(msg);
587         if (reply == NULL)
588                 return NULL;
589
590         dbus_message_iter_init_append(reply, &array);
591
592         dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
593                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
594                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
595                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
596
597         connman_dbus_dict_append_variant(&dict, "Name",
598                                                 DBUS_TYPE_STRING, &name);
599
600         dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
601                                                                 NULL, &entry);
602         append_services(&entry);
603         dbus_message_iter_close_container(&dict, &entry);
604
605         dbus_message_iter_close_container(&array, &dict);
606
607         return reply;
608 }
609
610 static GDBusMethodTable profile_methods[] = {
611         { "GetProperties", "", "a{sv}", profile_properties },
612         { },
613 };
614
615 static GDBusSignalTable profile_signals[] = {
616         { "PropertyChanged", "sv" },
617         { },
618 };
619
620 int __connman_profile_init(DBusConnection *conn)
621 {
622         DBG("conn %p", conn);
623
624         connection = dbus_connection_ref(conn);
625         if (connection == NULL)
626                 return -1;
627
628         groups = g_sequence_new(free_group);
629
630         g_dbus_register_interface(connection, PROFILE_DEFAULT,
631                                         CONNMAN_PROFILE_INTERFACE,
632                                         profile_methods, profile_signals,
633                                                         NULL, NULL, NULL);
634
635         return 0;
636 }
637
638 void __connman_profile_cleanup(void)
639 {
640         DBG("conn %p", connection);
641
642         g_dbus_unregister_interface(connection, PROFILE_DEFAULT,
643                                                 CONNMAN_PROFILE_INTERFACE);
644
645         g_sequence_free(groups);
646         groups = NULL;
647
648         if (connection == NULL)
649                 return;
650
651         dbus_connection_unref(connection);
652 }