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