service: Remove the gateways when removing the service
[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, "cellular") == 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 "cellular";
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         return FALSE;
508 }
509
510 static void ipconfig_ipv4_changed(struct connman_session *session)
511 {
512         struct session_info *info = session->info;
513
514         connman_dbus_setting_changed_dict(session->owner, session->notify_path,
515                                                 "IPv4", append_ipconfig_ipv4,
516                                                 info->entry->service);
517 }
518
519 static void ipconfig_ipv6_changed(struct connman_session *session)
520 {
521         struct session_info *info = session->info;
522
523         connman_dbus_setting_changed_dict(session->owner, session->notify_path,
524                                                 "IPv6", append_ipconfig_ipv6,
525                                                 info->entry->service);
526 }
527
528 static connman_bool_t service_type_match(struct connman_session *session,
529                                         struct connman_service *service)
530 {
531         struct session_info *info = session->info;
532         GSList *list;
533
534         for (list = info->allowed_bearers;
535                         list != NULL; list = list->next) {
536                 struct bearer_info *info = list->data;
537                 enum connman_service_type service_type;
538
539                 if (info->match_all == TRUE)
540                         return TRUE;
541
542                 service_type = connman_service_get_type(service);
543                 if (info->service_type == service_type)
544                         return TRUE;
545         }
546
547         return FALSE;
548 }
549
550 static connman_bool_t service_match(struct connman_session *session,
551                                         struct connman_service *service)
552 {
553         if (service_type_match(session, service) == FALSE)
554                 return FALSE;
555
556         return TRUE;
557 }
558
559 static int service_type_weight(enum connman_service_type type)
560 {
561         /*
562          * The session doesn't care which service
563          * to use. Nevertheless we have to sort them
564          * according their type. The ordering is
565          *
566          * 1. Ethernet
567          * 2. Bluetooth
568          * 3. WiFi/WiMAX
569          * 4. Cellular
570          */
571
572         switch (type) {
573         case CONNMAN_SERVICE_TYPE_ETHERNET:
574                 return 4;
575         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
576                 return 3;
577         case CONNMAN_SERVICE_TYPE_WIFI:
578         case CONNMAN_SERVICE_TYPE_WIMAX:
579                 return 2;
580         case CONNMAN_SERVICE_TYPE_CELLULAR:
581                 return 1;
582         case CONNMAN_SERVICE_TYPE_UNKNOWN:
583         case CONNMAN_SERVICE_TYPE_SYSTEM:
584         case CONNMAN_SERVICE_TYPE_GPS:
585         case CONNMAN_SERVICE_TYPE_VPN:
586         case CONNMAN_SERVICE_TYPE_GADGET:
587                 break;
588         }
589
590         return 0;
591 }
592
593 static gint sort_allowed_bearers(struct connman_service *service_a,
594                                         struct connman_service *service_b,
595                                         struct connman_session *session)
596 {
597         struct session_info *info = session->info;
598         GSList *list;
599         enum connman_service_type type_a, type_b;
600         int weight_a, weight_b;
601
602         type_a = connman_service_get_type(service_a);
603         type_b = connman_service_get_type(service_b);
604
605         for (list = info->allowed_bearers;
606                         list != NULL; list = list->next) {
607                 struct bearer_info *info = list->data;
608
609                 if (info->match_all == TRUE) {
610                         if (type_a != type_b) {
611                                 weight_a = service_type_weight(type_a);
612                                 weight_b = service_type_weight(type_b);
613
614                                 if (weight_a > weight_b)
615                                         return -1;
616
617                                 if (weight_a < weight_b)
618                                         return 1;
619
620                                 return 0;
621                         }
622                 }
623
624                 if (type_a == info->service_type &&
625                                 type_b == info->service_type) {
626                         return 0;
627                 }
628
629                 if (type_a == info->service_type &&
630                                 type_b != info->service_type) {
631                         return -1;
632                 }
633
634                 if (type_a != info->service_type &&
635                                 type_b == info->service_type) {
636                         return 1;
637                 }
638         }
639
640         return 0;
641 }
642
643 static gint sort_services(gconstpointer a, gconstpointer b, gpointer user_data)
644 {
645         struct service_entry *entry_a = (void *)a;
646         struct service_entry *entry_b = (void *)b;
647         struct connman_session *session = user_data;
648
649         return sort_allowed_bearers(entry_a->service, entry_b->service,
650                                 session);
651 }
652
653 static void cleanup_session(gpointer user_data)
654 {
655         struct connman_session *session = user_data;
656         struct session_info *info = session->info;
657
658         DBG("remove %s", session->session_path);
659
660         g_hash_table_destroy(session->service_hash);
661         g_sequence_free(session->service_list);
662
663         if (info->entry != NULL &&
664                         info->entry->reason == CONNMAN_SESSION_REASON_CONNECT) {
665                 __connman_service_disconnect(info->entry->service);
666         }
667
668         g_slist_foreach(info->allowed_bearers, cleanup_bearer_info, NULL);
669         g_slist_free(info->allowed_bearers);
670
671         g_free(session->owner);
672         g_free(session->session_path);
673         g_free(session->notify_path);
674         g_free(session->info);
675         g_free(session->info_last);
676
677         g_free(session);
678 }
679
680 static connman_bool_t is_online(enum connman_service_state state)
681 {
682         switch (state) {
683         case CONNMAN_SERVICE_STATE_UNKNOWN:
684         case CONNMAN_SERVICE_STATE_IDLE:
685         case CONNMAN_SERVICE_STATE_ASSOCIATION:
686         case CONNMAN_SERVICE_STATE_CONFIGURATION:
687         case CONNMAN_SERVICE_STATE_DISCONNECT:
688         case CONNMAN_SERVICE_STATE_FAILURE:
689         case CONNMAN_SERVICE_STATE_READY:
690                 break;
691         case CONNMAN_SERVICE_STATE_ONLINE:
692                 return TRUE;
693         }
694
695         return FALSE;
696 }
697
698 static connman_bool_t is_connecting(enum connman_service_state state)
699 {
700         switch (state) {
701         case CONNMAN_SERVICE_STATE_UNKNOWN:
702         case CONNMAN_SERVICE_STATE_IDLE:
703                 break;
704         case CONNMAN_SERVICE_STATE_ASSOCIATION:
705         case CONNMAN_SERVICE_STATE_CONFIGURATION:
706         case CONNMAN_SERVICE_STATE_READY:
707                 return TRUE;
708         case CONNMAN_SERVICE_STATE_DISCONNECT:
709         case CONNMAN_SERVICE_STATE_FAILURE:
710         case CONNMAN_SERVICE_STATE_ONLINE:
711                 break;
712         }
713
714         return FALSE;
715 }
716
717 static connman_bool_t explicit_connect(enum connman_session_reason reason)
718 {
719         switch (reason) {
720         case CONNMAN_SESSION_REASON_UNKNOWN:
721         case CONNMAN_SESSION_REASON_FREE_RIDE:
722         case CONNMAN_SESSION_REASON_DISCONNECT:
723                 break;
724         case CONNMAN_SESSION_REASON_CONNECT:
725         case CONNMAN_SESSION_REASON_PERIODIC:
726                 return TRUE;
727         }
728
729         return FALSE;
730 }
731
732 static connman_bool_t explicit_disconnect(struct session_info *info)
733 {
734         if (info->entry == NULL)
735                 return FALSE;
736
737         DBG("reason %s service %p state %d",
738                 reason2string(info->entry->reason),
739                 info->entry->service, info->entry->state);
740
741         if (info->entry->reason == CONNMAN_SESSION_REASON_UNKNOWN)
742                 return FALSE;
743
744         if (explicit_connect(info->entry->reason) == FALSE)
745                 return FALSE;
746
747         if (__connman_service_session_dec(info->entry->service) == FALSE)
748                 return FALSE;
749
750         if (ecall_info != NULL && ecall_info != info)
751                 return FALSE;
752
753         return TRUE;
754 }
755
756 static gboolean call_disconnect(gpointer user_data)
757 {
758         struct connman_service *service = user_data;
759
760         /*
761          * TODO: We should mark this entry as pending work. In case
762          * disconnect fails we just unassign this session from the
763          * service and can't do anything later on it
764          */
765         DBG("disconnect service %p", service);
766         __connman_service_disconnect(service);
767
768         return FALSE;
769 }
770
771 static gboolean call_connect(gpointer user_data)
772 {
773         struct connman_service *service = user_data;
774
775         DBG("connect service %p", service);
776         __connman_service_connect(service);
777
778         return FALSE;
779 }
780
781 static connman_bool_t deselect_service(struct session_info *info)
782 {
783         struct connman_service *service;
784         connman_bool_t disconnect, online;
785
786         DBG("");
787
788         if (info->entry == NULL)
789                 return FALSE;
790
791         disconnect = explicit_disconnect(info);
792
793         online = is_connecting(info->entry->state) == TRUE ||
794                         is_online(info->entry->state) == TRUE;
795
796         info->online = FALSE;
797         info->entry->reason = CONNMAN_SESSION_REASON_UNKNOWN;
798
799         service = info->entry->service;
800         info->entry = NULL;
801
802         DBG("disconnect %d online %d", disconnect, online);
803
804         if (disconnect == TRUE && online == TRUE)
805                 g_timeout_add_seconds(0, call_disconnect, service);
806
807         return TRUE;
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         session->info_dirty |= deselect_service(info);
816
817         info->reason = reason;
818 }
819
820 static connman_bool_t 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 TRUE;
830
831         __connman_service_session_inc(info->entry->service);
832
833         return TRUE;
834 }
835
836 static connman_bool_t select_offline_service(struct session_info *info,
837                                         struct service_entry *entry)
838 {
839         if (explicit_connect(info->reason) == FALSE)
840                 return FALSE;
841
842         info->online = FALSE;
843
844         info->entry = entry;
845         info->entry->reason = info->reason;
846
847         __connman_service_session_inc(info->entry->service);
848         g_timeout_add_seconds(0, call_connect, info->entry->service);
849
850         return TRUE;
851 }
852
853 static connman_bool_t select_service(struct session_info *info,
854                                 struct service_entry *entry)
855 {
856         DBG("service %p", entry->service);
857
858         if (is_online(entry->state) == TRUE)
859                 return select_online_service(info, entry);
860         else
861                 return select_offline_service(info, entry);
862 }
863
864 static void select_and_connect(struct connman_session *session,
865                                 enum connman_session_reason reason)
866 {
867         struct session_info *info = session->info;
868         struct service_entry *entry = NULL;
869         GSequenceIter *iter;
870
871         DBG("session %p reason %s", session, reason2string(reason));
872
873         info->reason = reason;
874
875         iter = g_sequence_get_begin_iter(session->service_list);
876
877         while (g_sequence_iter_is_end(iter) == FALSE) {
878                 entry = g_sequence_get(iter);
879
880                 switch (entry->state) {
881                 case CONNMAN_SERVICE_STATE_ASSOCIATION:
882                 case CONNMAN_SERVICE_STATE_CONFIGURATION:
883                 case CONNMAN_SERVICE_STATE_READY:
884                 case CONNMAN_SERVICE_STATE_ONLINE:
885                 case CONNMAN_SERVICE_STATE_IDLE:
886                 case CONNMAN_SERVICE_STATE_DISCONNECT:
887                         session->info_dirty |=
888                                 select_service(info, entry);
889                         return;
890                 case CONNMAN_SERVICE_STATE_UNKNOWN:
891                 case CONNMAN_SERVICE_STATE_FAILURE:
892                         break;
893                 }
894
895                 iter = g_sequence_iter_next(iter);
896         }
897 }
898
899 static struct service_entry *create_service_entry(struct connman_service *service,
900                                         const char *name,
901                                         enum connman_service_state state)
902 {
903         struct service_entry *entry;
904         enum connman_service_type type;
905         int idx;
906
907         entry = g_try_new0(struct service_entry, 1);
908         if (entry == NULL)
909                 return entry;
910
911         entry->reason = CONNMAN_SESSION_REASON_UNKNOWN;
912         entry->state = state;
913         if (name != NULL)
914                 entry->name = name;
915         else
916                 entry->name = "";
917         entry->service = service;
918
919         idx = __connman_service_get_index(entry->service);
920         entry->ifname = connman_inet_ifname(idx);
921         if (entry->ifname == NULL)
922                 entry->ifname = g_strdup("");
923
924         type = connman_service_get_type(entry->service);
925         entry->bearer = service2bearer(type);
926
927         return entry;
928 }
929
930 static void destroy_service_entry(gpointer data)
931 {
932         struct service_entry *entry = data;
933
934         g_free(entry->ifname);
935
936         g_free(entry);
937 }
938
939 static void populate_service_list(struct connman_session *session)
940 {
941         struct service_entry *entry;
942         GSequenceIter *iter;
943
944         session->service_hash =
945                 g_hash_table_new_full(g_direct_hash, g_direct_equal,
946                                         NULL, NULL);
947         session->service_list = __connman_service_get_list(session,
948                                                         service_match,
949                                                         create_service_entry,
950                                                         destroy_service_entry);
951
952         g_sequence_sort(session->service_list, sort_services, session);
953
954         iter = g_sequence_get_begin_iter(session->service_list);
955
956         while (g_sequence_iter_is_end(iter) == FALSE) {
957                 entry = g_sequence_get(iter);
958
959                 DBG("service %p type %s name %s", entry->service,
960                         service2bearer(connman_service_get_type(entry->service)),
961                         entry->name);
962
963                 g_hash_table_replace(session->service_hash,
964                                         entry->service, iter);
965
966                 iter = g_sequence_iter_next(iter);
967         }
968 }
969
970 static void session_changed(struct connman_session *session,
971                                 enum connman_session_trigger trigger)
972 {
973         struct session_info *info = session->info;
974         struct session_info *info_last = session->info_last;
975         GSequenceIter *service_iter = NULL, *service_iter_last = NULL;
976         GSequence *service_list_last;
977         GHashTable *service_hash_last;
978
979         /*
980          * TODO: This only a placeholder for the 'real' algorithm to
981          * play a bit around. So we are going to improve it step by step.
982          */
983
984         DBG("session %p trigger %s reason %s", session, trigger2string(trigger),
985                                                 reason2string(info->reason));
986
987         if (info->entry != NULL) {
988                 info->online = is_online(info->entry->state);
989                 if (info_last->online != info->online)
990                         session->info_dirty = TRUE;
991         }
992
993         switch (trigger) {
994         case CONNMAN_SESSION_TRIGGER_UNKNOWN:
995                 DBG("ignore session changed event");
996                 return;
997         case CONNMAN_SESSION_TRIGGER_SETTING:
998                 if (info->allowed_bearers != info_last->allowed_bearers) {
999
1000                         service_hash_last = session->service_hash;
1001                         service_list_last = session->service_list;
1002
1003                         populate_service_list(session);
1004
1005                         if (info->entry != NULL) {
1006                                 service_iter_last = g_hash_table_lookup(
1007                                                         service_hash_last,
1008                                                         info->entry->service);
1009                                 service_iter = g_hash_table_lookup(
1010                                                         session->service_hash,
1011                                                         info->entry->service);
1012                         }
1013
1014                         if (service_iter == NULL && service_iter_last != NULL) {
1015                                 /*
1016                                  * The currently selected service is
1017                                  * not part of this session anymore.
1018                                  */
1019                                 deselect_and_disconnect(session, info->reason);
1020                         }
1021
1022                         g_hash_table_remove_all(service_hash_last);
1023                         g_sequence_free(service_list_last);
1024                 }
1025
1026                 if (info->online == FALSE) {
1027                         select_and_connect(session,
1028                                         CONNMAN_SESSION_REASON_FREE_RIDE);
1029                 }
1030
1031                 break;
1032         case CONNMAN_SESSION_TRIGGER_CONNECT:
1033                 if (info->online == TRUE) {
1034                         if (info->entry->reason == CONNMAN_SESSION_REASON_CONNECT)
1035                                 break;
1036                         info->entry->reason = CONNMAN_SESSION_REASON_CONNECT;
1037                         __connman_service_session_inc(info->entry->service);
1038                         break;
1039                 }
1040
1041                 if (info->entry != NULL &&
1042                                 is_connecting(info->entry->state) == TRUE) {
1043                         break;
1044                 }
1045
1046                 select_and_connect(session,
1047                                 CONNMAN_SESSION_REASON_CONNECT);
1048
1049                 break;
1050         case CONNMAN_SESSION_TRIGGER_DISCONNECT:
1051                 deselect_and_disconnect(session,
1052                                         CONNMAN_SESSION_REASON_DISCONNECT);
1053
1054                 break;
1055         case CONNMAN_SESSION_TRIGGER_PERIODIC:
1056                 if (info->online == TRUE) {
1057                         info->entry->reason = CONNMAN_SESSION_REASON_PERIODIC;
1058                         __connman_service_session_inc(info->entry->service);
1059                         break;
1060                 }
1061
1062                 select_and_connect(session,
1063                                 CONNMAN_SESSION_REASON_PERIODIC);
1064
1065                 break;
1066         case CONNMAN_SESSION_TRIGGER_SERVICE:
1067                 if (info->entry != NULL &&
1068                                 (is_connecting(info->entry->state) == TRUE ||
1069                                         is_online(info->entry->state) == TRUE)) {
1070                         break;
1071                 }
1072
1073                 deselect_and_disconnect(session, info->reason);
1074
1075                 if (info->reason == CONNMAN_SESSION_REASON_FREE_RIDE ||
1076                                 info->stay_connected == TRUE) {
1077                         select_and_connect(session, info->reason);
1078                 }
1079
1080                 break;
1081         case CONNMAN_SESSION_TRIGGER_ECALL:
1082                 if (info->online == FALSE && info->entry != NULL &&
1083                                 info->entry->service != NULL) {
1084                         deselect_and_disconnect(session, info->reason);
1085                 }
1086
1087                 break;
1088         }
1089
1090         session_notify(session);
1091 }
1092
1093 static DBusMessage *connect_session(DBusConnection *conn,
1094                                         DBusMessage *msg, void *user_data)
1095 {
1096         struct connman_session *session = user_data;
1097         struct session_info *info = session->info;
1098
1099         DBG("session %p", session);
1100
1101         if (ecall_info != NULL && ecall_info != info)
1102                 return __connman_error_failed(msg, EBUSY);
1103
1104         session_changed(session, CONNMAN_SESSION_TRIGGER_CONNECT);
1105
1106         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1107 }
1108
1109 static DBusMessage *disconnect_session(DBusConnection *conn,
1110                                         DBusMessage *msg, void *user_data)
1111 {
1112         struct connman_session *session = user_data;
1113         struct session_info *info = session->info;
1114
1115         DBG("session %p", session);
1116
1117         if (ecall_info != NULL && ecall_info != info)
1118                 return __connman_error_failed(msg, EBUSY);
1119
1120         session_changed(session, CONNMAN_SESSION_TRIGGER_DISCONNECT);
1121
1122         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1123 }
1124
1125 static void update_ecall_sessions(struct connman_session *session)
1126 {
1127         struct session_info *info = session->info;
1128         struct connman_session *session_iter;
1129         GHashTableIter iter;
1130         gpointer key, value;
1131
1132         g_hash_table_iter_init(&iter, session_hash);
1133
1134         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1135                 session_iter = value;
1136
1137                 if (session_iter == session)
1138                         continue;
1139
1140                 session_iter->info->ecall = info->ecall;
1141                 session_iter->info_dirty = TRUE;
1142
1143                 session_changed(session_iter, CONNMAN_SESSION_TRIGGER_ECALL);
1144         }
1145 }
1146
1147 static void update_ecall(struct connman_session *session)
1148 {
1149         struct session_info *info = session->info;
1150         struct session_info *info_last = session->info_last;
1151
1152         DBG("session %p ecall_info %p ecall %d -> %d", session,
1153                 ecall_info, info_last->ecall, info->ecall);
1154
1155         if (ecall_info == NULL) {
1156                 if (!(info_last->ecall == FALSE && info->ecall == TRUE))
1157                         goto err;
1158
1159                 ecall_info = info;
1160         } else if (ecall_info == info) {
1161                 if (!(info_last->ecall == TRUE && info->ecall == FALSE))
1162                         goto err;
1163
1164                 ecall_info = NULL;
1165         } else {
1166                 goto err;
1167         }
1168
1169         update_ecall_sessions(session);
1170
1171         session->info_dirty = TRUE;
1172         return;
1173
1174 err:
1175         /* not a valid transition */
1176         info->ecall = info_last->ecall;
1177 }
1178
1179 static DBusMessage *change_session(DBusConnection *conn,
1180                                         DBusMessage *msg, void *user_data)
1181 {
1182         struct connman_session *session = user_data;
1183         struct session_info *info = session->info;
1184         struct session_info *info_last = session->info_last;
1185         DBusMessageIter iter, value;
1186         const char *name;
1187         GSList *allowed_bearers;
1188
1189         DBG("session %p", session);
1190         if (dbus_message_iter_init(msg, &iter) == FALSE)
1191                 return __connman_error_invalid_arguments(msg);
1192
1193         dbus_message_iter_get_basic(&iter, &name);
1194         dbus_message_iter_next(&iter);
1195         dbus_message_iter_recurse(&iter, &value);
1196
1197         switch (dbus_message_iter_get_arg_type(&value)) {
1198         case DBUS_TYPE_ARRAY:
1199                 if (g_str_equal(name, "AllowedBearers") == TRUE) {
1200                         allowed_bearers = session_parse_allowed_bearers(&value);
1201
1202                         g_slist_foreach(info->allowed_bearers,
1203                                         cleanup_bearer_info, NULL);
1204                         g_slist_free(info->allowed_bearers);
1205
1206                         if (allowed_bearers == NULL) {
1207                                 allowed_bearers = session_allowed_bearers_any();
1208
1209                                 if (allowed_bearers == NULL)
1210                                         return __connman_error_failed(msg, ENOMEM);
1211                         }
1212
1213                         info->allowed_bearers = allowed_bearers;
1214
1215                         session->info_dirty = TRUE;
1216                 } else {
1217                         goto err;
1218                 }
1219                 break;
1220         case DBUS_TYPE_BOOLEAN:
1221                 if (g_str_equal(name, "Priority") == TRUE) {
1222                         dbus_message_iter_get_basic(&value,
1223                                         &info->priority);
1224
1225                         if (info_last->priority != info->priority)
1226                                 session->info_dirty = TRUE;
1227                 } else if (g_str_equal(name, "AvoidHandover") == TRUE) {
1228                         dbus_message_iter_get_basic(&value,
1229                                         &info->avoid_handover);
1230
1231                         if (info_last->avoid_handover != info->avoid_handover)
1232                                 session->info_dirty = TRUE;
1233                 } else if (g_str_equal(name, "StayConnected") == TRUE) {
1234                         dbus_message_iter_get_basic(&value,
1235                                         &info->stay_connected);
1236
1237                         if (info_last->stay_connected != info->stay_connected)
1238                                 session->info_dirty = TRUE;
1239                 } else if (g_str_equal(name, "EmergencyCall") == TRUE) {
1240                         dbus_message_iter_get_basic(&value,
1241                                         &info->ecall);
1242
1243                         update_ecall(session);
1244                 } else {
1245                         goto err;
1246                 }
1247                 break;
1248         case DBUS_TYPE_UINT32:
1249                 if (g_str_equal(name, "PeriodicConnect") == TRUE) {
1250                         dbus_message_iter_get_basic(&value,
1251                                         &info->periodic_connect);
1252
1253                         if (info_last->periodic_connect != info->periodic_connect)
1254                                 session->info_dirty = TRUE;
1255                 } else if (g_str_equal(name, "IdleTimeout") == TRUE) {
1256                         dbus_message_iter_get_basic(&value,
1257                                         &info->idle_timeout);
1258
1259                         if (info_last->idle_timeout != info->idle_timeout)
1260                                 session->info_dirty = TRUE;
1261                 } else {
1262                         goto err;
1263                 }
1264                 break;
1265         case DBUS_TYPE_STRING:
1266                 if (g_str_equal(name, "RoamingPolicy") == TRUE) {
1267                         const char *val;
1268                         dbus_message_iter_get_basic(&value, &val);
1269                         info->roaming_policy =
1270                                         string2roamingpolicy(val);
1271
1272                         if (info_last->roaming_policy != info->roaming_policy)
1273                                 session->info_dirty = TRUE;
1274                 } else {
1275                         goto err;
1276                 }
1277                 break;
1278         default:
1279                 goto err;
1280         }
1281
1282         if (session->info_dirty == TRUE)
1283                 session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING);
1284
1285         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1286
1287 err:
1288         return __connman_error_invalid_arguments(msg);
1289 }
1290
1291 static void release_session(gpointer key, gpointer value, gpointer user_data)
1292 {
1293         struct connman_session *session = value;
1294         DBusMessage *message;
1295
1296         DBG("owner %s path %s", session->owner, session->notify_path);
1297
1298         if (session->notify_watch > 0)
1299                 g_dbus_remove_watch(connection, session->notify_watch);
1300
1301         g_dbus_unregister_interface(connection, session->session_path,
1302                                                 CONNMAN_SESSION_INTERFACE);
1303
1304         message = dbus_message_new_method_call(session->owner,
1305                                                 session->notify_path,
1306                                                 CONNMAN_NOTIFICATION_INTERFACE,
1307                                                 "Release");
1308         if (message == NULL)
1309                 return;
1310
1311         dbus_message_set_no_reply(message, TRUE);
1312
1313         g_dbus_send_message(connection, message);
1314 }
1315
1316 static int session_disconnect(struct connman_session *session)
1317 {
1318         DBG("session %p, %s", session, session->owner);
1319
1320         if (session->notify_watch > 0)
1321                 g_dbus_remove_watch(connection, session->notify_watch);
1322
1323         g_dbus_unregister_interface(connection, session->session_path,
1324                                                 CONNMAN_SESSION_INTERFACE);
1325
1326         deselect_and_disconnect(session,
1327                                 CONNMAN_SESSION_REASON_DISCONNECT);
1328
1329         g_hash_table_remove(session_hash, session->session_path);
1330
1331         return 0;
1332 }
1333
1334 static void owner_disconnect(DBusConnection *conn, void *user_data)
1335 {
1336         struct connman_session *session = user_data;
1337
1338         DBG("session %p, %s died", session, session->owner);
1339
1340         session_disconnect(session);
1341 }
1342
1343 static DBusMessage *destroy_session(DBusConnection *conn,
1344                                         DBusMessage *msg, void *user_data)
1345 {
1346         struct connman_session *session = user_data;
1347
1348         DBG("session %p", session);
1349
1350         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1351 }
1352
1353 static GDBusMethodTable session_methods[] = {
1354         { "Destroy",    "",   "", destroy_session    },
1355         { "Connect",    "",   "", connect_session    },
1356         { "Disconnect", "",   "", disconnect_session },
1357         { "Change",     "sv", "", change_session     },
1358         { },
1359 };
1360
1361 int __connman_session_create(DBusMessage *msg)
1362 {
1363         const char *owner, *notify_path;
1364         char *session_path = NULL;
1365         DBusMessageIter iter, array;
1366         struct connman_session *session = NULL;
1367         struct session_info *info, *info_last;
1368
1369         connman_bool_t priority = FALSE, avoid_handover = FALSE;
1370         connman_bool_t stay_connected = FALSE, ecall = FALSE;
1371         enum connman_session_roaming_policy roaming_policy =
1372                                 CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN;
1373         GSList *allowed_bearers = NULL;
1374         unsigned int periodic_connect = 0;
1375         unsigned int idle_timeout = 0;
1376
1377         int err;
1378
1379         owner = dbus_message_get_sender(msg);
1380
1381         DBG("owner %s", owner);
1382
1383         if (ecall_info != NULL) {
1384                 /*
1385                  * If there is an emergency call already going on,
1386                  * ignore session creation attempt
1387                  */
1388                 err = -EBUSY;
1389                 goto err;
1390         }
1391
1392         dbus_message_iter_init(msg, &iter);
1393         dbus_message_iter_recurse(&iter, &array);
1394
1395         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
1396                 DBusMessageIter entry, value;
1397                 const char *key, *val;
1398
1399                 dbus_message_iter_recurse(&array, &entry);
1400                 dbus_message_iter_get_basic(&entry, &key);
1401
1402                 dbus_message_iter_next(&entry);
1403                 dbus_message_iter_recurse(&entry, &value);
1404
1405                 switch (dbus_message_iter_get_arg_type(&value)) {
1406                 case DBUS_TYPE_ARRAY:
1407                         if (g_str_equal(key, "AllowedBearers") == TRUE) {
1408                                 allowed_bearers =
1409                                         session_parse_allowed_bearers(&value);
1410                         } else {
1411                                 return -EINVAL;
1412                         }
1413                         break;
1414                 case DBUS_TYPE_BOOLEAN:
1415                         if (g_str_equal(key, "Priority") == TRUE) {
1416                                 dbus_message_iter_get_basic(&value,
1417                                                         &priority);
1418                         } else if (g_str_equal(key, "AvoidHandover") == TRUE) {
1419                                 dbus_message_iter_get_basic(&value,
1420                                                         &avoid_handover);
1421                         } else if (g_str_equal(key, "StayConnected") == TRUE) {
1422                                 dbus_message_iter_get_basic(&value,
1423                                                         &stay_connected);
1424                         } else if (g_str_equal(key, "EmergencyCall") == TRUE) {
1425                                 dbus_message_iter_get_basic(&value,
1426                                                         &ecall);
1427                         } else {
1428                                 return -EINVAL;
1429                         }
1430                         break;
1431                 case DBUS_TYPE_UINT32:
1432                         if (g_str_equal(key, "PeriodicConnect") == TRUE) {
1433                                 dbus_message_iter_get_basic(&value,
1434                                                         &periodic_connect);
1435                         } else if (g_str_equal(key, "IdleTimeout") == TRUE) {
1436                                 dbus_message_iter_get_basic(&value,
1437                                                         &idle_timeout);
1438                         } else {
1439                                 return -EINVAL;
1440                         }
1441                         break;
1442                 case DBUS_TYPE_STRING:
1443                         if (g_str_equal(key, "RoamingPolicy") == TRUE) {
1444                                 dbus_message_iter_get_basic(&value, &val);
1445                                 roaming_policy = string2roamingpolicy(val);
1446                         } else {
1447                                 return -EINVAL;
1448                         }
1449                 }
1450                 dbus_message_iter_next(&array);
1451         }
1452
1453         dbus_message_iter_next(&iter);
1454         dbus_message_iter_get_basic(&iter, &notify_path);
1455
1456         if (notify_path == NULL) {
1457                 err = -EINVAL;
1458                 goto err;
1459         }
1460
1461         session_path = g_strdup_printf("/sessions%s", notify_path);
1462         if (session_path == NULL) {
1463                 err = -ENOMEM;
1464                 goto err;
1465         }
1466
1467         session = g_hash_table_lookup(session_hash, session_path);
1468         if (session != NULL) {
1469                 session = NULL;
1470                 err = -EEXIST;
1471                 goto err;
1472         }
1473
1474         session = g_try_new0(struct connman_session, 1);
1475         if (session == NULL) {
1476                 err = -ENOMEM;
1477                 goto err;
1478         }
1479
1480         session->info = g_try_new0(struct session_info, 1);
1481         if (session->info == NULL) {
1482                 err = -ENOMEM;
1483                 goto err;
1484         }
1485
1486         session->info_last = g_try_new0(struct session_info, 1);
1487         if (session->info_last == NULL) {
1488                 err = -ENOMEM;
1489                 goto err;
1490         }
1491
1492         info = session->info;
1493         info_last = session->info_last;
1494
1495         session->owner = g_strdup(owner);
1496         session->session_path = session_path;
1497         session->notify_path = g_strdup(notify_path);
1498         session->notify_watch =
1499                 g_dbus_add_disconnect_watch(connection, session->owner,
1500                                         owner_disconnect, session, NULL);
1501
1502         info->online = FALSE;
1503         info->priority = priority;
1504         info->avoid_handover = avoid_handover;
1505         info->stay_connected = stay_connected;
1506         info->periodic_connect = periodic_connect;
1507         info->idle_timeout = idle_timeout;
1508         info->ecall = ecall;
1509         info->roaming_policy = roaming_policy;
1510         info->entry = NULL;
1511         info->marker = 0;
1512
1513         if (allowed_bearers == NULL) {
1514                 info->allowed_bearers =
1515                                 session_allowed_bearers_any();
1516
1517                 if (info->allowed_bearers == NULL) {
1518                         err = -ENOMEM;
1519                         goto err;
1520                 }
1521         } else {
1522                 info->allowed_bearers = allowed_bearers;
1523         }
1524
1525         g_hash_table_replace(session_hash, session->session_path, session);
1526
1527         DBG("add %s", session->session_path);
1528
1529         if (g_dbus_register_interface(connection, session->session_path,
1530                                         CONNMAN_SESSION_INTERFACE,
1531                                         session_methods, NULL,
1532                                         NULL, session, NULL) == FALSE) {
1533                 connman_error("Failed to register %s", session->session_path);
1534                 g_hash_table_remove(session_hash, session->session_path);
1535                 session = NULL;
1536
1537                 err = -EINVAL;
1538                 goto err;
1539         }
1540
1541         g_dbus_send_reply(connection, msg,
1542                                 DBUS_TYPE_OBJECT_PATH, &session->session_path,
1543                                 DBUS_TYPE_INVALID);
1544
1545
1546         populate_service_list(session);
1547         if (info->ecall == TRUE) {
1548                 ecall_info = info;
1549                 update_ecall_sessions(session);
1550         }
1551
1552         info_last->online = info->online;
1553         info_last->priority = info->priority;
1554         info_last->avoid_handover = info->avoid_handover;
1555         info_last->stay_connected = info->stay_connected;
1556         info_last->periodic_connect = info->periodic_connect;
1557         info_last->idle_timeout = info->idle_timeout;
1558         info_last->ecall = info->ecall;
1559         info_last->roaming_policy = info->roaming_policy;
1560         info_last->entry = info->entry;
1561         info_last->marker = info->marker;
1562         info_last->allowed_bearers = info->allowed_bearers;
1563
1564         session->info_dirty = TRUE;
1565         session->append_all = TRUE;
1566
1567         session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING);
1568
1569         return 0;
1570
1571 err:
1572         connman_error("Failed to create session");
1573
1574         if (session != NULL) {
1575                 if (session->info_last != NULL)
1576                         g_free(session->info_last);
1577                 if (session->info != NULL)
1578                         g_free(session->info);
1579                 g_free(session);
1580         }
1581
1582         g_free(session_path);
1583
1584         g_slist_foreach(allowed_bearers, cleanup_bearer_info, NULL);
1585         g_slist_free(allowed_bearers);
1586
1587         return err;
1588 }
1589
1590 int __connman_session_destroy(DBusMessage *msg)
1591 {
1592         const char *owner, *session_path;
1593         struct connman_session *session;
1594
1595         owner = dbus_message_get_sender(msg);
1596
1597         DBG("owner %s", owner);
1598
1599         dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &session_path,
1600                                                         DBUS_TYPE_INVALID);
1601         if (session_path == NULL)
1602                 return -EINVAL;
1603
1604         session = g_hash_table_lookup(session_hash, session_path);
1605         if (session == NULL)
1606                 return -EINVAL;
1607
1608         if (g_strcmp0(owner, session->owner) != 0)
1609                 return -EACCES;
1610
1611         session_disconnect(session);
1612
1613         return 0;
1614 }
1615
1616 connman_bool_t __connman_session_mode()
1617 {
1618         return sessionmode;
1619 }
1620
1621 void __connman_session_set_mode(connman_bool_t enable)
1622 {
1623         DBG("enable %d", enable);
1624
1625         sessionmode = enable;
1626
1627         if (sessionmode == TRUE)
1628                 __connman_service_disconnect_all();
1629 }
1630
1631 static void service_add(struct connman_service *service,
1632                         const char *name)
1633 {
1634         GHashTableIter iter;
1635         GSequenceIter *iter_service_list;
1636         gpointer key, value;
1637         struct connman_session *session;
1638         struct service_entry *entry;
1639
1640         DBG("service %p", service);
1641
1642         g_hash_table_iter_init(&iter, session_hash);
1643
1644         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1645                 session = value;
1646
1647                 if (service_match(session, service) == FALSE)
1648                         continue;
1649
1650                 entry = create_service_entry(service, name,
1651                                                 CONNMAN_SERVICE_STATE_IDLE);
1652                 if (entry == NULL)
1653                         continue;
1654
1655                 iter_service_list =
1656                         g_sequence_insert_sorted(session->service_list,
1657                                                         entry, sort_services,
1658                                                         session);
1659
1660                 g_hash_table_replace(session->service_hash, service,
1661                                         iter_service_list);
1662
1663                 session_changed(session, CONNMAN_SESSION_TRIGGER_SERVICE);
1664         }
1665 }
1666
1667 static void service_remove(struct connman_service *service)
1668 {
1669
1670         GHashTableIter iter;
1671         gpointer key, value;
1672         struct connman_session *session;
1673         struct session_info *info;
1674
1675         DBG("service %p", service);
1676
1677         g_hash_table_iter_init(&iter, session_hash);
1678
1679         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1680                 GSequenceIter *iter;
1681                 session = value;
1682                 info = session->info;
1683
1684                 iter = g_hash_table_lookup(session->service_hash, service);
1685                 if (iter == NULL)
1686                         continue;
1687
1688                 g_sequence_remove(iter);
1689
1690                 if (info->entry != NULL && info->entry->service == service)
1691                         info->entry = NULL;
1692                 session_changed(session, CONNMAN_SESSION_TRIGGER_SERVICE);
1693         }
1694 }
1695
1696 static void service_state_changed(struct connman_service *service,
1697                                         enum connman_service_state state)
1698 {
1699         GHashTableIter iter;
1700         gpointer key, value;
1701
1702         DBG("service %p state %d", service, state);
1703
1704         g_hash_table_iter_init(&iter, session_hash);
1705
1706         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1707                 struct connman_session *session = value;
1708                 GSequenceIter *service_iter;
1709
1710                 service_iter = g_hash_table_lookup(session->service_hash, service);
1711                 if (service_iter != NULL) {
1712                         struct service_entry *entry;
1713
1714                         entry = g_sequence_get(service_iter);
1715                         entry->state = state;
1716                 }
1717
1718                 session_changed(session,
1719                                 CONNMAN_SESSION_TRIGGER_SERVICE);
1720         }
1721 }
1722
1723 static void ipconfig_changed(struct connman_service *service,
1724                                 struct connman_ipconfig *ipconfig)
1725 {
1726         GHashTableIter iter;
1727         gpointer key, value;
1728         struct connman_session *session;
1729         struct session_info *info;
1730         enum connman_ipconfig_type type;
1731
1732         DBG("service %p ipconfig %p", service, ipconfig);
1733
1734         type = __connman_ipconfig_get_config_type(ipconfig);
1735
1736         g_hash_table_iter_init(&iter, session_hash);
1737
1738         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1739                 session = value;
1740                 info = session->info;
1741
1742                 if (info->entry != NULL && info->entry->service == service) {
1743                         if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
1744                                 ipconfig_ipv4_changed(session);
1745                         else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
1746                                 ipconfig_ipv6_changed(session);
1747                 }
1748         }
1749 }
1750
1751 static struct connman_notifier session_notifier = {
1752         .name                   = "session",
1753         .service_add            = service_add,
1754         .service_remove         = service_remove,
1755         .service_state_changed  = service_state_changed,
1756         .ipconfig_changed       = ipconfig_changed,
1757 };
1758
1759 int __connman_session_init(void)
1760 {
1761         int err;
1762
1763         DBG("");
1764
1765         connection = connman_dbus_get_connection();
1766         if (connection == NULL)
1767                 return -1;
1768
1769         err = connman_notifier_register(&session_notifier);
1770         if (err < 0) {
1771                 dbus_connection_unref(connection);
1772                 return err;
1773         }
1774
1775         session_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1776                                                 NULL, cleanup_session);
1777
1778         sessionmode = FALSE;
1779         return 0;
1780 }
1781
1782 void __connman_session_cleanup(void)
1783 {
1784         DBG("");
1785
1786         if (connection == NULL)
1787                 return;
1788
1789         connman_notifier_unregister(&session_notifier);
1790
1791         g_hash_table_foreach(session_hash, release_session, NULL);
1792         g_hash_table_destroy(session_hash);
1793         session_hash = NULL;
1794
1795         dbus_connection_unref(connection);
1796 }