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