session: No need to free NULL list
[platform/upstream/connman.git] / src / session.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2012  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 <errno.h>
28
29 #include <gdbus.h>
30
31 #include <connman/session.h>
32
33 #include "connman.h"
34
35 static DBusConnection *connection;
36 static GHashTable *session_hash;
37 static connman_bool_t sessionmode;
38 static struct connman_session *ecall_session;
39 static struct connman_session_config *session_config;
40
41 enum connman_session_trigger {
42         CONNMAN_SESSION_TRIGGER_UNKNOWN         = 0,
43         CONNMAN_SESSION_TRIGGER_SETTING         = 1,
44         CONNMAN_SESSION_TRIGGER_CONNECT         = 2,
45         CONNMAN_SESSION_TRIGGER_DISCONNECT      = 3,
46         CONNMAN_SESSION_TRIGGER_SERVICE         = 4,
47         CONNMAN_SESSION_TRIGGER_ECALL           = 5,
48 };
49
50 enum connman_session_reason {
51         CONNMAN_SESSION_REASON_UNKNOWN          = 0,
52         CONNMAN_SESSION_REASON_CONNECT          = 1,
53         CONNMAN_SESSION_REASON_FREE_RIDE        = 2,
54 };
55
56 enum connman_session_state {
57         CONNMAN_SESSION_STATE_DISCONNECTED   = 0,
58         CONNMAN_SESSION_STATE_CONNECTED      = 1,
59         CONNMAN_SESSION_STATE_ONLINE         = 2,
60 };
61
62 enum connman_session_type {
63         CONNMAN_SESSION_TYPE_ANY      = 0,
64         CONNMAN_SESSION_TYPE_LOCAL    = 1,
65         CONNMAN_SESSION_TYPE_INTERNET = 2,
66 };
67
68 enum connman_session_roaming_policy {
69         CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN          = 0,
70         CONNMAN_SESSION_ROAMING_POLICY_DEFAULT          = 1,
71         CONNMAN_SESSION_ROAMING_POLICY_ALWAYS           = 2,
72         CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN        = 3,
73         CONNMAN_SESSION_ROAMING_POLICY_NATIONAL         = 4,
74         CONNMAN_SESSION_ROAMING_POLICY_INTERNATIONAL    = 5,
75 };
76
77 struct service_entry {
78         /* track why this service was selected */
79         enum connman_session_reason reason;
80         enum connman_service_state state;
81         const char *name;
82         struct connman_service *service;
83         char *ifname;
84         const char *bearer;
85         GSList *pending_timeouts;
86 };
87
88 struct session_info {
89         enum connman_session_state state;
90         enum connman_session_type type;
91         connman_bool_t priority;
92         GSList *allowed_bearers;
93         enum connman_session_roaming_policy roaming_policy;
94
95         struct service_entry *entry;
96         enum connman_session_reason reason;
97 };
98
99 struct connman_session {
100         char *owner;
101         char *session_path;
102         char *notify_path;
103         guint notify_watch;
104
105         connman_bool_t append_all;
106         struct session_info *info;
107         struct session_info *info_last;
108
109         connman_bool_t ecall;
110
111         GSequence *service_list;
112         GHashTable *service_hash;
113 };
114
115 struct bearer_info {
116         char *name;
117         connman_bool_t match_all;
118         enum connman_service_type service_type;
119 };
120
121 static const char *trigger2string(enum connman_session_trigger trigger)
122 {
123         switch (trigger) {
124         case CONNMAN_SESSION_TRIGGER_UNKNOWN:
125                 break;
126         case CONNMAN_SESSION_TRIGGER_SETTING:
127                 return "setting";
128         case CONNMAN_SESSION_TRIGGER_CONNECT:
129                 return "connect";
130         case CONNMAN_SESSION_TRIGGER_DISCONNECT:
131                 return "disconnect";
132         case CONNMAN_SESSION_TRIGGER_SERVICE:
133                 return "service";
134         case CONNMAN_SESSION_TRIGGER_ECALL:
135                 return "ecall";
136         }
137
138         return NULL;
139 }
140
141 static const char *reason2string(enum connman_session_reason reason)
142 {
143         switch (reason) {
144         case CONNMAN_SESSION_REASON_UNKNOWN:
145                 return "unknown";
146         case CONNMAN_SESSION_REASON_CONNECT:
147                 return "connect";
148         case CONNMAN_SESSION_REASON_FREE_RIDE:
149                 return "free-ride";
150         }
151
152         return NULL;
153 }
154
155 static const char *state2string(enum connman_session_state state)
156 {
157         switch (state) {
158         case CONNMAN_SESSION_STATE_DISCONNECTED:
159                 return "disconnected";
160         case CONNMAN_SESSION_STATE_CONNECTED:
161                 return "connected";
162         case CONNMAN_SESSION_STATE_ONLINE:
163                 return "online";
164         }
165
166         return NULL;
167 }
168
169 static const char *type2string(enum connman_session_type type)
170 {
171         switch (type) {
172         case CONNMAN_SESSION_TYPE_ANY:
173                 return "";
174         case CONNMAN_SESSION_TYPE_LOCAL:
175                 return "local";
176         case CONNMAN_SESSION_TYPE_INTERNET:
177                 return "internet";
178         }
179
180         return NULL;
181 }
182
183 static enum connman_session_type string2type(const char *type)
184 {
185         if (g_strcmp0(type, "local") == 0)
186                 return CONNMAN_SESSION_TYPE_LOCAL;
187         else if (g_strcmp0(type, "internet") == 0)
188                 return CONNMAN_SESSION_TYPE_INTERNET;
189
190         return CONNMAN_SESSION_TYPE_ANY;
191 }
192
193 static enum connman_session_roaming_policy string2roamingpolicy(const char *policy)
194 {
195         if (g_strcmp0(policy, "default") == 0)
196                 return CONNMAN_SESSION_ROAMING_POLICY_DEFAULT;
197         else if (g_strcmp0(policy, "always") == 0)
198                 return CONNMAN_SESSION_ROAMING_POLICY_ALWAYS;
199         else if (g_strcmp0(policy, "forbidden") == 0)
200                 return CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN;
201         else if (g_strcmp0(policy, "national") == 0)
202                 return CONNMAN_SESSION_ROAMING_POLICY_NATIONAL;
203         else if (g_strcmp0(policy, "international") == 0)
204                 return CONNMAN_SESSION_ROAMING_POLICY_INTERNATIONAL;
205         else
206                 return CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN;
207 }
208
209 static enum connman_service_type bearer2service(const char *bearer)
210 {
211         if (bearer == NULL)
212                 return CONNMAN_SERVICE_TYPE_UNKNOWN;
213
214         if (g_strcmp0(bearer, "ethernet") == 0)
215                 return CONNMAN_SERVICE_TYPE_ETHERNET;
216         else if (g_strcmp0(bearer, "wifi") == 0)
217                 return CONNMAN_SERVICE_TYPE_WIFI;
218         else if (g_strcmp0(bearer, "wimax") == 0)
219                 return CONNMAN_SERVICE_TYPE_WIMAX;
220         else if (g_strcmp0(bearer, "bluetooth") == 0)
221                 return CONNMAN_SERVICE_TYPE_BLUETOOTH;
222         else if (g_strcmp0(bearer, "cellular") == 0)
223                 return CONNMAN_SERVICE_TYPE_CELLULAR;
224         else if (g_strcmp0(bearer, "vpn") == 0)
225                 return CONNMAN_SERVICE_TYPE_VPN;
226         else
227                 return CONNMAN_SERVICE_TYPE_UNKNOWN;
228 }
229
230 static char *service2bearer(enum connman_service_type type)
231 {
232         switch (type) {
233         case CONNMAN_SERVICE_TYPE_ETHERNET:
234                 return "ethernet";
235         case CONNMAN_SERVICE_TYPE_WIFI:
236                 return "wifi";
237         case CONNMAN_SERVICE_TYPE_WIMAX:
238                 return "wimax";
239         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
240                 return "bluetooth";
241         case CONNMAN_SERVICE_TYPE_CELLULAR:
242                 return "cellular";
243         case CONNMAN_SERVICE_TYPE_VPN:
244                 return "vpn";
245         case CONNMAN_SERVICE_TYPE_UNKNOWN:
246         case CONNMAN_SERVICE_TYPE_SYSTEM:
247         case CONNMAN_SERVICE_TYPE_GPS:
248         case CONNMAN_SERVICE_TYPE_GADGET:
249                 return "";
250         }
251
252         return "";
253 }
254
255 static int config_get_bool(const char *id, const char *key, connman_bool_t *val)
256 {
257         if (session_config == NULL) {
258                 *val = FALSE;
259                 return -EINVAL;
260         }
261
262         return (*session_config->get_bool)(id, key, val);
263 }
264
265 static int config_get_string(const char *id, const char *key, char **val)
266 {
267         if (session_config == NULL) {
268                 *val = NULL;
269                 return -EINVAL;
270         }
271
272         return (*session_config->get_string)(id, key, val);
273 }
274
275 static void cleanup_bearer_info(gpointer data, gpointer user_data)
276 {
277         struct bearer_info *info = data;
278
279         g_free(info->name);
280         g_free(info);
281 }
282
283 static GSList *session_parse_allowed_bearers(DBusMessageIter *iter)
284 {
285         struct bearer_info *info;
286         DBusMessageIter array;
287         GSList *list = NULL;
288
289         dbus_message_iter_recurse(iter, &array);
290
291         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) {
292                 char *bearer = NULL;
293
294                 dbus_message_iter_get_basic(&array, &bearer);
295
296                 info = g_try_new0(struct bearer_info, 1);
297                 if (info == NULL) {
298                         g_slist_foreach(list, cleanup_bearer_info, NULL);
299                         g_slist_free(list);
300
301                         return NULL;
302                 }
303
304                 info->name = g_strdup(bearer);
305                 info->service_type = bearer2service(info->name);
306
307                 if (info->service_type == CONNMAN_SERVICE_TYPE_UNKNOWN &&
308                                 g_strcmp0(info->name, "*") == 0) {
309                         info->match_all = TRUE;
310                 } else {
311                         info->match_all = FALSE;
312                 }
313
314                 list = g_slist_append(list, info);
315
316                 dbus_message_iter_next(&array);
317         }
318
319         return list;
320 }
321
322 static GSList *session_allowed_bearers_any(void)
323 {
324         struct bearer_info *info;
325         GSList *list = NULL;
326
327         info = g_try_new0(struct bearer_info, 1);
328         if (info == NULL)
329                 return NULL;
330
331         info->name = g_strdup("");
332         info->match_all = TRUE;
333         info->service_type = CONNMAN_SERVICE_TYPE_UNKNOWN;
334
335         list = g_slist_append(list, info);
336
337         return list;
338 }
339
340 static void append_allowed_bearers(DBusMessageIter *iter, void *user_data)
341 {
342         struct session_info *info = user_data;
343         GSList *list;
344
345         for (list = info->allowed_bearers;
346                         list != NULL; list = list->next) {
347                 struct bearer_info *bearer_info = list->data;
348
349                 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
350                                                 &bearer_info->name);
351         }
352 }
353
354 static void append_ipconfig_ipv4(DBusMessageIter *iter, void *user_data)
355 {
356         struct connman_service *service = user_data;
357         struct connman_ipconfig *ipconfig_ipv4;
358
359         if (service == NULL)
360                 return;
361
362         if (__connman_service_is_connected_state(service,
363                                 CONNMAN_IPCONFIG_TYPE_IPV4) == FALSE) {
364                 return;
365         }
366
367         ipconfig_ipv4 = __connman_service_get_ip4config(service);
368         if (ipconfig_ipv4 == NULL)
369                 return;
370
371         __connman_ipconfig_append_ipv4(ipconfig_ipv4, iter);
372 }
373
374 static void append_ipconfig_ipv6(DBusMessageIter *iter, void *user_data)
375 {
376         struct connman_service *service = user_data;
377         struct connman_ipconfig *ipconfig_ipv4, *ipconfig_ipv6;
378
379         if (service == NULL)
380                 return;
381
382         if (__connman_service_is_connected_state(service,
383                                 CONNMAN_IPCONFIG_TYPE_IPV6) == FALSE) {
384                 return;
385         }
386
387         ipconfig_ipv4 = __connman_service_get_ip4config(service);
388         ipconfig_ipv6 = __connman_service_get_ip6config(service);
389         if (ipconfig_ipv6 == NULL)
390                 return;
391
392         __connman_ipconfig_append_ipv6(ipconfig_ipv6, iter, ipconfig_ipv4);
393 }
394
395 static void append_notify(DBusMessageIter *dict,
396                                         struct connman_session *session)
397 {
398         struct session_info *info = session->info;
399         struct session_info *info_last = session->info_last;
400         struct connman_service *service;
401         const char *name, *ifname, *bearer;
402
403         if (session->append_all == TRUE ||
404                         info->state != info_last->state) {
405                 const char *state = state2string(info->state);
406
407                 connman_dbus_dict_append_basic(dict, "State",
408                                                 DBUS_TYPE_STRING,
409                                                 &state);
410                 info_last->state = info->state;
411         }
412
413         if (session->append_all == TRUE ||
414                         info->entry != info_last->entry) {
415                 if (info->entry == NULL) {
416                         name = "";
417                         ifname = "";
418                         service = NULL;
419                         bearer = "";
420                 } else {
421                         name = info->entry->name;
422                         ifname = info->entry->ifname;
423                         service = info->entry->service;
424                         bearer = info->entry->bearer;
425                 }
426
427                 connman_dbus_dict_append_basic(dict, "Name",
428                                                 DBUS_TYPE_STRING,
429                                                 &name);
430
431                 connman_dbus_dict_append_dict(dict, "IPv4",
432                                                 append_ipconfig_ipv4,
433                                                 service);
434
435                 connman_dbus_dict_append_dict(dict, "IPv6",
436                                                 append_ipconfig_ipv6,
437                                                 service);
438
439                 connman_dbus_dict_append_basic(dict, "Interface",
440                                                 DBUS_TYPE_STRING,
441                                                 &ifname);
442
443                 connman_dbus_dict_append_basic(dict, "Bearer",
444                                                 DBUS_TYPE_STRING,
445                                                 &bearer);
446
447                 info_last->entry = info->entry;
448         }
449
450         if (session->append_all == TRUE || info->type != info_last->type) {
451                 const char *type = type2string(info->type);
452
453                 connman_dbus_dict_append_basic(dict, "ConnectionType",
454                                                 DBUS_TYPE_STRING,
455                                                 &type);
456                 info_last->type = info->type;
457         }
458
459         if (session->append_all == TRUE ||
460                         info->allowed_bearers != info_last->allowed_bearers) {
461                 connman_dbus_dict_append_array(dict, "AllowedBearers",
462                                                 DBUS_TYPE_STRING,
463                                                 append_allowed_bearers,
464                                                 info);
465                 info_last->allowed_bearers = info->allowed_bearers;
466         }
467
468         session->append_all = FALSE;
469 }
470
471 static connman_bool_t is_type_matching_state(enum connman_session_state *state,
472                                                 enum connman_session_type type)
473 {
474         switch (type) {
475         case CONNMAN_SESSION_TYPE_ANY:
476                 return TRUE;
477         case CONNMAN_SESSION_TYPE_LOCAL:
478                 if (*state >= CONNMAN_SESSION_STATE_CONNECTED) {
479                         *state = CONNMAN_SESSION_STATE_CONNECTED;
480                         return TRUE;
481                 }
482
483                 break;
484         case CONNMAN_SESSION_TYPE_INTERNET:
485                 if (*state == CONNMAN_SESSION_STATE_ONLINE)
486                         return TRUE;
487                 break;
488         }
489
490         return FALSE;
491 }
492
493 static connman_bool_t compute_notifiable_changes(struct connman_session *session)
494 {
495         struct session_info *info_last = session->info_last;
496         struct session_info *info = session->info;
497
498         if (session->append_all == TRUE)
499                 return TRUE;
500
501         if (info->state != info_last->state)
502                 return TRUE;
503
504         if (info->entry != info_last->entry &&
505                         info->state >= CONNMAN_SESSION_STATE_CONNECTED)
506                 return TRUE;
507
508         if (info->allowed_bearers != info_last->allowed_bearers ||
509                         info->type != info_last->type)
510                 return TRUE;
511
512         return FALSE;
513 }
514
515 static gboolean session_notify(gpointer user_data)
516 {
517         struct connman_session *session = user_data;
518         DBusMessage *msg;
519         DBusMessageIter array, dict;
520
521         if (compute_notifiable_changes(session) == FALSE)
522                 return FALSE;
523
524         DBG("session %p owner %s notify_path %s", session,
525                 session->owner, session->notify_path);
526
527         msg = dbus_message_new_method_call(session->owner, session->notify_path,
528                                                 CONNMAN_NOTIFICATION_INTERFACE,
529                                                 "Update");
530         if (msg == NULL)
531                 return FALSE;
532
533         dbus_message_iter_init_append(msg, &array);
534         connman_dbus_dict_open(&array, &dict);
535
536         append_notify(&dict, session);
537
538         connman_dbus_dict_close(&array, &dict);
539
540         g_dbus_send_message(connection, msg);
541
542         return FALSE;
543 }
544
545 static void ipconfig_ipv4_changed(struct connman_session *session)
546 {
547         struct session_info *info = session->info;
548
549         connman_dbus_setting_changed_dict(session->owner, session->notify_path,
550                                                 "IPv4", append_ipconfig_ipv4,
551                                                 info->entry->service);
552 }
553
554 static void ipconfig_ipv6_changed(struct connman_session *session)
555 {
556         struct session_info *info = session->info;
557
558         connman_dbus_setting_changed_dict(session->owner, session->notify_path,
559                                                 "IPv6", append_ipconfig_ipv6,
560                                                 info->entry->service);
561 }
562
563 static connman_bool_t service_type_match(struct connman_session *session,
564                                         struct connman_service *service)
565 {
566         struct session_info *info = session->info;
567         GSList *list;
568
569         for (list = info->allowed_bearers;
570                         list != NULL; list = list->next) {
571                 struct bearer_info *bearer_info = list->data;
572                 enum connman_service_type service_type;
573
574                 if (bearer_info->match_all == TRUE)
575                         return TRUE;
576
577                 service_type = connman_service_get_type(service);
578                 if (bearer_info->service_type == service_type)
579                         return TRUE;
580         }
581
582         return FALSE;
583 }
584
585 static connman_bool_t service_match(struct connman_session *session,
586                                         struct connman_service *service)
587 {
588         if (service_type_match(session, service) == FALSE)
589                 return FALSE;
590
591         return TRUE;
592 }
593
594 static int service_type_weight(enum connman_service_type type)
595 {
596         /*
597          * The session doesn't care which service
598          * to use. Nevertheless we have to sort them
599          * according their type. The ordering is
600          *
601          * 1. Ethernet
602          * 2. Bluetooth
603          * 3. WiFi/WiMAX
604          * 4. Cellular
605          */
606
607         switch (type) {
608         case CONNMAN_SERVICE_TYPE_ETHERNET:
609                 return 4;
610         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
611                 return 3;
612         case CONNMAN_SERVICE_TYPE_WIFI:
613         case CONNMAN_SERVICE_TYPE_WIMAX:
614                 return 2;
615         case CONNMAN_SERVICE_TYPE_CELLULAR:
616                 return 1;
617         case CONNMAN_SERVICE_TYPE_UNKNOWN:
618         case CONNMAN_SERVICE_TYPE_SYSTEM:
619         case CONNMAN_SERVICE_TYPE_GPS:
620         case CONNMAN_SERVICE_TYPE_VPN:
621         case CONNMAN_SERVICE_TYPE_GADGET:
622                 break;
623         }
624
625         return 0;
626 }
627
628 static gint sort_allowed_bearers(struct connman_service *service_a,
629                                         struct connman_service *service_b,
630                                         struct connman_session *session)
631 {
632         struct session_info *info = session->info;
633         GSList *list;
634         enum connman_service_type type_a, type_b;
635         int weight_a, weight_b;
636
637         type_a = connman_service_get_type(service_a);
638         type_b = connman_service_get_type(service_b);
639
640         for (list = info->allowed_bearers;
641                         list != NULL; list = list->next) {
642                 struct bearer_info *bearer_info = list->data;
643
644                 if (bearer_info->match_all == TRUE) {
645                         if (type_a != type_b) {
646                                 weight_a = service_type_weight(type_a);
647                                 weight_b = service_type_weight(type_b);
648
649                                 if (weight_a > weight_b)
650                                         return -1;
651
652                                 if (weight_a < weight_b)
653                                         return 1;
654
655                                 return 0;
656                         }
657                 }
658
659                 if (type_a == bearer_info->service_type &&
660                                 type_b == bearer_info->service_type) {
661                         return 0;
662                 }
663
664                 if (type_a == bearer_info->service_type &&
665                                 type_b != bearer_info->service_type) {
666                         return -1;
667                 }
668
669                 if (type_a != bearer_info->service_type &&
670                                 type_b == bearer_info->service_type) {
671                         return 1;
672                 }
673         }
674
675         return 0;
676 }
677
678 static gint sort_services(gconstpointer a, gconstpointer b, gpointer user_data)
679 {
680         struct service_entry *entry_a = (void *)a;
681         struct service_entry *entry_b = (void *)b;
682         struct connman_session *session = user_data;
683
684         return sort_allowed_bearers(entry_a->service, entry_b->service,
685                                 session);
686 }
687
688 static void cleanup_session(gpointer user_data)
689 {
690         struct connman_session *session = user_data;
691         struct session_info *info = session->info;
692
693         DBG("remove %s", session->session_path);
694
695         g_hash_table_destroy(session->service_hash);
696         g_sequence_free(session->service_list);
697
698         if (info->entry != NULL &&
699                         info->entry->reason == CONNMAN_SESSION_REASON_CONNECT) {
700                 __connman_service_disconnect(info->entry->service);
701         }
702
703         g_slist_foreach(info->allowed_bearers, cleanup_bearer_info, NULL);
704         g_slist_free(info->allowed_bearers);
705
706         g_free(session->owner);
707         g_free(session->session_path);
708         g_free(session->notify_path);
709         g_free(session->info);
710         g_free(session->info_last);
711
712         g_free(session);
713 }
714
715 static enum connman_session_state service_to_session_state(enum connman_service_state state)
716 {
717         switch (state) {
718         case CONNMAN_SERVICE_STATE_UNKNOWN:
719         case CONNMAN_SERVICE_STATE_IDLE:
720         case CONNMAN_SERVICE_STATE_ASSOCIATION:
721         case CONNMAN_SERVICE_STATE_CONFIGURATION:
722         case CONNMAN_SERVICE_STATE_DISCONNECT:
723         case CONNMAN_SERVICE_STATE_FAILURE:
724                 break;
725         case CONNMAN_SERVICE_STATE_READY:
726                 return CONNMAN_SESSION_STATE_CONNECTED;
727         case CONNMAN_SERVICE_STATE_ONLINE:
728                 return CONNMAN_SESSION_STATE_ONLINE;
729         }
730
731         return CONNMAN_SESSION_STATE_DISCONNECTED;
732 }
733
734 static connman_bool_t is_connected(enum connman_service_state state)
735 {
736         switch (state) {
737         case CONNMAN_SERVICE_STATE_UNKNOWN:
738         case CONNMAN_SERVICE_STATE_IDLE:
739         case CONNMAN_SERVICE_STATE_ASSOCIATION:
740         case CONNMAN_SERVICE_STATE_CONFIGURATION:
741         case CONNMAN_SERVICE_STATE_DISCONNECT:
742         case CONNMAN_SERVICE_STATE_FAILURE:
743                 break;
744         case CONNMAN_SERVICE_STATE_READY:
745         case CONNMAN_SERVICE_STATE_ONLINE:
746                 return TRUE;
747         }
748
749         return FALSE;
750 }
751
752 static connman_bool_t is_connecting(enum connman_service_state state)
753 {
754         switch (state) {
755         case CONNMAN_SERVICE_STATE_UNKNOWN:
756         case CONNMAN_SERVICE_STATE_IDLE:
757                 break;
758         case CONNMAN_SERVICE_STATE_ASSOCIATION:
759         case CONNMAN_SERVICE_STATE_CONFIGURATION:
760                 return TRUE;
761         case CONNMAN_SERVICE_STATE_DISCONNECT:
762         case CONNMAN_SERVICE_STATE_FAILURE:
763         case CONNMAN_SERVICE_STATE_READY:
764         case CONNMAN_SERVICE_STATE_ONLINE:
765                 break;
766         }
767
768         return FALSE;
769 }
770
771 static connman_bool_t explicit_connect(enum connman_session_reason reason)
772 {
773         switch (reason) {
774         case CONNMAN_SESSION_REASON_UNKNOWN:
775         case CONNMAN_SESSION_REASON_FREE_RIDE:
776                 break;
777         case CONNMAN_SESSION_REASON_CONNECT:
778                 return TRUE;
779         }
780
781         return FALSE;
782 }
783
784 static connman_bool_t explicit_disconnect(struct session_info *info)
785 {
786         if (info->entry == NULL)
787                 return FALSE;
788
789         DBG("reason %s service %p state %d",
790                 reason2string(info->entry->reason),
791                 info->entry->service, info->entry->state);
792
793         if (info->entry->reason == CONNMAN_SESSION_REASON_UNKNOWN)
794                 return FALSE;
795
796         if (explicit_connect(info->entry->reason) == FALSE)
797                 return FALSE;
798
799         if (__connman_service_session_dec(info->entry->service) == FALSE)
800                 return FALSE;
801
802         return TRUE;
803 }
804
805 struct pending_data {
806         unsigned int timeout;
807         struct service_entry *entry;
808         gboolean (*cb)(gpointer);
809 };
810
811 static void pending_timeout_free(gpointer data, gpointer user_data)
812 {
813         struct pending_data *pending = data;
814
815         DBG("pending %p timeout %d", pending, pending->timeout);
816         g_source_remove(pending->timeout);
817         g_free(pending);
818 }
819
820 static void pending_timeout_remove_all(struct service_entry *entry)
821 {
822         DBG("");
823
824         g_slist_foreach(entry->pending_timeouts, pending_timeout_free, NULL);
825         g_slist_free(entry->pending_timeouts);
826         entry->pending_timeouts = NULL;
827 }
828
829 static gboolean pending_timeout_cb(gpointer data)
830 {
831         struct pending_data *pending = data;
832         struct service_entry *entry = pending->entry;
833         gboolean ret;
834
835         DBG("pending %p timeout %d", pending, pending->timeout);
836
837         ret = pending->cb(pending->entry);
838         if (ret == FALSE) {
839                 entry->pending_timeouts =
840                         g_slist_remove(entry->pending_timeouts,
841                                         pending);
842                 g_free(pending);
843         }
844         return ret;
845 }
846
847 static connman_bool_t pending_timeout_add(unsigned int seconds,
848                                         gboolean (*cb)(gpointer),
849                                         struct service_entry *entry)
850 {
851         struct pending_data *pending = g_try_new0(struct pending_data, 1);
852
853         if (pending == NULL || cb == NULL || entry == NULL) {
854                 g_free(pending);
855                 return FALSE;
856         }
857
858         pending->cb = cb;
859         pending->entry = entry;
860         pending->timeout = g_timeout_add_seconds(seconds, pending_timeout_cb,
861                                                 pending);
862         entry->pending_timeouts = g_slist_prepend(entry->pending_timeouts,
863                                                 pending);
864
865         DBG("pending %p entry %p timeout id %d", pending, entry,
866                 pending->timeout);
867
868         return TRUE;
869 }
870
871 static gboolean call_disconnect(gpointer user_data)
872 {
873         struct service_entry *entry = user_data;
874         struct connman_service *service = entry->service;
875
876         /*
877          * TODO: We should mark this entry as pending work. In case
878          * disconnect fails we just unassign this session from the
879          * service and can't do anything later on it
880          */
881         DBG("disconnect service %p", service);
882         __connman_service_disconnect(service);
883
884         return FALSE;
885 }
886
887 static gboolean call_connect(gpointer user_data)
888 {
889         struct service_entry *entry = user_data;
890         struct connman_service *service = entry->service;
891
892         DBG("connect service %p", service);
893         __connman_service_connect(service);
894
895         return FALSE;
896 }
897
898 static void deselect_service(struct session_info *info)
899 {
900         struct service_entry *entry;
901         connman_bool_t disconnect, connected;
902
903         DBG("");
904
905         if (info->entry == NULL)
906                 return;
907
908         disconnect = explicit_disconnect(info);
909
910         connected = is_connecting(info->entry->state) == TRUE ||
911                         is_connected(info->entry->state) == TRUE;
912
913         info->state = CONNMAN_SESSION_STATE_DISCONNECTED;
914         info->entry->reason = CONNMAN_SESSION_REASON_UNKNOWN;
915
916         entry = info->entry;
917         info->entry = NULL;
918
919         DBG("disconnect %d connected %d", disconnect, connected);
920
921         if (disconnect == TRUE && connected == TRUE)
922                 pending_timeout_add(0, call_disconnect, entry);
923 }
924
925 static void deselect_and_disconnect(struct connman_session *session)
926 {
927         struct session_info *info = session->info;
928
929         deselect_service(info);
930
931         info->reason = CONNMAN_SESSION_REASON_FREE_RIDE;
932 }
933
934 static void select_connected_service(struct session_info *info,
935                                         struct service_entry *entry)
936 {
937         enum connman_session_state state;
938
939         state = service_to_session_state(entry->state);
940         if (is_type_matching_state(&state, info->type) == FALSE)
941                 return;
942
943         info->state = state;
944
945         info->entry = entry;
946         info->entry->reason = info->reason;
947
948         if (explicit_connect(info->reason) == FALSE)
949                 return;
950
951         __connman_service_session_inc(info->entry->service);
952 }
953
954 static void select_offline_service(struct session_info *info,
955                                         struct service_entry *entry)
956 {
957         if (explicit_connect(info->reason) == FALSE)
958                 return;
959
960         info->state = service_to_session_state(entry->state);
961
962         info->entry = entry;
963         info->entry->reason = info->reason;
964
965         __connman_service_session_inc(info->entry->service);
966         pending_timeout_add(0, call_connect, entry);
967 }
968
969 static void select_service(struct session_info *info,
970                                 struct service_entry *entry)
971 {
972         DBG("service %p", entry->service);
973
974         if (is_connected(entry->state) == TRUE)
975                 select_connected_service(info, entry);
976         else
977                 select_offline_service(info, entry);
978 }
979
980 static void select_and_connect(struct connman_session *session,
981                                 enum connman_session_reason reason)
982 {
983         struct session_info *info = session->info;
984         struct service_entry *entry = NULL;
985         GSequenceIter *iter;
986
987         DBG("session %p reason %s", session, reason2string(reason));
988
989         info->reason = reason;
990
991         iter = g_sequence_get_begin_iter(session->service_list);
992
993         while (g_sequence_iter_is_end(iter) == FALSE) {
994                 entry = g_sequence_get(iter);
995
996                 switch (entry->state) {
997                 case CONNMAN_SERVICE_STATE_ASSOCIATION:
998                 case CONNMAN_SERVICE_STATE_CONFIGURATION:
999                 case CONNMAN_SERVICE_STATE_READY:
1000                 case CONNMAN_SERVICE_STATE_ONLINE:
1001                 case CONNMAN_SERVICE_STATE_IDLE:
1002                 case CONNMAN_SERVICE_STATE_DISCONNECT:
1003                         select_service(info, entry);
1004                         return;
1005                 case CONNMAN_SERVICE_STATE_UNKNOWN:
1006                 case CONNMAN_SERVICE_STATE_FAILURE:
1007                         break;
1008                 }
1009
1010                 iter = g_sequence_iter_next(iter);
1011         }
1012 }
1013
1014 static struct service_entry *create_service_entry(struct connman_service *service,
1015                                         const char *name,
1016                                         enum connman_service_state state)
1017 {
1018         struct service_entry *entry;
1019         enum connman_service_type type;
1020         int idx;
1021
1022         entry = g_try_new0(struct service_entry, 1);
1023         if (entry == NULL)
1024                 return entry;
1025
1026         entry->reason = CONNMAN_SESSION_REASON_UNKNOWN;
1027         entry->state = state;
1028         if (name != NULL)
1029                 entry->name = name;
1030         else
1031                 entry->name = "";
1032         entry->service = service;
1033
1034         idx = __connman_service_get_index(entry->service);
1035         entry->ifname = connman_inet_ifname(idx);
1036         if (entry->ifname == NULL)
1037                 entry->ifname = g_strdup("");
1038
1039         type = connman_service_get_type(entry->service);
1040         entry->bearer = service2bearer(type);
1041
1042         return entry;
1043 }
1044
1045 static void destroy_service_entry(gpointer data)
1046 {
1047         struct service_entry *entry = data;
1048
1049         pending_timeout_remove_all(entry);
1050         g_free(entry->ifname);
1051
1052         g_free(entry);
1053 }
1054
1055 static void populate_service_list(struct connman_session *session)
1056 {
1057         struct service_entry *entry;
1058         GSequenceIter *iter;
1059
1060         session->service_hash =
1061                 g_hash_table_new_full(g_direct_hash, g_direct_equal,
1062                                         NULL, NULL);
1063         session->service_list = __connman_service_get_list(session,
1064                                                         service_match,
1065                                                         create_service_entry,
1066                                                         destroy_service_entry);
1067
1068         g_sequence_sort(session->service_list, sort_services, session);
1069
1070         iter = g_sequence_get_begin_iter(session->service_list);
1071
1072         while (g_sequence_iter_is_end(iter) == FALSE) {
1073                 entry = g_sequence_get(iter);
1074
1075                 DBG("service %p type %s name %s", entry->service,
1076                         service2bearer(connman_service_get_type(entry->service)),
1077                         entry->name);
1078
1079                 g_hash_table_replace(session->service_hash,
1080                                         entry->service, iter);
1081
1082                 iter = g_sequence_iter_next(iter);
1083         }
1084 }
1085
1086 static void session_changed(struct connman_session *session,
1087                                 enum connman_session_trigger trigger)
1088 {
1089         struct session_info *info = session->info;
1090         struct session_info *info_last = session->info_last;
1091         GSequenceIter *service_iter = NULL, *service_iter_last = NULL;
1092         GSequence *service_list_last;
1093         GHashTable *service_hash_last;
1094
1095         /*
1096          * TODO: This only a placeholder for the 'real' algorithm to
1097          * play a bit around. So we are going to improve it step by step.
1098          */
1099
1100         DBG("session %p trigger %s reason %s", session, trigger2string(trigger),
1101                                                 reason2string(info->reason));
1102
1103         if (info->entry != NULL) {
1104                 enum connman_session_state state;
1105
1106                 state = service_to_session_state(info->entry->state);
1107
1108                 if (is_type_matching_state(&state, info->type) == TRUE)
1109                         info->state = state;
1110         }
1111
1112         switch (trigger) {
1113         case CONNMAN_SESSION_TRIGGER_UNKNOWN:
1114                 DBG("ignore session changed event");
1115                 return;
1116         case CONNMAN_SESSION_TRIGGER_SETTING:
1117                 if (info->allowed_bearers != info_last->allowed_bearers) {
1118
1119                         service_hash_last = session->service_hash;
1120                         service_list_last = session->service_list;
1121
1122                         populate_service_list(session);
1123
1124                         if (info->entry != NULL) {
1125                                 service_iter_last = g_hash_table_lookup(
1126                                                         service_hash_last,
1127                                                         info->entry->service);
1128                                 service_iter = g_hash_table_lookup(
1129                                                         session->service_hash,
1130                                                         info->entry->service);
1131                         }
1132
1133                         if (service_iter == NULL && service_iter_last != NULL) {
1134                                 /*
1135                                  * The currently selected service is
1136                                  * not part of this session anymore.
1137                                  */
1138                                 deselect_and_disconnect(session);
1139                         }
1140
1141                         g_hash_table_remove_all(service_hash_last);
1142                         g_sequence_free(service_list_last);
1143                 }
1144
1145                 if (info->type != info_last->type) {
1146                         if (info->state >= CONNMAN_SESSION_STATE_CONNECTED &&
1147                                         is_type_matching_state(&info->state,
1148                                                         info->type) == FALSE)
1149                                 deselect_and_disconnect(session);
1150                 }
1151
1152                 if (info->state == CONNMAN_SESSION_STATE_DISCONNECTED) {
1153                         select_and_connect(session,
1154                                         CONNMAN_SESSION_REASON_FREE_RIDE);
1155                 }
1156
1157                 break;
1158         case CONNMAN_SESSION_TRIGGER_ECALL:
1159                 /*
1160                  * For the time beeing we fallback to normal connect
1161                  * strategy.
1162                  */
1163         case CONNMAN_SESSION_TRIGGER_CONNECT:
1164                 if (info->state >= CONNMAN_SESSION_STATE_CONNECTED) {
1165                         if (info->entry->reason == CONNMAN_SESSION_REASON_CONNECT)
1166                                 break;
1167                         info->entry->reason = CONNMAN_SESSION_REASON_CONNECT;
1168                         __connman_service_session_inc(info->entry->service);
1169                         break;
1170                 }
1171
1172                 if (info->entry != NULL &&
1173                                 is_connecting(info->entry->state) == TRUE) {
1174                         break;
1175                 }
1176
1177                 select_and_connect(session,
1178                                 CONNMAN_SESSION_REASON_CONNECT);
1179
1180                 break;
1181         case CONNMAN_SESSION_TRIGGER_DISCONNECT:
1182                 deselect_and_disconnect(session);
1183
1184                 break;
1185         case CONNMAN_SESSION_TRIGGER_SERVICE:
1186                 if (info->entry != NULL &&
1187                         (is_connecting(info->entry->state) == TRUE ||
1188                                 is_connected(info->entry->state) == TRUE)) {
1189                         break;
1190                 }
1191
1192                 deselect_and_disconnect(session);
1193
1194                 if (info->reason == CONNMAN_SESSION_REASON_FREE_RIDE) {
1195                         select_and_connect(session, info->reason);
1196                 }
1197
1198                 break;
1199         }
1200
1201         session_notify(session);
1202 }
1203
1204 static DBusMessage *connect_session(DBusConnection *conn,
1205                                         DBusMessage *msg, void *user_data)
1206 {
1207         struct connman_session *session = user_data;
1208
1209         DBG("session %p", session);
1210
1211         if (ecall_session != NULL) {
1212                 if (ecall_session->ecall == TRUE && ecall_session != session)
1213                         return __connman_error_failed(msg, EBUSY);
1214
1215                 session->ecall = TRUE;
1216                 session_changed(session, CONNMAN_SESSION_TRIGGER_ECALL);
1217         } else
1218                 session_changed(session, CONNMAN_SESSION_TRIGGER_CONNECT);
1219
1220         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1221 }
1222
1223 static DBusMessage *disconnect_session(DBusConnection *conn,
1224                                         DBusMessage *msg, void *user_data)
1225 {
1226         struct connman_session *session = user_data;
1227
1228         DBG("session %p", session);
1229
1230         if (ecall_session != NULL) {
1231                 if (ecall_session->ecall == TRUE && ecall_session != session)
1232                         return __connman_error_failed(msg, EBUSY);
1233
1234                 session->ecall = FALSE;
1235         }
1236
1237         session_changed(session, CONNMAN_SESSION_TRIGGER_DISCONNECT);
1238
1239         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1240 }
1241
1242 static DBusMessage *change_session(DBusConnection *conn,
1243                                         DBusMessage *msg, void *user_data)
1244 {
1245         struct connman_session *session = user_data;
1246         struct session_info *info = session->info;
1247         DBusMessageIter iter, value;
1248         const char *name;
1249         const char *val;
1250         GSList *allowed_bearers;
1251
1252         DBG("session %p", session);
1253         if (dbus_message_iter_init(msg, &iter) == FALSE)
1254                 return __connman_error_invalid_arguments(msg);
1255
1256         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
1257                 return __connman_error_invalid_arguments(msg);
1258
1259         dbus_message_iter_get_basic(&iter, &name);
1260         dbus_message_iter_next(&iter);
1261
1262         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
1263                 return __connman_error_invalid_arguments(msg);
1264
1265         dbus_message_iter_recurse(&iter, &value);
1266
1267         switch (dbus_message_iter_get_arg_type(&value)) {
1268         case DBUS_TYPE_ARRAY:
1269                 if (g_str_equal(name, "AllowedBearers") == TRUE) {
1270                         allowed_bearers = session_parse_allowed_bearers(&value);
1271
1272                         g_slist_foreach(info->allowed_bearers,
1273                                         cleanup_bearer_info, NULL);
1274                         g_slist_free(info->allowed_bearers);
1275
1276                         if (allowed_bearers == NULL) {
1277                                 allowed_bearers = session_allowed_bearers_any();
1278
1279                                 if (allowed_bearers == NULL)
1280                                         return __connman_error_failed(msg, ENOMEM);
1281                         }
1282
1283                         info->allowed_bearers = allowed_bearers;
1284                 } else {
1285                         goto err;
1286                 }
1287                 break;
1288         case DBUS_TYPE_STRING:
1289                 if (g_str_equal(name, "ConnectionType") == TRUE) {
1290                         dbus_message_iter_get_basic(&value, &val);
1291                         info->type = string2type(val);
1292                 } else {
1293                         goto err;
1294                 }
1295                 break;
1296         default:
1297                 goto err;
1298         }
1299
1300         session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING);
1301
1302         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1303
1304 err:
1305         return __connman_error_invalid_arguments(msg);
1306 }
1307
1308 static void release_session(gpointer key, gpointer value, gpointer user_data)
1309 {
1310         struct connman_session *session = value;
1311         DBusMessage *message;
1312
1313         DBG("owner %s path %s", session->owner, session->notify_path);
1314
1315         if (session->notify_watch > 0)
1316                 g_dbus_remove_watch(connection, session->notify_watch);
1317
1318         g_dbus_unregister_interface(connection, session->session_path,
1319                                                 CONNMAN_SESSION_INTERFACE);
1320
1321         message = dbus_message_new_method_call(session->owner,
1322                                                 session->notify_path,
1323                                                 CONNMAN_NOTIFICATION_INTERFACE,
1324                                                 "Release");
1325         if (message == NULL)
1326                 return;
1327
1328         dbus_message_set_no_reply(message, TRUE);
1329
1330         g_dbus_send_message(connection, message);
1331 }
1332
1333 static int session_disconnect(struct connman_session *session)
1334 {
1335         DBG("session %p, %s", session, session->owner);
1336
1337         if (session->notify_watch > 0)
1338                 g_dbus_remove_watch(connection, session->notify_watch);
1339
1340         g_dbus_unregister_interface(connection, session->session_path,
1341                                                 CONNMAN_SESSION_INTERFACE);
1342
1343         deselect_and_disconnect(session);
1344
1345         g_hash_table_remove(session_hash, session->session_path);
1346
1347         return 0;
1348 }
1349
1350 static void owner_disconnect(DBusConnection *conn, void *user_data)
1351 {
1352         struct connman_session *session = user_data;
1353
1354         DBG("session %p, %s died", session, session->owner);
1355
1356         session_disconnect(session);
1357 }
1358
1359 static DBusMessage *destroy_session(DBusConnection *conn,
1360                                         DBusMessage *msg, void *user_data)
1361 {
1362         struct connman_session *session = user_data;
1363
1364         DBG("session %p", session);
1365
1366         if (ecall_session != NULL && ecall_session != session)
1367                 return __connman_error_failed(msg, EBUSY);
1368
1369         session_disconnect(session);
1370
1371         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1372 }
1373
1374 static const GDBusMethodTable session_methods[] = {
1375         { GDBUS_METHOD("Destroy", NULL, NULL, destroy_session) },
1376         { GDBUS_METHOD("Connect", NULL, NULL, connect_session) },
1377         { GDBUS_METHOD("Disconnect", NULL, NULL,
1378                         disconnect_session ) },
1379         { GDBUS_METHOD("Change",
1380                         GDBUS_ARGS({ "name", "s" }, { "value", "v" }),
1381                         NULL, change_session) },
1382         { },
1383 };
1384
1385 int __connman_session_create(DBusMessage *msg)
1386 {
1387         const char *owner, *notify_path;
1388         char *session_path = NULL;
1389         DBusMessageIter iter, array;
1390         struct connman_session *session = NULL;
1391         struct session_info *info, *info_last;
1392
1393         enum connman_session_type type = CONNMAN_SESSION_TYPE_ANY;
1394         connman_bool_t priority;
1395         connman_bool_t ecall_app;
1396         enum connman_session_roaming_policy roaming_policy;
1397         char *roaming_policy_str;
1398         GSList *allowed_bearers = NULL;
1399         int err;
1400
1401         owner = dbus_message_get_sender(msg);
1402
1403         DBG("owner %s", owner);
1404
1405         if (ecall_session != NULL && ecall_session->ecall == TRUE) {
1406                 /*
1407                  * If there is an emergency call already going on,
1408                  * ignore session creation attempt
1409                  */
1410                 err = -EBUSY;
1411                 goto err;
1412         }
1413
1414         dbus_message_iter_init(msg, &iter);
1415         dbus_message_iter_recurse(&iter, &array);
1416
1417         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
1418                 DBusMessageIter entry, value;
1419                 const char *key, *val;
1420
1421                 dbus_message_iter_recurse(&array, &entry);
1422                 dbus_message_iter_get_basic(&entry, &key);
1423
1424                 dbus_message_iter_next(&entry);
1425                 dbus_message_iter_recurse(&entry, &value);
1426
1427                 switch (dbus_message_iter_get_arg_type(&value)) {
1428                 case DBUS_TYPE_ARRAY:
1429                         if (g_str_equal(key, "AllowedBearers") == TRUE) {
1430                                 allowed_bearers =
1431                                         session_parse_allowed_bearers(&value);
1432                         } else {
1433                                 return -EINVAL;
1434                         }
1435                         break;
1436                 case DBUS_TYPE_STRING:
1437                         if (g_str_equal(key, "ConnectionType") == TRUE) {
1438                                 dbus_message_iter_get_basic(&value, &val);
1439                                 type = string2type(val);
1440                         } else {
1441                                 return -EINVAL;
1442                         }
1443                 }
1444                 dbus_message_iter_next(&array);
1445         }
1446
1447         dbus_message_iter_next(&iter);
1448         dbus_message_iter_get_basic(&iter, &notify_path);
1449
1450         if (notify_path == NULL) {
1451                 err = -EINVAL;
1452                 goto err;
1453         }
1454
1455         session_path = g_strdup_printf("/sessions%s", notify_path);
1456         if (session_path == NULL) {
1457                 err = -ENOMEM;
1458                 goto err;
1459         }
1460
1461         session = g_hash_table_lookup(session_hash, session_path);
1462         if (session != NULL) {
1463                 session = NULL;
1464                 err = -EEXIST;
1465                 goto err;
1466         }
1467
1468         session = g_try_new0(struct connman_session, 1);
1469         if (session == NULL) {
1470                 err = -ENOMEM;
1471                 goto err;
1472         }
1473
1474         session->info = g_try_new0(struct session_info, 1);
1475         if (session->info == NULL) {
1476                 err = -ENOMEM;
1477                 goto err;
1478         }
1479
1480         session->info_last = g_try_new0(struct session_info, 1);
1481         if (session->info_last == NULL) {
1482                 err = -ENOMEM;
1483                 goto err;
1484         }
1485
1486         info = session->info;
1487         info_last = session->info_last;
1488
1489         config_get_bool(owner, "Priority", &priority);
1490         config_get_bool(owner, "EmergencyCall", &ecall_app);
1491         config_get_string(owner, "RoamingPolicy", &roaming_policy_str);
1492         roaming_policy = string2roamingpolicy(roaming_policy_str);
1493
1494         session->owner = g_strdup(owner);
1495         session->session_path = session_path;
1496         session->notify_path = g_strdup(notify_path);
1497         session->notify_watch =
1498                 g_dbus_add_disconnect_watch(connection, session->owner,
1499                                         owner_disconnect, session, NULL);
1500         if (ecall_app == TRUE)
1501                 ecall_session = session;
1502
1503         info->state = CONNMAN_SESSION_STATE_DISCONNECTED;
1504         info->type = type;
1505         info->priority = priority;
1506         info->roaming_policy = roaming_policy;
1507         info->entry = NULL;
1508
1509         if (allowed_bearers == NULL) {
1510                 info->allowed_bearers =
1511                                 session_allowed_bearers_any();
1512
1513                 if (info->allowed_bearers == NULL) {
1514                         err = -ENOMEM;
1515                         goto err;
1516                 }
1517         } else {
1518                 info->allowed_bearers = allowed_bearers;
1519         }
1520
1521         g_hash_table_replace(session_hash, session->session_path, session);
1522
1523         DBG("add %s", session->session_path);
1524
1525         if (g_dbus_register_interface(connection, session->session_path,
1526                                         CONNMAN_SESSION_INTERFACE,
1527                                         session_methods, NULL,
1528                                         NULL, session, NULL) == FALSE) {
1529                 connman_error("Failed to register %s", session->session_path);
1530                 g_hash_table_remove(session_hash, session->session_path);
1531                 session = NULL;
1532
1533                 err = -EINVAL;
1534                 goto err;
1535         }
1536
1537         g_dbus_send_reply(connection, msg,
1538                                 DBUS_TYPE_OBJECT_PATH, &session->session_path,
1539                                 DBUS_TYPE_INVALID);
1540
1541
1542         populate_service_list(session);
1543
1544         info_last->state = info->state;
1545         info_last->priority = info->priority;
1546         info_last->roaming_policy = info->roaming_policy;
1547         info_last->entry = info->entry;
1548         info_last->allowed_bearers = info->allowed_bearers;
1549
1550         session->append_all = TRUE;
1551
1552         session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING);
1553
1554         return 0;
1555
1556 err:
1557         connman_error("Failed to create session");
1558
1559         if (session != NULL) {
1560                 if (session->info_last != NULL)
1561                         g_free(session->info_last);
1562                 if (session->info != NULL)
1563                         g_free(session->info);
1564                 g_free(session);
1565         }
1566
1567         g_free(session_path);
1568
1569         g_slist_foreach(allowed_bearers, cleanup_bearer_info, NULL);
1570         g_slist_free(allowed_bearers);
1571
1572         return err;
1573 }
1574
1575 int __connman_session_destroy(DBusMessage *msg)
1576 {
1577         const char *owner, *session_path;
1578         struct connman_session *session;
1579
1580         owner = dbus_message_get_sender(msg);
1581
1582         DBG("owner %s", owner);
1583
1584         dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &session_path,
1585                                                         DBUS_TYPE_INVALID);
1586         if (session_path == NULL)
1587                 return -EINVAL;
1588
1589         session = g_hash_table_lookup(session_hash, session_path);
1590         if (session == NULL)
1591                 return -EINVAL;
1592
1593         if (g_strcmp0(owner, session->owner) != 0)
1594                 return -EACCES;
1595
1596         session_disconnect(session);
1597
1598         return 0;
1599 }
1600
1601 connman_bool_t __connman_session_mode()
1602 {
1603         return sessionmode;
1604 }
1605
1606 void __connman_session_set_mode(connman_bool_t enable)
1607 {
1608         DBG("enable %d", enable);
1609
1610         if (sessionmode != enable) {
1611                 sessionmode = enable;
1612
1613                 connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
1614                                 CONNMAN_MANAGER_INTERFACE, "SessionMode",
1615                                 DBUS_TYPE_BOOLEAN, &sessionmode);
1616         }
1617
1618         if (sessionmode == TRUE)
1619                 __connman_service_disconnect_all();
1620 }
1621
1622 static void service_add(struct connman_service *service,
1623                         const char *name)
1624 {
1625         GHashTableIter iter;
1626         GSequenceIter *iter_service_list;
1627         gpointer key, value;
1628         struct connman_session *session;
1629         struct service_entry *entry;
1630
1631         DBG("service %p", service);
1632
1633         g_hash_table_iter_init(&iter, session_hash);
1634
1635         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1636                 session = value;
1637
1638                 if (service_match(session, service) == FALSE)
1639                         continue;
1640
1641                 entry = create_service_entry(service, name,
1642                                                 CONNMAN_SERVICE_STATE_IDLE);
1643                 if (entry == NULL)
1644                         continue;
1645
1646                 iter_service_list =
1647                         g_sequence_insert_sorted(session->service_list,
1648                                                         entry, sort_services,
1649                                                         session);
1650
1651                 g_hash_table_replace(session->service_hash, service,
1652                                         iter_service_list);
1653
1654                 session_changed(session, CONNMAN_SESSION_TRIGGER_SERVICE);
1655         }
1656 }
1657
1658 static void service_remove(struct connman_service *service)
1659 {
1660
1661         GHashTableIter iter;
1662         gpointer key, value;
1663         struct connman_session *session;
1664         struct session_info *info;
1665
1666         DBG("service %p", service);
1667
1668         g_hash_table_iter_init(&iter, session_hash);
1669
1670         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1671                 GSequenceIter *seq_iter;
1672                 session = value;
1673                 info = session->info;
1674
1675                 seq_iter = g_hash_table_lookup(session->service_hash, service);
1676                 if (seq_iter == NULL)
1677                         continue;
1678
1679                 g_sequence_remove(seq_iter);
1680
1681                 if (info->entry != NULL && info->entry->service == service)
1682                         info->entry = NULL;
1683                 session_changed(session, CONNMAN_SESSION_TRIGGER_SERVICE);
1684         }
1685 }
1686
1687 static void service_state_changed(struct connman_service *service,
1688                                         enum connman_service_state state)
1689 {
1690         GHashTableIter iter;
1691         gpointer key, value;
1692
1693         DBG("service %p state %d", service, state);
1694
1695         g_hash_table_iter_init(&iter, session_hash);
1696
1697         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1698                 struct connman_session *session = value;
1699                 GSequenceIter *service_iter;
1700
1701                 service_iter = g_hash_table_lookup(session->service_hash, service);
1702                 if (service_iter != NULL) {
1703                         struct service_entry *entry;
1704
1705                         entry = g_sequence_get(service_iter);
1706                         entry->state = state;
1707                 }
1708
1709                 session_changed(session,
1710                                 CONNMAN_SESSION_TRIGGER_SERVICE);
1711         }
1712 }
1713
1714 static void ipconfig_changed(struct connman_service *service,
1715                                 struct connman_ipconfig *ipconfig)
1716 {
1717         GHashTableIter iter;
1718         gpointer key, value;
1719         struct connman_session *session;
1720         struct session_info *info;
1721         enum connman_ipconfig_type type;
1722
1723         DBG("service %p ipconfig %p", service, ipconfig);
1724
1725         type = __connman_ipconfig_get_config_type(ipconfig);
1726
1727         g_hash_table_iter_init(&iter, session_hash);
1728
1729         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1730                 session = value;
1731                 info = session->info;
1732
1733                 if (info->state == CONNMAN_SESSION_STATE_DISCONNECTED)
1734                         continue;
1735
1736                 if (info->entry != NULL && info->entry->service == service) {
1737                         if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
1738                                 ipconfig_ipv4_changed(session);
1739                         else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
1740                                 ipconfig_ipv6_changed(session);
1741                 }
1742         }
1743 }
1744
1745 static struct connman_notifier session_notifier = {
1746         .name                   = "session",
1747         .service_add            = service_add,
1748         .service_remove         = service_remove,
1749         .service_state_changed  = service_state_changed,
1750         .ipconfig_changed       = ipconfig_changed,
1751 };
1752
1753 static struct connman_session *session_lookup_by_id(const char *id)
1754 {
1755         struct connman_session *session;
1756         GHashTableIter iter;
1757         gpointer key, value;
1758
1759         DBG("id %s", id);
1760
1761         g_hash_table_iter_init(&iter, session_hash);
1762
1763         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1764                 session = value;
1765
1766                 if (g_strcmp0(session->owner, id) == FALSE)
1767                         continue;
1768
1769                 return session;
1770         }
1771
1772         DBG("No session found by id %s", id);
1773
1774         return NULL;
1775 }
1776
1777 int connman_session_update_bool(const char *id, const char *key,
1778                                         connman_bool_t val)
1779 {
1780         struct connman_session *session;
1781         struct session_info *info;
1782
1783         session = session_lookup_by_id(id);
1784         if (session == NULL)
1785                 return -EINVAL;
1786
1787         info = session->info;
1788         if (info == NULL)
1789                 return 0;
1790
1791         DBG("%s %d", key, val);
1792
1793         return -EINVAL;
1794 }
1795
1796 int connman_session_update_string(const char *id, const char *key,
1797                                         const char *val)
1798 {
1799         struct connman_session *session;
1800         struct session_info *info;
1801
1802         session = session_lookup_by_id(id);
1803         if (session == NULL)
1804                 return -EINVAL;
1805
1806         info = session->info;
1807         if (info == NULL)
1808                 return 0;
1809
1810         DBG("%s %s", key, val);
1811
1812         return -EINVAL;
1813 }
1814
1815 int connman_session_config_register(struct connman_session_config *config)
1816 {
1817         DBG("name %s", config->name);
1818
1819         if (session_config != NULL) {
1820                 connman_warn("A session configuration plugin '%s' is "
1821                                 "already registerd. Skipping registration "
1822                                 "of plugin '%s'",
1823                                 session_config->name, config->name);
1824                 return -EALREADY;
1825         }
1826
1827         session_config = config;
1828
1829         return 0;
1830 }
1831
1832 void connman_session_config_unregister(struct connman_session_config *config)
1833 {
1834         DBG("name %s", config->name);
1835
1836         if (config != session_config) {
1837                 connman_warn("Trying to unregister session configuration "
1838                                 "plugin '%s'", config->name);
1839                 return;
1840         }
1841
1842         session_config = NULL;
1843 }
1844
1845 int __connman_session_init(void)
1846 {
1847         int err;
1848
1849         DBG("");
1850
1851         connection = connman_dbus_get_connection();
1852         if (connection == NULL)
1853                 return -1;
1854
1855         err = connman_notifier_register(&session_notifier);
1856         if (err < 0) {
1857                 dbus_connection_unref(connection);
1858                 return err;
1859         }
1860
1861         session_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1862                                                 NULL, cleanup_session);
1863
1864         sessionmode = FALSE;
1865         return 0;
1866 }
1867
1868 void __connman_session_cleanup(void)
1869 {
1870         DBG("");
1871
1872         if (connection == NULL)
1873                 return;
1874
1875         connman_notifier_unregister(&session_notifier);
1876
1877         g_hash_table_foreach(session_hash, release_session, NULL);
1878         g_hash_table_destroy(session_hash);
1879         session_hash = NULL;
1880
1881         dbus_connection_unref(connection);
1882 }