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