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