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