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