service: Add create callback for __connman_service_get_list()
[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         struct connman_service *service;
64 };
65
66 struct session_info {
67         char *bearer;
68         const char *name;
69         char *ifname;
70         connman_bool_t online;
71         connman_bool_t priority;
72         GSList *allowed_bearers;
73         connman_bool_t avoid_handover;
74         connman_bool_t stay_connected;
75         unsigned int periodic_connect;
76         unsigned int idle_timeout;
77         connman_bool_t ecall;
78         enum connman_session_roaming_policy roaming_policy;
79         unsigned int marker;
80
81         /* track why this service was selected */
82         enum connman_session_reason reason;
83         struct service_entry *entry;
84 };
85
86 struct connman_session {
87         char *owner;
88         char *session_path;
89         char *notify_path;
90         guint notify_watch;
91
92         connman_bool_t append_all;
93         connman_bool_t info_dirty;
94         struct session_info info;
95         struct session_info info_last;
96
97         GSequence *service_list;
98         GHashTable *service_hash;
99 };
100
101 struct bearer_info {
102         char *name;
103         connman_bool_t match_all;
104         enum connman_service_type service_type;
105 };
106
107 static const char *trigger2string(enum connman_session_trigger trigger)
108 {
109         switch (trigger) {
110         case CONNMAN_SESSION_TRIGGER_UNKNOWN:
111                 break;
112         case CONNMAN_SESSION_TRIGGER_SETTING:
113                 return "setting";
114         case CONNMAN_SESSION_TRIGGER_CONNECT:
115                 return "connect";
116         case CONNMAN_SESSION_TRIGGER_DISCONNECT:
117                 return "disconnect";
118         case CONNMAN_SESSION_TRIGGER_PERIODIC:
119                 return "periodic";
120         case CONNMAN_SESSION_TRIGGER_SERVICE:
121                 return "service";
122         case CONNMAN_SESSION_TRIGGER_ECALL:
123                 return "ecall";
124         }
125
126         return NULL;
127 }
128
129 static const char *reason2string(enum connman_session_reason reason)
130 {
131         switch (reason) {
132         case CONNMAN_SESSION_REASON_UNKNOWN:
133                 break;
134         case CONNMAN_SESSION_REASON_CONNECT:
135                 return "connect";
136         case CONNMAN_SESSION_REASON_FREE_RIDE:
137                 return "free-ride";
138         case CONNMAN_SESSION_REASON_PERIODIC:
139                 return "periodic";
140         }
141
142         return NULL;
143 }
144
145 static const char *roamingpolicy2string(enum connman_session_roaming_policy policy)
146 {
147         switch (policy) {
148         case CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN:
149                 break;
150         case CONNMAN_SESSION_ROAMING_POLICY_DEFAULT:
151                 return "default";
152         case CONNMAN_SESSION_ROAMING_POLICY_ALWAYS:
153                 return "always";
154         case CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN:
155                 return "forbidden";
156         case CONNMAN_SESSION_ROAMING_POLICY_NATIONAL:
157                 return "national";
158         case CONNMAN_SESSION_ROAMING_POLICY_INTERNATIONAL:
159                 return "international";
160         }
161
162         return NULL;
163 }
164
165 static enum connman_session_roaming_policy string2roamingpolicy(const char *policy)
166 {
167         if (g_strcmp0(policy, "default") == 0)
168                 return CONNMAN_SESSION_ROAMING_POLICY_DEFAULT;
169         else if (g_strcmp0(policy, "always") == 0)
170                 return CONNMAN_SESSION_ROAMING_POLICY_ALWAYS;
171         else if (g_strcmp0(policy, "forbidden") == 0)
172                 return CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN;
173         else if (g_strcmp0(policy, "national") == 0)
174                 return CONNMAN_SESSION_ROAMING_POLICY_NATIONAL;
175         else if (g_strcmp0(policy, "international") == 0)
176                 return CONNMAN_SESSION_ROAMING_POLICY_INTERNATIONAL;
177         else
178                 return CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN;
179 }
180
181 static enum connman_service_type bearer2service(const char *bearer)
182 {
183         if (bearer == NULL)
184                 return CONNMAN_SERVICE_TYPE_UNKNOWN;
185
186         if (g_strcmp0(bearer, "ethernet") == 0)
187                 return CONNMAN_SERVICE_TYPE_ETHERNET;
188         else if (g_strcmp0(bearer, "wifi") == 0)
189                 return CONNMAN_SERVICE_TYPE_WIFI;
190         else if (g_strcmp0(bearer, "wimax") == 0)
191                 return CONNMAN_SERVICE_TYPE_WIMAX;
192         else if (g_strcmp0(bearer, "bluetooth") == 0)
193                 return CONNMAN_SERVICE_TYPE_BLUETOOTH;
194         else if (g_strcmp0(bearer, "3g") == 0)
195                 return CONNMAN_SERVICE_TYPE_CELLULAR;
196         else
197                 return CONNMAN_SERVICE_TYPE_UNKNOWN;
198 }
199
200 static char *service2bearer(enum connman_service_type type)
201 {
202         switch (type) {
203         case CONNMAN_SERVICE_TYPE_ETHERNET:
204                 return "ethernet";
205         case CONNMAN_SERVICE_TYPE_WIFI:
206                 return "wifi";
207         case CONNMAN_SERVICE_TYPE_WIMAX:
208                 return "wimax";
209         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
210                 return "bluetooth";
211         case CONNMAN_SERVICE_TYPE_CELLULAR:
212                 return "3g";
213         case CONNMAN_SERVICE_TYPE_UNKNOWN:
214         case CONNMAN_SERVICE_TYPE_SYSTEM:
215         case CONNMAN_SERVICE_TYPE_GPS:
216         case CONNMAN_SERVICE_TYPE_VPN:
217         case CONNMAN_SERVICE_TYPE_GADGET:
218                 return "";
219         }
220
221         return "";
222 }
223
224 static void cleanup_bearer_info(gpointer data, gpointer user_data)
225 {
226         struct bearer_info *info = data;
227
228         g_free(info->name);
229         g_free(info);
230 }
231
232 static GSList *session_parse_allowed_bearers(DBusMessageIter *iter)
233 {
234         struct bearer_info *info;
235         DBusMessageIter array;
236         GSList *list = NULL;
237
238         dbus_message_iter_recurse(iter, &array);
239
240         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) {
241                 char *bearer = NULL;
242
243                 dbus_message_iter_get_basic(&array, &bearer);
244
245                 info = g_try_new0(struct bearer_info, 1);
246                 if (info == NULL) {
247                         g_slist_foreach(list, cleanup_bearer_info, NULL);
248                         g_slist_free(list);
249
250                         return NULL;
251                 }
252
253                 info->name = g_strdup(bearer);
254                 info->service_type = bearer2service(info->name);
255
256                 if (info->service_type == CONNMAN_SERVICE_TYPE_UNKNOWN &&
257                                 g_strcmp0(info->name, "*") == 0) {
258                         info->match_all = TRUE;
259                 } else {
260                         info->match_all = FALSE;
261                 }
262
263                 list = g_slist_append(list, info);
264
265                 dbus_message_iter_next(&array);
266         }
267
268         return list;
269 }
270
271 static GSList *session_allowed_bearers_any(void)
272 {
273         struct bearer_info *info;
274         GSList *list = NULL;
275
276         info = g_try_new0(struct bearer_info, 1);
277         if (info == NULL) {
278                 g_slist_free(list);
279
280                 return NULL;
281         }
282
283         info->name = g_strdup("");
284         info->match_all = TRUE;
285         info->service_type = CONNMAN_SERVICE_TYPE_UNKNOWN;
286
287         list = g_slist_append(list, info);
288
289         return list;
290 }
291
292 static void append_allowed_bearers(DBusMessageIter *iter, void *user_data)
293 {
294         struct session_info *info = user_data;
295         GSList *list;
296
297         for (list = info->allowed_bearers;
298                         list != NULL; list = list->next) {
299                 struct bearer_info *info = list->data;
300
301                 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
302                                                 &info->name);
303         }
304 }
305
306 static void append_ipconfig_ipv4(DBusMessageIter *iter, void *user_data)
307 {
308         struct connman_service *service = user_data;
309         struct connman_ipconfig *ipconfig_ipv4;
310
311         if (service == NULL)
312                 return;
313
314         ipconfig_ipv4 = __connman_service_get_ip4config(service);
315         if (ipconfig_ipv4 == NULL)
316                 return;
317
318         __connman_ipconfig_append_ipv4(ipconfig_ipv4, iter);
319 }
320
321 static void append_ipconfig_ipv6(DBusMessageIter *iter, void *user_data)
322 {
323         struct connman_service *service = user_data;
324         struct connman_ipconfig *ipconfig_ipv4, *ipconfig_ipv6;
325
326         if (service == NULL)
327                 return;
328
329         ipconfig_ipv4 = __connman_service_get_ip4config(service);
330         ipconfig_ipv6 = __connman_service_get_ip6config(service);
331         if (ipconfig_ipv6 == NULL)
332                 return;
333
334         __connman_ipconfig_append_ipv6(ipconfig_ipv6, iter, ipconfig_ipv4);
335 }
336
337 static void append_notify(DBusMessageIter *dict,
338                                         struct connman_session *session)
339 {
340         struct session_info *info = &session->info;
341         struct session_info *info_last = &session->info_last;
342         const char *policy;
343         struct connman_service *service;
344
345         if (session->append_all == TRUE ||
346                         info->bearer != info_last->bearer) {
347                 connman_dbus_dict_append_basic(dict, "Bearer",
348                                                 DBUS_TYPE_STRING,
349                                                 &info->bearer);
350                 info_last->bearer = info->bearer;
351         }
352
353         if (session->append_all == TRUE ||
354                         info->online != info_last->online) {
355                 connman_dbus_dict_append_basic(dict, "Online",
356                                                 DBUS_TYPE_BOOLEAN,
357                                                 &info->online);
358                 info_last->online = info->online;
359         }
360
361         if (session->append_all == TRUE ||
362                         info->name != info_last->name) {
363                 connman_dbus_dict_append_basic(dict, "Name",
364                                                 DBUS_TYPE_STRING,
365                                                 &info->name);
366                 info_last->name = info->name;
367         }
368
369         if (session->append_all == TRUE ||
370                         info->entry != info_last->entry) {
371                 if (info->entry == NULL)
372                         service = NULL;
373                 else
374                         service = info->entry->service;
375
376                 connman_dbus_dict_append_dict(dict, "IPv4",
377                                                 append_ipconfig_ipv4,
378                                                 service);
379
380                 connman_dbus_dict_append_dict(dict, "IPv6",
381                                                 append_ipconfig_ipv6,
382                                                 service);
383
384                 connman_dbus_dict_append_basic(dict, "Interface",
385                                                 DBUS_TYPE_STRING,
386                                                 &info->ifname);
387
388                 info_last->ifname = info->ifname;
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->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 void update_info(struct session_info *info)
730 {
731         enum connman_service_type type;
732         int idx;
733
734         if (info->entry != NULL) {
735                 type = connman_service_get_type(info->entry->service);
736                 info->bearer = service2bearer(type);
737
738                 info->online = __connman_service_is_connected(info->entry->service);
739                 info->name = __connman_service_get_name(info->entry->service);
740                 if (info->name == NULL)
741                         info->name = "";
742
743                 idx = __connman_service_get_index(info->entry->service);
744                 info->ifname = connman_inet_ifname(idx);
745                 if (info->ifname == NULL)
746                         info->ifname = "";
747         } else {
748                 info->bearer = "";
749                 info->online = FALSE;
750                 info->name = "";
751                 info->ifname = "";
752         }
753 }
754
755 static connman_bool_t explicit_connect(enum connman_session_reason reason)
756 {
757         switch (reason) {
758         case CONNMAN_SESSION_REASON_UNKNOWN:
759         case CONNMAN_SESSION_REASON_FREE_RIDE:
760                 break;
761         case CONNMAN_SESSION_REASON_CONNECT:
762         case CONNMAN_SESSION_REASON_PERIODIC:
763                 return TRUE;
764         }
765
766         return FALSE;
767 }
768
769 static void test_and_disconnect(struct connman_session *session)
770 {
771         struct session_info *info = &session->info;
772
773         if (explicit_connect(info->reason) == FALSE)
774                 goto out;
775
776         if (__connman_service_session_dec(info->entry->service) == TRUE)
777                 goto out;
778
779         if (ecall_session != NULL && ecall_session != session)
780                 goto out;
781
782         __connman_service_disconnect(info->entry->service);
783
784 out:
785         info->reason = CONNMAN_SESSION_REASON_UNKNOWN;
786         info->entry = NULL;
787 }
788
789 static void select_and_connect(struct connman_session *session,
790                                 enum connman_session_reason reason)
791 {
792         struct session_info *info = &session->info;
793         struct service_entry *entry = NULL;
794         GSequenceIter *iter;
795         connman_bool_t do_connect = FALSE;
796
797         DBG("session %p reason %s", session, reason2string(reason));
798
799         iter = g_sequence_get_begin_iter(session->service_list);
800
801         while (g_sequence_iter_is_end(iter) == FALSE) {
802                 entry = g_sequence_get(iter);
803
804                 if (__connman_service_is_connecting(entry->service) == TRUE ||
805                                 __connman_service_is_connected(entry->service) == TRUE) {
806                         break;
807                 }
808
809                 if (__connman_service_is_idle(entry->service) == TRUE &&
810                                 explicit_connect(reason) == TRUE) {
811                         do_connect = TRUE;
812                         break;
813                 }
814
815                 entry = NULL;
816
817                 iter = g_sequence_iter_next(iter);
818         }
819
820         if (info->entry != NULL && info->entry != entry)
821                 test_and_disconnect(session);
822
823         if (entry != NULL) {
824                 info->reason = reason;
825                 info->entry = entry;
826
827                 if (explicit_connect(reason) == TRUE)
828                         __connman_service_session_inc(info->entry->service);
829
830                 if (do_connect == TRUE)
831                         __connman_service_connect(info->entry->service);
832         }
833 }
834
835 static void session_changed(struct connman_session *session,
836                                 enum connman_session_trigger trigger)
837 {
838         struct session_info *info = &session->info;
839         struct session_info *info_last = &session->info_last;
840         GSequenceIter *iter;
841
842         /*
843          * TODO: This only a placeholder for the 'real' algorithm to
844          * play a bit around. So we are going to improve it step by step.
845          */
846
847         DBG("session %p trigger %s", session, trigger2string(trigger));
848
849         switch (trigger) {
850         case CONNMAN_SESSION_TRIGGER_UNKNOWN:
851                 DBG("ignore session changed event");
852                 return;
853         case CONNMAN_SESSION_TRIGGER_SETTING:
854                 if (info->entry != NULL) {
855                         iter = g_hash_table_lookup(session->service_hash,
856                                                         info->entry->service);
857                         if (iter == NULL) {
858                                 /*
859                                  * This service is not part of this
860                                  * session anymore.
861                                  */
862                                 test_and_disconnect(session);
863                         }
864                 }
865
866                 if (info->online == FALSE) {
867                         select_and_connect(session,
868                                         CONNMAN_SESSION_REASON_FREE_RIDE);
869                 }
870
871                 break;
872         case CONNMAN_SESSION_TRIGGER_CONNECT:
873                 if (info->online == TRUE) {
874                         info->reason = CONNMAN_SESSION_REASON_CONNECT;
875                         __connman_service_session_inc(info->entry->service);
876                         break;
877                 }
878
879                 select_and_connect(session,
880                                 CONNMAN_SESSION_REASON_CONNECT);
881
882                 break;
883         case CONNMAN_SESSION_TRIGGER_DISCONNECT:
884                 test_and_disconnect(session);
885
886                 break;
887         case CONNMAN_SESSION_TRIGGER_PERIODIC:
888                 if (info->online == TRUE) {
889                         info->reason = CONNMAN_SESSION_REASON_PERIODIC;
890                         __connman_service_session_inc(info->entry->service);
891                         break;
892                 }
893
894                 select_and_connect(session,
895                                 CONNMAN_SESSION_REASON_PERIODIC);
896
897                 break;
898         case CONNMAN_SESSION_TRIGGER_SERVICE:
899                 if (info->online == TRUE)
900                         break;
901
902                 if (info->stay_connected == TRUE) {
903                         DBG("StayConnected");
904                         select_and_connect(session,
905                                         CONNMAN_SESSION_REASON_CONNECT);
906
907                         break;
908                 }
909
910                 select_and_connect(session,
911                                 CONNMAN_SESSION_REASON_FREE_RIDE);
912
913                 break;
914         case CONNMAN_SESSION_TRIGGER_ECALL:
915                 if (info->online == FALSE && info->entry->service != NULL)
916                         test_and_disconnect(session);
917
918                 break;
919         }
920
921         if (info->entry != info_last->entry) {
922                 update_info(info);
923                 session->info_dirty = TRUE;
924         }
925
926         session_notify(session);
927 }
928
929 static DBusMessage *connect_session(DBusConnection *conn,
930                                         DBusMessage *msg, void *user_data)
931 {
932         struct connman_session *session = user_data;
933
934         DBG("session %p", session);
935
936         if (ecall_session != NULL && ecall_session != session)
937                 return __connman_error_failed(msg, EBUSY);
938
939         session_changed(session, CONNMAN_SESSION_TRIGGER_CONNECT);
940
941         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
942 }
943
944 static DBusMessage *disconnect_session(DBusConnection *conn,
945                                         DBusMessage *msg, void *user_data)
946 {
947         struct connman_session *session = user_data;
948
949         DBG("session %p", session);
950
951         if (ecall_session != NULL && ecall_session != session)
952                 return __connman_error_failed(msg, EBUSY);
953
954         session_changed(session, CONNMAN_SESSION_TRIGGER_DISCONNECT);
955
956         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
957 }
958
959 static struct service_entry *create_service_entry(struct connman_service *service,
960                                         const char *name,
961                                         enum connman_service_state state)
962 {
963         struct service_entry *entry;
964
965         entry = g_try_new0(struct service_entry, 1);
966         if (entry == NULL)
967                 return entry;
968
969         entry->service = service;
970
971         return entry;
972 }
973
974 static void destroy_service_entry(gpointer data)
975 {
976         struct service_entry *entry = data;
977
978         g_free(entry);
979 }
980
981 static void update_allowed_bearers(struct connman_session *session)
982 {
983         struct service_entry *entry;
984         GSequenceIter *iter;
985
986         if (session->service_list != NULL) {
987                 g_hash_table_remove_all(session->service_hash);
988                 g_sequence_free(session->service_list);
989         }
990
991         session->service_list = __connman_service_get_list(session,
992                                                         service_match,
993                                                         create_service_entry,
994                                                         destroy_service_entry);
995
996         g_sequence_sort(session->service_list, sort_services, session);
997
998         iter = g_sequence_get_begin_iter(session->service_list);
999
1000         while (g_sequence_iter_is_end(iter) == FALSE) {
1001                 entry = g_sequence_get(iter);
1002
1003                 DBG("service %p type %s name %s", entry->service,
1004                         service2bearer(connman_service_get_type(entry->service)),
1005                         __connman_service_get_name(entry->service));
1006
1007                 g_hash_table_replace(session->service_hash,
1008                                         entry->service, iter);
1009
1010                 iter = g_sequence_iter_next(iter);
1011         }
1012
1013         session->info_dirty = TRUE;
1014 }
1015
1016 static void update_ecall_sessions(struct connman_session *session)
1017 {
1018         struct session_info *info = &session->info;
1019         struct connman_session *session_iter;
1020         GHashTableIter iter;
1021         gpointer key, value;
1022
1023         g_hash_table_iter_init(&iter, session_hash);
1024
1025         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1026                 session_iter = value;
1027
1028                 if (session_iter == session)
1029                         continue;
1030
1031                 session_iter->info.ecall = info->ecall;
1032                 session_iter->info_dirty = TRUE;
1033
1034                 session_changed(session_iter, CONNMAN_SESSION_TRIGGER_ECALL);
1035         }
1036 }
1037
1038 static void update_ecall(struct connman_session *session)
1039 {
1040         struct session_info *info = &session->info;
1041         struct session_info *info_last = &session->info_last;
1042
1043         DBG("session %p ecall_session %p ecall %d -> %d", session,
1044                 ecall_session, info_last->ecall, info->ecall);
1045
1046         if (ecall_session == NULL) {
1047                 if (!(info_last->ecall == FALSE && info->ecall == TRUE))
1048                         goto err;
1049
1050                 ecall_session = session;
1051         } else if (ecall_session == session) {
1052                 if (!(info_last->ecall == TRUE && info->ecall == FALSE))
1053                         goto err;
1054
1055                 ecall_session = NULL;
1056         } else {
1057                 goto err;
1058         }
1059
1060         update_ecall_sessions(session);
1061
1062         session->info_dirty = TRUE;
1063         return;
1064
1065 err:
1066         /* not a valid transition */
1067         info->ecall = info_last->ecall;
1068 }
1069
1070 static DBusMessage *change_session(DBusConnection *conn,
1071                                         DBusMessage *msg, void *user_data)
1072 {
1073         struct connman_session *session = user_data;
1074         struct session_info *info = &session->info;
1075         struct session_info *info_last = &session->info_last;
1076         DBusMessageIter iter, value;
1077         const char *name;
1078         GSList *allowed_bearers;
1079
1080         DBG("session %p", session);
1081         if (dbus_message_iter_init(msg, &iter) == FALSE)
1082                 return __connman_error_invalid_arguments(msg);
1083
1084         dbus_message_iter_get_basic(&iter, &name);
1085         dbus_message_iter_next(&iter);
1086         dbus_message_iter_recurse(&iter, &value);
1087
1088         switch (dbus_message_iter_get_arg_type(&value)) {
1089         case DBUS_TYPE_ARRAY:
1090                 if (g_str_equal(name, "AllowedBearers") == TRUE) {
1091                         allowed_bearers = session_parse_allowed_bearers(&value);
1092
1093                         g_slist_foreach(info->allowed_bearers,
1094                                         cleanup_bearer_info, NULL);
1095                         g_slist_free(info->allowed_bearers);
1096
1097                         if (allowed_bearers == NULL) {
1098                                 allowed_bearers = session_allowed_bearers_any();
1099
1100                                 if (allowed_bearers == NULL)
1101                                         return __connman_error_failed(msg, ENOMEM);
1102                         }
1103
1104                         info->allowed_bearers = allowed_bearers;
1105
1106                         update_allowed_bearers(session);
1107                 } else {
1108                         goto err;
1109                 }
1110                 break;
1111         case DBUS_TYPE_BOOLEAN:
1112                 if (g_str_equal(name, "Priority") == TRUE) {
1113                         dbus_message_iter_get_basic(&value,
1114                                         &info->priority);
1115
1116                         if (info_last->priority != info->priority)
1117                                 session->info_dirty = TRUE;
1118                 } else if (g_str_equal(name, "AvoidHandover") == TRUE) {
1119                         dbus_message_iter_get_basic(&value,
1120                                         &info->avoid_handover);
1121
1122                         if (info_last->avoid_handover != info->avoid_handover)
1123                                 session->info_dirty = TRUE;
1124                 } else if (g_str_equal(name, "StayConnected") == TRUE) {
1125                         dbus_message_iter_get_basic(&value,
1126                                         &info->stay_connected);
1127
1128                         if (info_last->stay_connected != info->stay_connected)
1129                                 session->info_dirty = TRUE;
1130                 } else if (g_str_equal(name, "EmergencyCall") == TRUE) {
1131                         dbus_message_iter_get_basic(&value,
1132                                         &info->ecall);
1133
1134                         update_ecall(session);
1135                 } else {
1136                         goto err;
1137                 }
1138                 break;
1139         case DBUS_TYPE_UINT32:
1140                 if (g_str_equal(name, "PeriodicConnect") == TRUE) {
1141                         dbus_message_iter_get_basic(&value,
1142                                         &info->periodic_connect);
1143
1144                         if (info_last->periodic_connect != info->periodic_connect)
1145                                 session->info_dirty = TRUE;
1146                 } else if (g_str_equal(name, "IdleTimeout") == TRUE) {
1147                         dbus_message_iter_get_basic(&value,
1148                                         &info->idle_timeout);
1149
1150                         if (info_last->idle_timeout != info->idle_timeout)
1151                                 session->info_dirty = TRUE;
1152                 } else {
1153                         goto err;
1154                 }
1155                 break;
1156         case DBUS_TYPE_STRING:
1157                 if (g_str_equal(name, "RoamingPolicy") == TRUE) {
1158                         const char *val;
1159                         dbus_message_iter_get_basic(&value, &val);
1160                         info->roaming_policy =
1161                                         string2roamingpolicy(val);
1162
1163                         if (info_last->roaming_policy != info->roaming_policy)
1164                                 session->info_dirty = TRUE;
1165                 } else {
1166                         goto err;
1167                 }
1168                 break;
1169         default:
1170                 goto err;
1171         }
1172
1173         if (session->info_dirty == TRUE)
1174                 session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING);
1175
1176         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1177
1178 err:
1179         return __connman_error_invalid_arguments(msg);
1180 }
1181
1182 static GDBusMethodTable session_methods[] = {
1183         { "Destroy",    "",   "", destroy_session    },
1184         { "Connect",    "",   "", connect_session    },
1185         { "Disconnect", "",   "", disconnect_session },
1186         { "Change",     "sv", "", change_session     },
1187         { },
1188 };
1189
1190 int __connman_session_create(DBusMessage *msg)
1191 {
1192         const char *owner, *notify_path;
1193         char *session_path = NULL;
1194         DBusMessageIter iter, array;
1195         struct connman_session *session;
1196         struct session_info *info, *info_last;
1197
1198         connman_bool_t priority = FALSE, avoid_handover = FALSE;
1199         connman_bool_t stay_connected = FALSE, ecall = FALSE;
1200         enum connman_session_roaming_policy roaming_policy =
1201                                 CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN;
1202         GSList *allowed_bearers = NULL;
1203         unsigned int periodic_connect = 0;
1204         unsigned int idle_timeout = 0;
1205
1206         int err;
1207
1208         owner = dbus_message_get_sender(msg);
1209
1210         DBG("owner %s", owner);
1211
1212         if (ecall_session != NULL) {
1213                 /*
1214                  * If there is an emergency call already going on,
1215                  * ignore session creation attempt
1216                  */
1217                 err = -EBUSY;
1218                 goto err;
1219         }
1220
1221         dbus_message_iter_init(msg, &iter);
1222         dbus_message_iter_recurse(&iter, &array);
1223
1224         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
1225                 DBusMessageIter entry, value;
1226                 const char *key, *val;
1227
1228                 dbus_message_iter_recurse(&array, &entry);
1229                 dbus_message_iter_get_basic(&entry, &key);
1230
1231                 dbus_message_iter_next(&entry);
1232                 dbus_message_iter_recurse(&entry, &value);
1233
1234                 switch (dbus_message_iter_get_arg_type(&value)) {
1235                 case DBUS_TYPE_ARRAY:
1236                         if (g_str_equal(key, "AllowedBearers") == TRUE) {
1237                                 allowed_bearers =
1238                                         session_parse_allowed_bearers(&value);
1239                         } else {
1240                                 return -EINVAL;
1241                         }
1242                         break;
1243                 case DBUS_TYPE_BOOLEAN:
1244                         if (g_str_equal(key, "Priority") == TRUE) {
1245                                 dbus_message_iter_get_basic(&value,
1246                                                         &priority);
1247                         } else if (g_str_equal(key, "AvoidHandover") == TRUE) {
1248                                 dbus_message_iter_get_basic(&value,
1249                                                         &avoid_handover);
1250                         } else if (g_str_equal(key, "StayConnected") == TRUE) {
1251                                 dbus_message_iter_get_basic(&value,
1252                                                         &stay_connected);
1253                         } else if (g_str_equal(key, "EmergencyCall") == TRUE) {
1254                                 dbus_message_iter_get_basic(&value,
1255                                                         &ecall);
1256                         } else {
1257                                 return -EINVAL;
1258                         }
1259                         break;
1260                 case DBUS_TYPE_UINT32:
1261                         if (g_str_equal(key, "PeriodicConnect") == TRUE) {
1262                                 dbus_message_iter_get_basic(&value,
1263                                                         &periodic_connect);
1264                         } else if (g_str_equal(key, "IdleTimeout") == TRUE) {
1265                                 dbus_message_iter_get_basic(&value,
1266                                                         &idle_timeout);
1267                         } else {
1268                                 return -EINVAL;
1269                         }
1270                         break;
1271                 case DBUS_TYPE_STRING:
1272                         if (g_str_equal(key, "RoamingPolicy") == TRUE) {
1273                                 dbus_message_iter_get_basic(&value, &val);
1274                                 roaming_policy = string2roamingpolicy(val);
1275                         } else {
1276                                 return -EINVAL;
1277                         }
1278                 }
1279                 dbus_message_iter_next(&array);
1280         }
1281
1282         dbus_message_iter_next(&iter);
1283         dbus_message_iter_get_basic(&iter, &notify_path);
1284
1285         if (notify_path == NULL) {
1286                 err = -EINVAL;
1287                 goto err;
1288         }
1289
1290         session_path = g_strdup_printf("/sessions%s", notify_path);
1291         if (session_path == NULL) {
1292                 err = -ENOMEM;
1293                 goto err;
1294         }
1295
1296         session = g_hash_table_lookup(session_hash, session_path);
1297         if (session != NULL) {
1298                 err = -EEXIST;
1299                 goto err;
1300         }
1301
1302         session = g_try_new0(struct connman_session, 1);
1303         if (session == NULL) {
1304                 err = -ENOMEM;
1305                 goto err;
1306         }
1307
1308         info = &session->info;
1309         info_last = &session->info_last;
1310
1311         session->owner = g_strdup(owner);
1312         session->session_path = session_path;
1313         session->notify_path = g_strdup(notify_path);
1314         session->notify_watch =
1315                 g_dbus_add_disconnect_watch(connection, session->owner,
1316                                         owner_disconnect, session, NULL);
1317
1318         session->service_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1319                                                 NULL, NULL);
1320
1321         info->bearer = "";
1322         info->online = FALSE;
1323         info->priority = priority;
1324         info->avoid_handover = avoid_handover;
1325         info->stay_connected = stay_connected;
1326         info->periodic_connect = periodic_connect;
1327         info->idle_timeout = idle_timeout;
1328         info->ecall = ecall;
1329         info->roaming_policy = roaming_policy;
1330         info->entry = NULL;
1331         info->marker = 0;
1332         info->reason = CONNMAN_SESSION_REASON_UNKNOWN;
1333
1334         if (allowed_bearers == NULL) {
1335                 info->allowed_bearers =
1336                                 session_allowed_bearers_any();
1337
1338                 if (info->allowed_bearers == NULL) {
1339                         err = -ENOMEM;
1340                         goto err;
1341                 }
1342         } else {
1343                 info->allowed_bearers = allowed_bearers;
1344         }
1345
1346         g_hash_table_replace(session_hash, session->session_path, session);
1347
1348         DBG("add %s", session->session_path);
1349
1350         if (g_dbus_register_interface(connection, session->session_path,
1351                                         CONNMAN_SESSION_INTERFACE,
1352                                         session_methods, NULL,
1353                                         NULL, session, NULL) == FALSE) {
1354                 connman_error("Failed to register %s", session->session_path);
1355                 g_hash_table_remove(session_hash, session->session_path);
1356                 session = NULL;
1357
1358                 err = -EINVAL;
1359                 goto err;
1360         }
1361
1362         g_dbus_send_reply(connection, msg,
1363                                 DBUS_TYPE_OBJECT_PATH, &session->session_path,
1364                                 DBUS_TYPE_INVALID);
1365
1366
1367         update_allowed_bearers(session);
1368         update_info(info);
1369         if (info->ecall == TRUE) {
1370                 ecall_session = session;
1371                 update_ecall_sessions(session);
1372         }
1373
1374         info_last->bearer = info->bearer;
1375         info_last->online = info->online;
1376         info_last->priority = info->priority;
1377         info_last->avoid_handover = info->avoid_handover;
1378         info_last->stay_connected = info->stay_connected;
1379         info_last->periodic_connect = info->periodic_connect;
1380         info_last->idle_timeout = info->idle_timeout;
1381         info_last->ecall = info->ecall;
1382         info_last->roaming_policy = info->roaming_policy;
1383         info_last->entry = info->entry;
1384         info_last->marker = info->marker;
1385         info_last->reason = info->reason;
1386         info_last->allowed_bearers = info->allowed_bearers;
1387         update_info(info_last);
1388
1389         session->info_dirty = TRUE;
1390         session->append_all = TRUE;
1391
1392         session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING);
1393
1394         return 0;
1395
1396 err:
1397         connman_error("Failed to create session");
1398         g_free(session_path);
1399
1400         g_slist_foreach(allowed_bearers, cleanup_bearer_info, NULL);
1401         g_slist_free(allowed_bearers);
1402
1403         return err;
1404 }
1405
1406 int __connman_session_destroy(DBusMessage *msg)
1407 {
1408         const char *owner, *session_path;
1409         struct connman_session *session;
1410
1411         owner = dbus_message_get_sender(msg);
1412
1413         DBG("owner %s", owner);
1414
1415         dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &session_path,
1416                                                         DBUS_TYPE_INVALID);
1417         if (session_path == NULL)
1418                 return -EINVAL;
1419
1420         session = g_hash_table_lookup(session_hash, session_path);
1421         if (session == NULL)
1422                 return -EINVAL;
1423
1424         if (g_strcmp0(owner, session->owner) != 0)
1425                 return -EACCES;
1426
1427         session_disconnect(session);
1428
1429         return 0;
1430 }
1431
1432 connman_bool_t __connman_session_mode()
1433 {
1434         return sessionmode;
1435 }
1436
1437 void __connman_session_set_mode(connman_bool_t enable)
1438 {
1439         DBG("enable %d", enable);
1440
1441         if (sessionmode == enable)
1442                 return;
1443
1444         sessionmode = enable;
1445
1446         if (sessionmode == TRUE)
1447                 __connman_service_disconnect_all();
1448 }
1449
1450 static void service_add(struct connman_service *service)
1451 {
1452         GHashTableIter iter;
1453         GSequenceIter *iter_service_list;
1454         gpointer key, value;
1455         struct connman_session *session;
1456
1457         DBG("service %p", service);
1458
1459         g_hash_table_iter_init(&iter, session_hash);
1460
1461         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1462                 session = value;
1463
1464                 if (service_match(session, service) == FALSE)
1465                         continue;
1466
1467                 iter_service_list =
1468                         g_sequence_insert_sorted(session->service_list,
1469                                                         service, sort_services,
1470                                                         session);
1471
1472                 g_hash_table_replace(session->service_hash, service,
1473                                         iter_service_list);
1474
1475                 session_changed(session, CONNMAN_SESSION_TRIGGER_SERVICE);
1476         }
1477 }
1478
1479 static void service_remove(struct connman_service *service)
1480 {
1481
1482         GHashTableIter iter;
1483         gpointer key, value;
1484         struct connman_session *session;
1485         struct session_info *info;
1486
1487         DBG("service %p", service);
1488
1489         g_hash_table_iter_init(&iter, session_hash);
1490
1491         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1492                 GSequenceIter *iter;
1493                 session = value;
1494                 info = &session->info;
1495
1496                 iter = g_hash_table_lookup(session->service_hash, service);
1497                 if (iter == NULL)
1498                         continue;
1499
1500                 g_sequence_remove(iter);
1501
1502                 info->entry = NULL;
1503                 session_changed(session, CONNMAN_SESSION_TRIGGER_SERVICE);
1504         }
1505 }
1506
1507 static void service_state_changed(struct connman_service *service,
1508                                         enum connman_service_state state)
1509 {
1510         GHashTableIter iter;
1511         gpointer key, value;
1512         struct connman_session *session;
1513         struct session_info *info, *info_last;
1514
1515         DBG("service %p state %d", service, state);
1516
1517         g_hash_table_iter_init(&iter, session_hash);
1518
1519         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1520                 session = value;
1521                 info = &session->info;
1522                 info_last = &session->info_last;
1523
1524                 if (info->entry != NULL && info->entry->service == service) {
1525                         info->online = __connman_service_is_connected(service);
1526                         if (info_last->online != info->online)
1527                                 session->info_dirty = TRUE;
1528                 }
1529
1530                 session_changed(session,
1531                                 CONNMAN_SESSION_TRIGGER_SERVICE);
1532         }
1533 }
1534
1535 static void ipconfig_changed(struct connman_service *service,
1536                                 struct connman_ipconfig *ipconfig)
1537 {
1538         GHashTableIter iter;
1539         gpointer key, value;
1540         struct connman_session *session;
1541         struct session_info *info;
1542         enum connman_ipconfig_type type;
1543
1544         DBG("service %p ipconfig %p", service, ipconfig);
1545
1546         type = __connman_ipconfig_get_config_type(ipconfig);
1547
1548         g_hash_table_iter_init(&iter, session_hash);
1549
1550         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1551                 session = value;
1552                 info = &session->info;
1553
1554                 if (info->entry != NULL && info->entry->service == service) {
1555                         if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
1556                                 ipconfig_ipv4_changed(session);
1557                         else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
1558                                 ipconfig_ipv6_changed(session);
1559                 }
1560         }
1561 }
1562
1563 static struct connman_notifier session_notifier = {
1564         .name                   = "session",
1565         .service_add            = service_add,
1566         .service_remove         = service_remove,
1567         .service_state_changed  = service_state_changed,
1568         .ipconfig_changed       = ipconfig_changed,
1569 };
1570
1571 int __connman_session_init(void)
1572 {
1573         int err;
1574
1575         DBG("");
1576
1577         connection = connman_dbus_get_connection();
1578         if (connection == NULL)
1579                 return -1;
1580
1581         err = connman_notifier_register(&session_notifier);
1582         if (err < 0) {
1583                 dbus_connection_unref(connection);
1584                 return err;
1585         }
1586
1587         session_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1588                                                 NULL, cleanup_session);
1589
1590         sessionmode = FALSE;
1591         return 0;
1592 }
1593
1594 void __connman_session_cleanup(void)
1595 {
1596         DBG("");
1597
1598         if (connection == NULL)
1599                 return;
1600
1601         connman_notifier_unregister(&session_notifier);
1602
1603         g_hash_table_foreach(session_hash, release_session, NULL);
1604         g_hash_table_destroy(session_hash);
1605
1606         dbus_connection_unref(connection);
1607 }