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