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