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