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