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