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