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