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