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