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