session: Fix Session.Connect()/Disconnect()
[framework/connectivity/connman.git] / src / session.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
6  *  Copyright (C) 2011  BWM CarIT GmbH. All rights reserved.
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License version 2 as
10  *  published by the Free Software Foundation.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <gdbus.h>
28
29 #include "connman.h"
30
31 static DBusConnection *connection;
32 static GHashTable *session_hash;
33 static connman_bool_t sessionmode;
34
35 enum connman_session_roaming_policy {
36         CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN          = 0,
37         CONNMAN_SESSION_ROAMING_POLICY_DEFAULT          = 1,
38         CONNMAN_SESSION_ROAMING_POLICY_ALWAYS           = 2,
39         CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN        = 3,
40         CONNMAN_SESSION_ROAMING_POLICY_NATIONAL         = 4,
41         CONNMAN_SESSION_ROAMING_POLICY_INTERNATIONAL    = 5,
42 };
43
44 struct connman_session {
45         char *owner;
46         char *session_path;
47         char *notify_path;
48         guint notify_watch;
49
50         char *bearer;
51         const char *name;
52         char *ifname;
53         connman_bool_t online;
54         connman_bool_t priority;
55         GSList *allowed_bearers;
56         connman_bool_t avoid_handover;
57         connman_bool_t stay_connected;
58         unsigned int periodic_connect;
59         unsigned int idle_timeout;
60         connman_bool_t ecall;
61         enum connman_session_roaming_policy roaming_policy;
62         unsigned int marker;
63
64         GSequence *service_list;
65         struct connman_service *service;
66 };
67
68 struct bearer_info {
69         char *name;
70         connman_bool_t match_all;
71         enum connman_service_type service_type;
72 };
73
74 static const char *roamingpolicy2string(enum connman_session_roaming_policy policy)
75 {
76         switch (policy) {
77         case CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN:
78                 break;
79         case CONNMAN_SESSION_ROAMING_POLICY_DEFAULT:
80                 return "default";
81         case CONNMAN_SESSION_ROAMING_POLICY_ALWAYS:
82                 return "always";
83         case CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN:
84                 return "forbidden";
85         case CONNMAN_SESSION_ROAMING_POLICY_NATIONAL:
86                 return "national";
87         case CONNMAN_SESSION_ROAMING_POLICY_INTERNATIONAL:
88                 return "international";
89         }
90
91         return NULL;
92 }
93
94 static enum connman_session_roaming_policy string2roamingpolicy(const char *policy)
95 {
96         if (g_strcmp0(policy, "default") == 0)
97                 return CONNMAN_SESSION_ROAMING_POLICY_DEFAULT;
98         else if (g_strcmp0(policy, "always") == 0)
99                 return CONNMAN_SESSION_ROAMING_POLICY_ALWAYS;
100         else if (g_strcmp0(policy, "forbidden") == 0)
101                 return CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN;
102         else if (g_strcmp0(policy, "national") == 0)
103                 return CONNMAN_SESSION_ROAMING_POLICY_NATIONAL;
104         else if (g_strcmp0(policy, "international") == 0)
105                 return CONNMAN_SESSION_ROAMING_POLICY_INTERNATIONAL;
106         else
107                 return CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN;
108 }
109
110 static enum connman_service_type bearer2service(const char *bearer)
111 {
112         if (bearer == NULL)
113                 return CONNMAN_SERVICE_TYPE_UNKNOWN;
114
115         if (g_strcmp0(bearer, "ethernet") == 0)
116                 return CONNMAN_SERVICE_TYPE_ETHERNET;
117         else if (g_strcmp0(bearer, "wifi") == 0)
118                 return CONNMAN_SERVICE_TYPE_WIFI;
119         else if (g_strcmp0(bearer, "wimax") == 0)
120                 return CONNMAN_SERVICE_TYPE_WIMAX;
121         else if (g_strcmp0(bearer, "bluetooth") == 0)
122                 return CONNMAN_SERVICE_TYPE_BLUETOOTH;
123         else if (g_strcmp0(bearer, "3g") == 0)
124                 return CONNMAN_SERVICE_TYPE_CELLULAR;
125         else
126                 return CONNMAN_SERVICE_TYPE_UNKNOWN;
127 }
128
129 static char *service2bearer(enum connman_service_type type)
130 {
131         switch (type) {
132         case CONNMAN_SERVICE_TYPE_ETHERNET:
133                 return "ethernet";
134         case CONNMAN_SERVICE_TYPE_WIFI:
135                 return "wifi";
136         case CONNMAN_SERVICE_TYPE_WIMAX:
137                 return "wimax";
138         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
139                 return "bluetooth";
140         case CONNMAN_SERVICE_TYPE_CELLULAR:
141                 return "3g";
142         case CONNMAN_SERVICE_TYPE_UNKNOWN:
143         case CONNMAN_SERVICE_TYPE_SYSTEM:
144         case CONNMAN_SERVICE_TYPE_GPS:
145         case CONNMAN_SERVICE_TYPE_VPN:
146         case CONNMAN_SERVICE_TYPE_GADGET:
147                 return NULL;
148         }
149
150         return NULL;
151 }
152
153 static char *session2bearer(struct connman_session *session)
154 {
155         GSList *list;
156         struct bearer_info *info;
157         enum connman_service_type type;
158
159         if (session->service == NULL)
160                 return NULL;
161
162         type = connman_service_get_type(session->service);
163
164         for (list = session->allowed_bearers; list != NULL; list = list->next) {
165                 info = list->data;
166
167                 if (info->match_all)
168                         return service2bearer(type);
169
170                 if (info->service_type == CONNMAN_SERVICE_TYPE_UNKNOWN)
171                         return info->name;
172
173                 if (info->service_type == type)
174                         return service2bearer(type);
175         }
176
177         return NULL;
178
179 }
180
181 static void cleanup_bearer_info(gpointer data, gpointer user_data)
182 {
183         struct bearer_info *info = data;
184
185         g_free(info->name);
186         g_free(info);
187 }
188
189 static GSList *session_parse_allowed_bearers(DBusMessageIter *iter)
190 {
191         struct bearer_info *info;
192         DBusMessageIter array;
193         GSList *list = NULL;
194
195         dbus_message_iter_recurse(iter, &array);
196
197         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) {
198                 char *bearer = NULL;
199
200                 dbus_message_iter_get_basic(&array, &bearer);
201
202                 info = g_try_new0(struct bearer_info, 1);
203                 if (info == NULL) {
204                         g_slist_foreach(list, cleanup_bearer_info, NULL);
205                         g_slist_free(list);
206
207                         return NULL;
208                 }
209
210                 info->name = g_strdup(bearer);
211                 info->service_type = bearer2service(info->name);
212
213                 if (info->service_type == CONNMAN_SERVICE_TYPE_UNKNOWN &&
214                                 g_strcmp0(info->name, "*") == 0) {
215                         info->match_all = TRUE;
216                 } else {
217                         info->match_all = FALSE;
218                 }
219
220                 list = g_slist_append(list, info);
221
222                 dbus_message_iter_next(&array);
223         }
224
225         return list;
226 }
227
228 static GSList *session_allowed_bearers_any(void)
229 {
230         struct bearer_info *info;
231         GSList *list = NULL;
232
233         info = g_try_new0(struct bearer_info, 1);
234         if (info == NULL) {
235                 g_slist_free(list);
236
237                 return NULL;
238         }
239
240         info->name = g_strdup("");
241         info->match_all = TRUE;
242         info->service_type = CONNMAN_SERVICE_TYPE_UNKNOWN;
243
244         list = g_slist_append(list, info);
245
246         return list;
247 }
248
249 static void append_allowed_bearers(DBusMessageIter *iter, void *user_data)
250 {
251         struct connman_session *session = user_data;
252         GSList *list;
253
254         for (list = session->allowed_bearers; list != NULL; list = list->next) {
255                 struct bearer_info *info = list->data;
256
257                 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
258                                                 &info->name);
259         }
260 }
261
262 static void append_ipconfig_ipv4(DBusMessageIter *iter, void *user_data)
263 {
264         struct connman_service *service = user_data;
265         struct connman_ipconfig *ipconfig_ipv4;
266
267         if (service == NULL)
268                 return;
269
270         ipconfig_ipv4 = __connman_service_get_ip4config(service);
271         if (ipconfig_ipv4 == NULL)
272                 return;
273
274         __connman_ipconfig_append_ipv4(ipconfig_ipv4, iter);
275 }
276
277 static void append_ipconfig_ipv6(DBusMessageIter *iter, void *user_data)
278 {
279         struct connman_service *service = user_data;
280         struct connman_ipconfig *ipconfig_ipv4, *ipconfig_ipv6;
281
282         if (service == NULL)
283                 return;
284
285         ipconfig_ipv4 = __connman_service_get_ip4config(service);
286         ipconfig_ipv6 = __connman_service_get_ip6config(service);
287         if (ipconfig_ipv6 == NULL)
288                 return;
289
290         __connman_ipconfig_append_ipv6(ipconfig_ipv6, iter, ipconfig_ipv4);
291 }
292
293 static void append_service(DBusMessageIter *dict,
294                                         struct connman_session *session)
295 {
296         connman_dbus_dict_append_basic(dict, "Bearer",
297                                         DBUS_TYPE_STRING, &session->bearer);
298
299         connman_dbus_dict_append_basic(dict, "Online",
300                                         DBUS_TYPE_BOOLEAN, &session->online);
301
302         connman_dbus_dict_append_basic(dict, "Name",
303                                         DBUS_TYPE_STRING, &session->name);
304
305         connman_dbus_dict_append_dict(dict, "IPv4",
306                                         append_ipconfig_ipv4, session->service);
307
308         connman_dbus_dict_append_dict(dict, "IPv6",
309                                         append_ipconfig_ipv6, session->service);
310
311         connman_dbus_dict_append_basic(dict, "Interface",
312                                         DBUS_TYPE_STRING, &session->ifname);
313
314 }
315
316 static void append_notify_all(DBusMessageIter *dict,
317                                         struct connman_session *session)
318 {
319         const char *policy;
320
321         append_service(dict, session);
322
323         connman_dbus_dict_append_basic(dict, "Priority",
324                                         DBUS_TYPE_BOOLEAN, &session->priority);
325
326         connman_dbus_dict_append_array(dict, "AllowedBearers",
327                                         DBUS_TYPE_STRING,
328                                         append_allowed_bearers,
329                                         session);
330
331         connman_dbus_dict_append_basic(dict, "AvoidHandover",
332                                         DBUS_TYPE_BOOLEAN,
333                                         &session->avoid_handover);
334
335         connman_dbus_dict_append_basic(dict, "StayConnected",
336                                         DBUS_TYPE_BOOLEAN,
337                                         &session->stay_connected);
338
339         connman_dbus_dict_append_basic(dict, "PeriodicConnect",
340                                         DBUS_TYPE_UINT32,
341                                         &session->periodic_connect);
342
343         connman_dbus_dict_append_basic(dict, "IdleTimeout",
344                                         DBUS_TYPE_UINT32,
345                                         &session->idle_timeout);
346
347         connman_dbus_dict_append_basic(dict, "EmergencyCall",
348                                         DBUS_TYPE_BOOLEAN, &session->ecall);
349
350         policy = roamingpolicy2string(session->roaming_policy);
351         connman_dbus_dict_append_basic(dict, "RoamingPolicy",
352                                         DBUS_TYPE_STRING,
353                                         &policy);
354
355         connman_dbus_dict_append_basic(dict, "SessionMarker",
356                                         DBUS_TYPE_UINT32, &session->marker);
357 }
358
359 static gboolean session_notify_all(gpointer user_data)
360 {
361         struct connman_session *session = user_data;
362         DBusMessage *msg;
363         DBusMessageIter array, dict;
364
365         DBG("session %p owner %s notify_path %s", session,
366                 session->owner, session->notify_path);
367
368         msg = dbus_message_new_method_call(session->owner, session->notify_path,
369                                                 CONNMAN_NOTIFICATION_INTERFACE,
370                                                 "Update");
371         if (msg == NULL) {
372                 connman_error("Could not create notification message");
373                 return FALSE;
374         }
375
376         dbus_message_iter_init_append(msg, &array);
377         connman_dbus_dict_open(&array, &dict);
378
379         append_notify_all(&dict, session);
380
381         connman_dbus_dict_close(&array, &dict);
382
383         g_dbus_send_message(connection, msg);
384
385         return FALSE;
386 }
387
388 static gboolean service_changed(gpointer user_data)
389 {
390         struct connman_session *session = user_data;
391
392
393         DBusMessage *msg;
394         DBusMessageIter array, dict;
395
396         DBG("session %p owner %s notify_path %s", session,
397                 session->owner, session->notify_path);
398
399         msg = dbus_message_new_method_call(session->owner, session->notify_path,
400                                                 CONNMAN_NOTIFICATION_INTERFACE,
401                                                 "Update");
402         if (msg == NULL) {
403                 connman_error("Could not create notification message");
404                 return FALSE;
405         }
406
407         dbus_message_iter_init_append(msg, &array);
408         connman_dbus_dict_open(&array, &dict);
409
410         append_service(&dict, session);
411
412         connman_dbus_dict_close(&array, &dict);
413
414         g_dbus_send_message(connection, msg);
415
416         return FALSE;
417 }
418
419 static void online_changed(struct connman_session *session)
420 {
421         connman_dbus_setting_changed_basic(session->owner, session->notify_path,
422                                                 "Online", DBUS_TYPE_BOOLEAN,
423                                                 &session->online);
424 }
425
426 static void ipconfig_ipv4_changed(struct connman_session *session)
427 {
428         connman_dbus_setting_changed_dict(session->owner, session->notify_path,
429                                         "IPv4", append_ipconfig_ipv4, session->service);
430 }
431
432 static void ipconfig_ipv6_changed(struct connman_session *session)
433 {
434         connman_dbus_setting_changed_dict(session->owner, session->notify_path,
435                                         "IPv6", append_ipconfig_ipv6, session->service);
436 }
437
438 static void update_service(struct connman_session *session)
439 {
440         int idx;
441
442         if (session->service != NULL) {
443                 session->bearer = session2bearer(session);
444                 session->online =
445                         __connman_service_is_connected(session->service);
446                 session->name = __connman_service_get_name(session->service);
447                 idx = __connman_service_get_index(session->service);
448                 session->ifname = connman_inet_ifname(idx);
449
450         } else {
451                 session->bearer = "";
452                 session->online = FALSE;
453                 session->name = "";
454                 session->ifname = "";
455         }
456 }
457
458 static connman_bool_t service_match(struct connman_session *session,
459                                         struct connman_service *service)
460 {
461         GSList *list;
462
463         DBG("session %p service %p", session, service);
464
465         for (list = session->allowed_bearers; list != NULL; list = list->next) {
466                 struct bearer_info *info = list->data;
467                 enum connman_service_type service_type;
468
469                 if (info->match_all == TRUE)
470                         return TRUE;
471
472                 service_type = connman_service_get_type(service);
473                 if (info->service_type == service_type)
474                         return TRUE;
475         }
476
477         return FALSE;
478 }
479
480 static connman_bool_t session_select_service(struct connman_session *session)
481 {
482         struct connman_service *service;
483         GSequenceIter *iter;
484
485         session->service_list =
486                 __connman_service_get_list(session, service_match);
487
488         if (session->service_list == NULL)
489                 return FALSE;
490
491         iter = g_sequence_get_begin_iter(session->service_list);
492
493         while (g_sequence_iter_is_end(iter) == FALSE) {
494                 service = g_sequence_get(iter);
495
496                 if (__connman_service_is_connected(service) == TRUE) {
497                         session->service = service;
498                         return TRUE;
499                 }
500                 iter = g_sequence_iter_next(iter);
501         }
502
503         return FALSE;
504 }
505
506 static void print_name(gpointer data, gpointer user_data)
507 {
508         struct connman_service *service = data;
509
510         DBG("service %p name %s", service,
511                 __connman_service_get_name(service));
512 }
513
514 static void cleanup_session(gpointer user_data)
515 {
516         struct connman_session *session = user_data;
517
518         DBG("remove %s", session->session_path);
519
520         g_sequence_free(session->service_list);
521
522         g_slist_foreach(session->allowed_bearers, cleanup_bearer_info, NULL);
523         g_slist_free(session->allowed_bearers);
524
525         g_free(session->owner);
526         g_free(session->session_path);
527         g_free(session->notify_path);
528
529         g_free(session);
530 }
531
532 static void release_session(gpointer key, gpointer value, gpointer user_data)
533 {
534         struct connman_session *session = value;
535         DBusMessage *message;
536
537         DBG("owner %s path %s", session->owner, session->notify_path);
538
539         if (session->notify_watch > 0)
540                 g_dbus_remove_watch(connection, session->notify_watch);
541
542         g_dbus_unregister_interface(connection, session->session_path,
543                                                 CONNMAN_SESSION_INTERFACE);
544
545         message = dbus_message_new_method_call(session->owner,
546                                                 session->notify_path,
547                                                 CONNMAN_NOTIFICATION_INTERFACE,
548                                                 "Release");
549         if (message == NULL)
550                 return;
551
552         dbus_message_set_no_reply(message, TRUE);
553
554         g_dbus_send_message(connection, message);
555 }
556
557 static int session_disconnect(struct connman_session *session)
558 {
559         DBG("session %p, %s", session, session->owner);
560
561         if (session->notify_watch > 0)
562                 g_dbus_remove_watch(connection, session->notify_watch);
563
564         g_dbus_unregister_interface(connection, session->session_path,
565                                                 CONNMAN_SESSION_INTERFACE);
566
567         g_hash_table_remove(session_hash, session->session_path);
568
569         return 0;
570 }
571
572 static void owner_disconnect(DBusConnection *conn, void *user_data)
573 {
574         struct connman_session *session = user_data;
575
576         DBG("session %p, %s died", session, session->owner);
577
578         session_disconnect(session);
579 }
580
581 static DBusMessage *destroy_session(DBusConnection *conn,
582                                         DBusMessage *msg, void *user_data)
583 {
584         struct connman_session *session = user_data;
585
586         DBG("session %p", session);
587
588         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
589 }
590
591 static gboolean session_do_connect(gpointer user_data)
592 {
593         struct connman_session *session = user_data;
594
595         __connman_service_connect(session->service);
596
597         service_changed(session);
598         return FALSE;
599 }
600
601 static DBusMessage *connect_session(DBusConnection *conn,
602                                         DBusMessage *msg, void *user_data)
603 {
604         struct connman_session *session = user_data;
605         struct connman_service *service = NULL;
606         GSourceFunc callback;
607         GSequenceIter *iter;
608
609         DBG("session %p", session);
610
611         if (session->service_list != NULL)
612                 g_sequence_free(session->service_list);
613
614         session->service_list = __connman_service_get_list(session,
615                                                                 service_match);
616
617         g_sequence_foreach(session->service_list, print_name, NULL);
618
619
620         iter = g_sequence_get_begin_iter(session->service_list);
621
622         while (g_sequence_iter_is_end(iter) == FALSE) {
623                 service = g_sequence_get(iter);
624
625                 if (__connman_service_is_connecting(service) == TRUE ||
626                                 __connman_service_is_connected(service) == TRUE) {
627                         callback = service_changed;
628                         break;
629                 }
630
631                 if (__connman_service_is_idle(service) == TRUE) {
632                         callback = session_do_connect;
633                         break;
634                 }
635
636                 iter = g_sequence_iter_next(iter);
637         }
638
639         if (session != NULL) {
640                 session->service = service;
641                 update_service(session);
642                 g_timeout_add_seconds(0, callback, session);
643         }
644
645         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
646 }
647
648 static gboolean session_do_disconnect(gpointer user_data)
649 {
650         struct connman_session *session = user_data;
651
652         if (__connman_service_disconnect(session->service) < 0)
653                 return FALSE;
654
655         session->service = NULL;
656         update_service(session);
657         service_changed(session);
658
659         return FALSE;
660 }
661
662 static DBusMessage *disconnect_session(DBusConnection *conn,
663                                         DBusMessage *msg, void *user_data)
664 {
665         struct connman_session *session = user_data;
666
667         DBG("session %p", session);
668
669         if (session->service == NULL)
670                 return __connman_error_already_disabled(msg);
671
672         g_timeout_add_seconds(0, session_do_disconnect, session);
673
674         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
675 }
676
677 static DBusMessage *change_session(DBusConnection *conn,
678                                         DBusMessage *msg, void *user_data)
679 {
680         struct connman_session *session = user_data;
681         DBusMessageIter iter, value;
682         DBusMessage *reply;
683         DBusMessageIter reply_array, reply_dict;
684         const char *name;
685         GSList *allowed_bearers;
686
687         DBG("session %p", session);
688         if (dbus_message_iter_init(msg, &iter) == FALSE)
689                 return __connman_error_invalid_arguments(msg);
690
691         reply = dbus_message_new_method_call(session->owner,
692                                                 session->notify_path,
693                                                 CONNMAN_NOTIFICATION_INTERFACE,
694                                                 "Update");
695         if (reply == NULL)
696                 return __connman_error_failed(msg, ENOMEM);
697
698         dbus_message_iter_init_append(reply, &reply_array);
699         connman_dbus_dict_open(&reply_array, &reply_dict);
700
701         dbus_message_iter_get_basic(&iter, &name);
702         dbus_message_iter_next(&iter);
703         dbus_message_iter_recurse(&iter, &value);
704
705         switch (dbus_message_iter_get_arg_type(&value)) {
706         case DBUS_TYPE_ARRAY:
707                 if (g_str_equal(name, "AllowedBearers") == TRUE) {
708                         allowed_bearers = session_parse_allowed_bearers(&value);
709
710                         g_slist_foreach(session->allowed_bearers,
711                                         cleanup_bearer_info, NULL);
712                         g_slist_free(session->allowed_bearers);
713
714                         if (allowed_bearers == NULL) {
715                                 allowed_bearers = session_allowed_bearers_any();
716
717                                 if (allowed_bearers == NULL) {
718                                         dbus_message_unref(reply);
719                                         return __connman_error_failed(msg, ENOMEM);
720                                 }
721                         }
722
723                         session->allowed_bearers = allowed_bearers;
724
725                         /* update_allowed_bearers(); */
726
727                         connman_dbus_dict_append_array(&reply_dict,
728                                                         "AllowedBearers",
729                                                         DBUS_TYPE_STRING,
730                                                         append_allowed_bearers,
731                                                         session);
732                 } else {
733                         goto err;
734                 }
735                 break;
736         case DBUS_TYPE_BOOLEAN:
737                 if (g_str_equal(name, "Priority") == TRUE) {
738                         dbus_message_iter_get_basic(&value, &session->priority);
739
740                         /* update_priority(); */
741
742                         connman_dbus_dict_append_basic(&reply_dict, "Priority",
743                                                         DBUS_TYPE_BOOLEAN,
744                                                         &session->priority);
745
746                 } else if (g_str_equal(name, "AvoidHandover") == TRUE) {
747                         dbus_message_iter_get_basic(&value,
748                                                 &session->avoid_handover);
749
750                         /* update_avoid_handover(); */
751
752                         connman_dbus_dict_append_basic(&reply_dict,
753                                                 "AvoidHandover",
754                                                 DBUS_TYPE_BOOLEAN,
755                                                 &session->avoid_handover);
756
757                 } else if (g_str_equal(name, "StayConnected") == TRUE) {
758                         dbus_message_iter_get_basic(&value,
759                                                 &session->stay_connected);
760
761                         /* update_stay_connected(); */
762
763                         connman_dbus_dict_append_basic(&reply_dict,
764                                                 "StayConnected",
765                                                 DBUS_TYPE_BOOLEAN,
766                                                 &session->stay_connected);
767
768                 } else if (g_str_equal(name, "EmergencyCall") == TRUE) {
769                         dbus_message_iter_get_basic(&value, &session->ecall);
770
771                         /* update_ecall(); */
772
773                         connman_dbus_dict_append_basic(&reply_dict,
774                                                 "EmergencyCall",
775                                                 DBUS_TYPE_BOOLEAN,
776                                                 &session->ecall);
777
778                 } else {
779                         goto err;
780                 }
781                 break;
782         case DBUS_TYPE_UINT32:
783                 if (g_str_equal(name, "PeriodicConnect") == TRUE) {
784                         dbus_message_iter_get_basic(&value,
785                                                 &session->periodic_connect);
786
787                         /* update_periodic_update(); */
788
789                         connman_dbus_dict_append_basic(&reply_dict,
790                                                 "PeriodicConnect",
791                                                 DBUS_TYPE_UINT32,
792                                                 &session->periodic_connect);
793                 } else if (g_str_equal(name, "IdleTimeout") == TRUE) {
794                         dbus_message_iter_get_basic(&value,
795                                                 &session->idle_timeout);
796
797                         /* update_idle_timeout(); */
798
799                         connman_dbus_dict_append_basic(&reply_dict,
800                                                 "IdleTimeout",
801                                                 DBUS_TYPE_UINT32,
802                                                 &session->idle_timeout);
803                 } else {
804                         goto err;
805                 }
806                 break;
807         case DBUS_TYPE_STRING:
808                 if (g_str_equal(name, "RoamingPolicy") == TRUE) {
809                         const char *val;
810                         dbus_message_iter_get_basic(&value, &val);
811                         session->roaming_policy = string2roamingpolicy(val);
812
813                         /* update_roaming_allowed(); */
814
815                         val = roamingpolicy2string(session->roaming_policy);
816                         connman_dbus_dict_append_basic(&reply_dict,
817                                                 "RoamingPolicy",
818                                                 DBUS_TYPE_STRING,
819                                                 &val);
820                 } else {
821                         goto err;
822                 }
823                 break;
824         }
825
826         connman_dbus_dict_close(&reply_array, &reply_dict);
827
828         g_dbus_send_message(connection, reply);
829
830         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
831
832 err:
833         dbus_message_unref(reply);
834         return __connman_error_invalid_arguments(msg);
835 }
836
837 static GDBusMethodTable session_methods[] = {
838         { "Destroy",    "",   "", destroy_session    },
839         { "Connect",    "",   "", connect_session    },
840         { "Disconnect", "",   "", disconnect_session },
841         { "Change",     "sv", "", change_session     },
842         { },
843 };
844
845 int __connman_session_create(DBusMessage *msg)
846 {
847         const char *owner, *notify_path;
848         char *session_path;
849         DBusMessageIter iter, array;
850         struct connman_session *session;
851
852         connman_bool_t priority = FALSE, avoid_handover = FALSE;
853         connman_bool_t stay_connected = FALSE, ecall = FALSE;
854         enum connman_session_roaming_policy roaming_policy =
855                                 CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN;
856         GSList *allowed_bearers = NULL;
857         unsigned int periodic_connect = 0;
858         unsigned int idle_timeout = 0;
859
860         int err;
861
862         owner = dbus_message_get_sender(msg);
863
864         DBG("owner %s", owner);
865
866         dbus_message_iter_init(msg, &iter);
867         dbus_message_iter_recurse(&iter, &array);
868
869         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
870                 DBusMessageIter entry, value;
871                 const char *key, *val;
872
873                 dbus_message_iter_recurse(&array, &entry);
874                 dbus_message_iter_get_basic(&entry, &key);
875
876                 dbus_message_iter_next(&entry);
877                 dbus_message_iter_recurse(&entry, &value);
878
879                 switch (dbus_message_iter_get_arg_type(&value)) {
880                 case DBUS_TYPE_ARRAY:
881                         if (g_str_equal(key, "AllowedBearers") == TRUE) {
882                                 allowed_bearers =
883                                         session_parse_allowed_bearers(&value);
884                         } else {
885                                 return -EINVAL;
886                         }
887                         break;
888                 case DBUS_TYPE_BOOLEAN:
889                         if (g_str_equal(key, "Priority") == TRUE) {
890                                 dbus_message_iter_get_basic(&value,
891                                                         &priority);
892                         } else if (g_str_equal(key, "AvoidHandover") == TRUE) {
893                                 dbus_message_iter_get_basic(&value,
894                                                         &avoid_handover);
895                         } else if (g_str_equal(key, "StayConnected") == TRUE) {
896                                 dbus_message_iter_get_basic(&value,
897                                                         &stay_connected);
898                         } else if (g_str_equal(key, "EmergencyCall") == TRUE) {
899                                 dbus_message_iter_get_basic(&value,
900                                                         &ecall);
901                         } else {
902                                 return -EINVAL;
903                         }
904                         break;
905                 case DBUS_TYPE_UINT32:
906                         if (g_str_equal(key, "PeriodicConnect") == TRUE) {
907                                 dbus_message_iter_get_basic(&value,
908                                                         &periodic_connect);
909                         } else if (g_str_equal(key, "IdleTimeout") == TRUE) {
910                                 dbus_message_iter_get_basic(&value,
911                                                         &idle_timeout);
912                         } else {
913                                 return -EINVAL;
914                         }
915                         break;
916                 case DBUS_TYPE_STRING:
917                         if (g_str_equal(key, "RoamingPolicy") == TRUE) {
918                                 dbus_message_iter_get_basic(&value, &val);
919                                 roaming_policy = string2roamingpolicy(val);
920                         } else {
921                                 return -EINVAL;
922                         }
923                 }
924                 dbus_message_iter_next(&array);
925         }
926
927         dbus_message_iter_next(&iter);
928         dbus_message_iter_get_basic(&iter, &notify_path);
929
930         if (notify_path == NULL) {
931                 session_path = NULL;
932                 err = -EINVAL;
933                 goto err;
934         }
935
936         session_path = g_strdup_printf("/sessions%s", notify_path);
937         if (session_path == NULL) {
938                 err = -ENOMEM;
939                 goto err;
940         }
941
942         session = g_hash_table_lookup(session_hash, session_path);
943         if (session != NULL) {
944                 err = -EEXIST;
945                 goto err;
946         }
947
948         session = g_try_new0(struct connman_session, 1);
949         if (session == NULL) {
950                 err = -ENOMEM;
951                 goto err;
952         }
953
954         session->owner = g_strdup(owner);
955         session->session_path = session_path;
956         session->notify_path = g_strdup(notify_path);
957         session->notify_watch =
958                 g_dbus_add_disconnect_watch(connection, session->owner,
959                                         owner_disconnect, session, NULL);
960
961         session->bearer = "";
962         session->online = FALSE;
963         session->priority = priority;
964         session->avoid_handover = avoid_handover;
965         session->stay_connected = stay_connected;
966         session->periodic_connect = periodic_connect;
967         session->idle_timeout = idle_timeout;
968         session->ecall = ecall;
969         session->roaming_policy = roaming_policy;
970
971         if (session->allowed_bearers == NULL) {
972                 session->allowed_bearers = session_allowed_bearers_any();
973
974                 if (session->allowed_bearers == NULL) {
975                         err = -ENOMEM;
976                         goto err;
977                 }
978         }
979
980         session->service_list = NULL;
981
982         update_service(session);
983
984         g_hash_table_replace(session_hash, session->session_path, session);
985
986         DBG("add %s", session->session_path);
987
988         if (g_dbus_register_interface(connection, session->session_path,
989                                         CONNMAN_SESSION_INTERFACE,
990                                         session_methods, NULL,
991                                         NULL, session, NULL) == FALSE) {
992                 connman_error("Failed to register %s", session->session_path);
993                 g_hash_table_remove(session_hash, session->session_path);
994                 session = NULL;
995
996                 err = -EINVAL;
997                 goto err;
998         }
999
1000         g_dbus_send_reply(connection, msg,
1001                                 DBUS_TYPE_OBJECT_PATH, &session->session_path,
1002                                 DBUS_TYPE_INVALID);
1003
1004
1005         /*
1006          * Check if the session settings matches to a service which is
1007          * a already connected
1008          */
1009         if (session_select_service(session) == TRUE)
1010                 update_service(session);
1011
1012         g_timeout_add_seconds(0, session_notify_all, session);
1013
1014         return 0;
1015
1016 err:
1017         connman_error("Failed to create session");
1018         g_free(session_path);
1019
1020         g_slist_foreach(allowed_bearers, cleanup_bearer_info, NULL);
1021         g_slist_free(allowed_bearers);
1022
1023         return err;
1024 }
1025
1026 int __connman_session_destroy(DBusMessage *msg)
1027 {
1028         const char *owner, *session_path;
1029         struct connman_session *session;
1030
1031         owner = dbus_message_get_sender(msg);
1032
1033         DBG("owner %s", owner);
1034
1035         dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &session_path,
1036                                                         DBUS_TYPE_INVALID);
1037         if (session_path == NULL)
1038                 return -EINVAL;
1039
1040         session = g_hash_table_lookup(session_hash, session_path);
1041         if (session == NULL)
1042                 return -EINVAL;
1043
1044         if (g_strcmp0(owner, session->owner) != 0)
1045                 return -EACCES;
1046
1047         session_disconnect(session);
1048
1049         return 0;
1050 }
1051
1052 connman_bool_t __connman_session_mode()
1053 {
1054         return sessionmode;
1055 }
1056
1057 void __connman_session_set_mode(connman_bool_t enable)
1058 {
1059         DBG("enable %d", enable);
1060
1061         if (sessionmode == enable)
1062                 return;
1063
1064         sessionmode = enable;
1065
1066         if (sessionmode == TRUE)
1067                 __connman_service_disconnect_all();
1068 }
1069
1070 static void service_state_changed(struct connman_service *service,
1071                                         enum connman_service_state state)
1072 {
1073         GHashTableIter iter;
1074         gpointer key, value;
1075         struct connman_session *session;
1076         connman_bool_t online;
1077
1078         DBG("service %p state %d", service, state);
1079
1080         g_hash_table_iter_init(&iter, session_hash);
1081
1082         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1083                 session = value;
1084
1085                 if (session->service == service) {
1086                         online = __connman_service_is_connected(service);
1087                         if (session->online == online)
1088                                 continue;
1089
1090                         session->online = online;
1091                         online_changed(session);
1092                 }
1093         }
1094 }
1095
1096 static void ipconfig_changed(struct connman_service *service,
1097                                 struct connman_ipconfig *ipconfig)
1098 {
1099         GHashTableIter iter;
1100         gpointer key, value;
1101         struct connman_session *session;
1102         enum connman_ipconfig_type type;
1103
1104         DBG("service %p ipconfig %p", service, ipconfig);
1105
1106         type = __connman_ipconfig_get_config_type(ipconfig);
1107
1108         g_hash_table_iter_init(&iter, session_hash);
1109
1110         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1111                 session = value;
1112
1113                 if (session->service == service) {
1114                         if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
1115                                 ipconfig_ipv4_changed(session);
1116                         else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
1117                                 ipconfig_ipv6_changed(session);
1118                 }
1119         }
1120 }
1121
1122 static struct connman_notifier session_notifier = {
1123         .name                   = "session",
1124         .service_state_changed  = service_state_changed,
1125         .ipconfig_changed       = ipconfig_changed,
1126 };
1127
1128 int __connman_session_init(void)
1129 {
1130         int err;
1131
1132         DBG("");
1133
1134         connection = connman_dbus_get_connection();
1135         if (connection == NULL)
1136                 return -1;
1137
1138         err = connman_notifier_register(&session_notifier);
1139         if (err < 0) {
1140                 dbus_connection_unref(connection);
1141                 return err;
1142         }
1143
1144         session_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1145                                                 NULL, cleanup_session);
1146
1147         sessionmode = FALSE;
1148         return 0;
1149 }
1150
1151 void __connman_session_cleanup(void)
1152 {
1153         DBG("");
1154
1155         if (connection == NULL)
1156                 return;
1157
1158         connman_notifier_unregister(&session_notifier);
1159
1160         g_hash_table_foreach(session_hash, release_session, NULL);
1161         g_hash_table_destroy(session_hash);
1162
1163         dbus_connection_unref(connection);
1164 }