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