5 * Copyright (C) 2007-2010 Intel Corporation. All rights reserved.
6 * Copyright (C) 2011 BWM CarIT GmbH. All rights reserved.
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.
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.
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
33 static DBusConnection *connection;
34 static GHashTable *session_hash;
35 static connman_bool_t sessionmode;
36 static struct session_info *ecall_info;
38 enum connman_session_trigger {
39 CONNMAN_SESSION_TRIGGER_UNKNOWN = 0,
40 CONNMAN_SESSION_TRIGGER_SETTING = 1,
41 CONNMAN_SESSION_TRIGGER_CONNECT = 2,
42 CONNMAN_SESSION_TRIGGER_DISCONNECT = 3,
43 CONNMAN_SESSION_TRIGGER_PERIODIC = 4,
44 CONNMAN_SESSION_TRIGGER_SERVICE = 5,
45 CONNMAN_SESSION_TRIGGER_ECALL = 6,
48 enum connman_session_reason {
49 CONNMAN_SESSION_REASON_UNKNOWN = 0,
50 CONNMAN_SESSION_REASON_CONNECT = 1,
51 CONNMAN_SESSION_REASON_DISCONNECT = 2,
52 CONNMAN_SESSION_REASON_FREE_RIDE = 3,
53 CONNMAN_SESSION_REASON_PERIODIC = 4,
56 enum connman_session_state {
57 CONNMAN_SESSION_STATE_DISCONNECTED = 0,
58 CONNMAN_SESSION_STATE_CONNECTED = 1,
59 CONNMAN_SESSION_STATE_ONLINE = 2,
62 enum connman_session_type {
63 CONNMAN_SESSION_TYPE_ANY = 0,
64 CONNMAN_SESSION_TYPE_LOCAL = 1,
65 CONNMAN_SESSION_TYPE_INTERNET = 2,
68 enum connman_session_roaming_policy {
69 CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN = 0,
70 CONNMAN_SESSION_ROAMING_POLICY_DEFAULT = 1,
71 CONNMAN_SESSION_ROAMING_POLICY_ALWAYS = 2,
72 CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN = 3,
73 CONNMAN_SESSION_ROAMING_POLICY_NATIONAL = 4,
74 CONNMAN_SESSION_ROAMING_POLICY_INTERNATIONAL = 5,
77 struct service_entry {
78 /* track why this service was selected */
79 enum connman_session_reason reason;
80 enum connman_service_state state;
82 struct connman_service *service;
85 GSList *pending_timeouts;
89 enum connman_session_state state;
90 enum connman_session_type type;
91 connman_bool_t priority;
92 GSList *allowed_bearers;
93 connman_bool_t avoid_handover;
94 connman_bool_t stay_connected;
95 unsigned int periodic_connect;
96 unsigned int idle_timeout;
98 enum connman_session_roaming_policy roaming_policy;
101 struct service_entry *entry;
102 enum connman_session_reason reason;
105 struct connman_session {
111 connman_bool_t append_all;
112 struct session_info *info;
113 struct session_info *info_last;
115 GSequence *service_list;
116 GHashTable *service_hash;
121 connman_bool_t match_all;
122 enum connman_service_type service_type;
125 static const char *trigger2string(enum connman_session_trigger trigger)
128 case CONNMAN_SESSION_TRIGGER_UNKNOWN:
130 case CONNMAN_SESSION_TRIGGER_SETTING:
132 case CONNMAN_SESSION_TRIGGER_CONNECT:
134 case CONNMAN_SESSION_TRIGGER_DISCONNECT:
136 case CONNMAN_SESSION_TRIGGER_PERIODIC:
138 case CONNMAN_SESSION_TRIGGER_SERVICE:
140 case CONNMAN_SESSION_TRIGGER_ECALL:
147 static const char *reason2string(enum connman_session_reason reason)
150 case CONNMAN_SESSION_REASON_UNKNOWN:
152 case CONNMAN_SESSION_REASON_CONNECT:
154 case CONNMAN_SESSION_REASON_DISCONNECT:
156 case CONNMAN_SESSION_REASON_FREE_RIDE:
158 case CONNMAN_SESSION_REASON_PERIODIC:
165 static const char *state2string(enum connman_session_state state)
168 case CONNMAN_SESSION_STATE_DISCONNECTED:
169 return "disconnected";
170 case CONNMAN_SESSION_STATE_CONNECTED:
172 case CONNMAN_SESSION_STATE_ONLINE:
179 static const char *type2string(enum connman_session_type type)
182 case CONNMAN_SESSION_TYPE_ANY:
184 case CONNMAN_SESSION_TYPE_LOCAL:
186 case CONNMAN_SESSION_TYPE_INTERNET:
193 static enum connman_session_type string2type(const char *type)
195 if (g_strcmp0(type, "local") == 0)
196 return CONNMAN_SESSION_TYPE_LOCAL;
197 else if (g_strcmp0(type, "internet") == 0)
198 return CONNMAN_SESSION_TYPE_INTERNET;
200 return CONNMAN_SESSION_TYPE_ANY;
203 static const char *roamingpolicy2string(enum connman_session_roaming_policy policy)
206 case CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN:
208 case CONNMAN_SESSION_ROAMING_POLICY_DEFAULT:
210 case CONNMAN_SESSION_ROAMING_POLICY_ALWAYS:
212 case CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN:
214 case CONNMAN_SESSION_ROAMING_POLICY_NATIONAL:
216 case CONNMAN_SESSION_ROAMING_POLICY_INTERNATIONAL:
217 return "international";
223 static enum connman_session_roaming_policy string2roamingpolicy(const char *policy)
225 if (g_strcmp0(policy, "default") == 0)
226 return CONNMAN_SESSION_ROAMING_POLICY_DEFAULT;
227 else if (g_strcmp0(policy, "always") == 0)
228 return CONNMAN_SESSION_ROAMING_POLICY_ALWAYS;
229 else if (g_strcmp0(policy, "forbidden") == 0)
230 return CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN;
231 else if (g_strcmp0(policy, "national") == 0)
232 return CONNMAN_SESSION_ROAMING_POLICY_NATIONAL;
233 else if (g_strcmp0(policy, "international") == 0)
234 return CONNMAN_SESSION_ROAMING_POLICY_INTERNATIONAL;
236 return CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN;
239 static enum connman_service_type bearer2service(const char *bearer)
242 return CONNMAN_SERVICE_TYPE_UNKNOWN;
244 if (g_strcmp0(bearer, "ethernet") == 0)
245 return CONNMAN_SERVICE_TYPE_ETHERNET;
246 else if (g_strcmp0(bearer, "wifi") == 0)
247 return CONNMAN_SERVICE_TYPE_WIFI;
248 else if (g_strcmp0(bearer, "wimax") == 0)
249 return CONNMAN_SERVICE_TYPE_WIMAX;
250 else if (g_strcmp0(bearer, "bluetooth") == 0)
251 return CONNMAN_SERVICE_TYPE_BLUETOOTH;
252 else if (g_strcmp0(bearer, "cellular") == 0)
253 return CONNMAN_SERVICE_TYPE_CELLULAR;
254 else if (g_strcmp0(bearer, "vpn") == 0)
255 return CONNMAN_SERVICE_TYPE_VPN;
257 return CONNMAN_SERVICE_TYPE_UNKNOWN;
260 static char *service2bearer(enum connman_service_type type)
263 case CONNMAN_SERVICE_TYPE_ETHERNET:
265 case CONNMAN_SERVICE_TYPE_WIFI:
267 case CONNMAN_SERVICE_TYPE_WIMAX:
269 case CONNMAN_SERVICE_TYPE_BLUETOOTH:
271 case CONNMAN_SERVICE_TYPE_CELLULAR:
273 case CONNMAN_SERVICE_TYPE_VPN:
275 case CONNMAN_SERVICE_TYPE_UNKNOWN:
276 case CONNMAN_SERVICE_TYPE_SYSTEM:
277 case CONNMAN_SERVICE_TYPE_GPS:
278 case CONNMAN_SERVICE_TYPE_GADGET:
285 static void cleanup_bearer_info(gpointer data, gpointer user_data)
287 struct bearer_info *info = data;
293 static GSList *session_parse_allowed_bearers(DBusMessageIter *iter)
295 struct bearer_info *info;
296 DBusMessageIter array;
299 dbus_message_iter_recurse(iter, &array);
301 while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) {
304 dbus_message_iter_get_basic(&array, &bearer);
306 info = g_try_new0(struct bearer_info, 1);
308 g_slist_foreach(list, cleanup_bearer_info, NULL);
314 info->name = g_strdup(bearer);
315 info->service_type = bearer2service(info->name);
317 if (info->service_type == CONNMAN_SERVICE_TYPE_UNKNOWN &&
318 g_strcmp0(info->name, "*") == 0) {
319 info->match_all = TRUE;
321 info->match_all = FALSE;
324 list = g_slist_append(list, info);
326 dbus_message_iter_next(&array);
332 static GSList *session_allowed_bearers_any(void)
334 struct bearer_info *info;
337 info = g_try_new0(struct bearer_info, 1);
344 info->name = g_strdup("");
345 info->match_all = TRUE;
346 info->service_type = CONNMAN_SERVICE_TYPE_UNKNOWN;
348 list = g_slist_append(list, info);
353 static void append_allowed_bearers(DBusMessageIter *iter, void *user_data)
355 struct session_info *info = user_data;
358 for (list = info->allowed_bearers;
359 list != NULL; list = list->next) {
360 struct bearer_info *info = list->data;
362 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
367 static void append_ipconfig_ipv4(DBusMessageIter *iter, void *user_data)
369 struct connman_service *service = user_data;
370 struct connman_ipconfig *ipconfig_ipv4;
375 if (__connman_service_is_connected_state(service,
376 CONNMAN_IPCONFIG_TYPE_IPV4) == FALSE) {
380 ipconfig_ipv4 = __connman_service_get_ip4config(service);
381 if (ipconfig_ipv4 == NULL)
384 __connman_ipconfig_append_ipv4(ipconfig_ipv4, iter);
387 static void append_ipconfig_ipv6(DBusMessageIter *iter, void *user_data)
389 struct connman_service *service = user_data;
390 struct connman_ipconfig *ipconfig_ipv4, *ipconfig_ipv6;
395 if (__connman_service_is_connected_state(service,
396 CONNMAN_IPCONFIG_TYPE_IPV6) == FALSE) {
400 ipconfig_ipv4 = __connman_service_get_ip4config(service);
401 ipconfig_ipv6 = __connman_service_get_ip6config(service);
402 if (ipconfig_ipv6 == NULL)
405 __connman_ipconfig_append_ipv6(ipconfig_ipv6, iter, ipconfig_ipv4);
408 static void append_notify(DBusMessageIter *dict,
409 struct connman_session *session)
411 struct session_info *info = session->info;
412 struct session_info *info_last = session->info_last;
414 struct connman_service *service;
415 const char *name, *ifname, *bearer;
417 if (session->append_all == TRUE ||
418 info->state != info_last->state) {
419 const char *state = state2string(info->state);
421 connman_dbus_dict_append_basic(dict, "State",
424 info_last->state = info->state;
427 if (session->append_all == TRUE ||
428 info->entry != info_last->entry) {
429 if (info->entry == NULL) {
435 name = info->entry->name;
436 ifname = info->entry->ifname;
437 service = info->entry->service;
438 bearer = info->entry->bearer;
441 connman_dbus_dict_append_basic(dict, "Name",
445 connman_dbus_dict_append_dict(dict, "IPv4",
446 append_ipconfig_ipv4,
449 connman_dbus_dict_append_dict(dict, "IPv6",
450 append_ipconfig_ipv6,
453 connman_dbus_dict_append_basic(dict, "Interface",
457 connman_dbus_dict_append_basic(dict, "Bearer",
461 info_last->entry = info->entry;
464 if (session->append_all == TRUE || info->type != info_last->type) {
465 const char *type = type2string(info->type);
467 connman_dbus_dict_append_basic(dict, "ConnectionType",
470 info_last->type = info->type;
473 if (session->append_all == TRUE ||
474 info->priority != info_last->priority) {
475 connman_dbus_dict_append_basic(dict, "Priority",
478 info_last->priority = info->priority;
481 if (session->append_all == TRUE ||
482 info->allowed_bearers != info_last->allowed_bearers) {
483 connman_dbus_dict_append_array(dict, "AllowedBearers",
485 append_allowed_bearers,
487 info_last->allowed_bearers = info->allowed_bearers;
490 if (session->append_all == TRUE ||
491 info->avoid_handover != info_last->avoid_handover) {
492 connman_dbus_dict_append_basic(dict, "AvoidHandover",
494 &info->avoid_handover);
495 info_last->avoid_handover = info->avoid_handover;
498 if (session->append_all == TRUE ||
499 info->stay_connected != info_last->stay_connected) {
500 connman_dbus_dict_append_basic(dict, "StayConnected",
502 &info->stay_connected);
503 info_last->stay_connected = info->stay_connected;
506 if (session->append_all == TRUE ||
507 info->periodic_connect != info_last->periodic_connect) {
508 connman_dbus_dict_append_basic(dict, "PeriodicConnect",
510 &info->periodic_connect);
511 info_last->periodic_connect = info->periodic_connect;
514 if (session->append_all == TRUE ||
515 info->idle_timeout != info_last->idle_timeout) {
516 connman_dbus_dict_append_basic(dict, "IdleTimeout",
518 &info->idle_timeout);
519 info_last->idle_timeout = info->idle_timeout;
522 if (session->append_all == TRUE ||
523 info->ecall != info_last->ecall) {
524 connman_dbus_dict_append_basic(dict, "EmergencyCall",
527 info_last->ecall = info->ecall;
530 if (session->append_all == TRUE ||
531 info->roaming_policy != info_last->roaming_policy) {
532 policy = roamingpolicy2string(info->roaming_policy);
533 connman_dbus_dict_append_basic(dict, "RoamingPolicy",
536 info_last->roaming_policy = info->roaming_policy;
539 if (session->append_all == TRUE ||
540 info->marker != info_last->marker) {
541 connman_dbus_dict_append_basic(dict, "SessionMarker",
544 info_last->marker = info->marker;
547 session->append_all = FALSE;
550 static connman_bool_t compute_notifiable_changes(struct connman_session *session)
552 struct session_info *info_last = session->info_last;
553 struct session_info *info = session->info;
555 if (session->append_all == TRUE)
558 if (info->state != info_last->state)
561 if (info->entry != info_last->entry &&
562 info->state >= CONNMAN_SESSION_STATE_CONNECTED)
565 if (info->periodic_connect != info_last->periodic_connect ||
566 info->allowed_bearers != info_last->allowed_bearers ||
567 info->avoid_handover != info_last->avoid_handover ||
568 info->stay_connected != info_last->stay_connected ||
569 info->roaming_policy != info_last->roaming_policy ||
570 info->idle_timeout != info_last->idle_timeout ||
571 info->priority != info_last->priority ||
572 info->marker != info_last->marker ||
573 info->ecall != info_last->ecall ||
574 info->type != info_last->type)
580 static gboolean session_notify(gpointer user_data)
582 struct connman_session *session = user_data;
584 DBusMessageIter array, dict;
586 if (compute_notifiable_changes(session) == FALSE)
589 DBG("session %p owner %s notify_path %s", session,
590 session->owner, session->notify_path);
592 msg = dbus_message_new_method_call(session->owner, session->notify_path,
593 CONNMAN_NOTIFICATION_INTERFACE,
598 dbus_message_iter_init_append(msg, &array);
599 connman_dbus_dict_open(&array, &dict);
601 append_notify(&dict, session);
603 connman_dbus_dict_close(&array, &dict);
605 g_dbus_send_message(connection, msg);
610 static void ipconfig_ipv4_changed(struct connman_session *session)
612 struct session_info *info = session->info;
614 connman_dbus_setting_changed_dict(session->owner, session->notify_path,
615 "IPv4", append_ipconfig_ipv4,
616 info->entry->service);
619 static void ipconfig_ipv6_changed(struct connman_session *session)
621 struct session_info *info = session->info;
623 connman_dbus_setting_changed_dict(session->owner, session->notify_path,
624 "IPv6", append_ipconfig_ipv6,
625 info->entry->service);
628 static connman_bool_t service_type_match(struct connman_session *session,
629 struct connman_service *service)
631 struct session_info *info = session->info;
634 for (list = info->allowed_bearers;
635 list != NULL; list = list->next) {
636 struct bearer_info *info = list->data;
637 enum connman_service_type service_type;
639 if (info->match_all == TRUE)
642 service_type = connman_service_get_type(service);
643 if (info->service_type == service_type)
650 static connman_bool_t service_match(struct connman_session *session,
651 struct connman_service *service)
653 if (service_type_match(session, service) == FALSE)
659 static int service_type_weight(enum connman_service_type type)
662 * The session doesn't care which service
663 * to use. Nevertheless we have to sort them
664 * according their type. The ordering is
673 case CONNMAN_SERVICE_TYPE_ETHERNET:
675 case CONNMAN_SERVICE_TYPE_BLUETOOTH:
677 case CONNMAN_SERVICE_TYPE_WIFI:
678 case CONNMAN_SERVICE_TYPE_WIMAX:
680 case CONNMAN_SERVICE_TYPE_CELLULAR:
682 case CONNMAN_SERVICE_TYPE_UNKNOWN:
683 case CONNMAN_SERVICE_TYPE_SYSTEM:
684 case CONNMAN_SERVICE_TYPE_GPS:
685 case CONNMAN_SERVICE_TYPE_VPN:
686 case CONNMAN_SERVICE_TYPE_GADGET:
693 static gint sort_allowed_bearers(struct connman_service *service_a,
694 struct connman_service *service_b,
695 struct connman_session *session)
697 struct session_info *info = session->info;
699 enum connman_service_type type_a, type_b;
700 int weight_a, weight_b;
702 type_a = connman_service_get_type(service_a);
703 type_b = connman_service_get_type(service_b);
705 for (list = info->allowed_bearers;
706 list != NULL; list = list->next) {
707 struct bearer_info *info = list->data;
709 if (info->match_all == TRUE) {
710 if (type_a != type_b) {
711 weight_a = service_type_weight(type_a);
712 weight_b = service_type_weight(type_b);
714 if (weight_a > weight_b)
717 if (weight_a < weight_b)
724 if (type_a == info->service_type &&
725 type_b == info->service_type) {
729 if (type_a == info->service_type &&
730 type_b != info->service_type) {
734 if (type_a != info->service_type &&
735 type_b == info->service_type) {
743 static gint sort_services(gconstpointer a, gconstpointer b, gpointer user_data)
745 struct service_entry *entry_a = (void *)a;
746 struct service_entry *entry_b = (void *)b;
747 struct connman_session *session = user_data;
749 return sort_allowed_bearers(entry_a->service, entry_b->service,
753 static void cleanup_session(gpointer user_data)
755 struct connman_session *session = user_data;
756 struct session_info *info = session->info;
758 DBG("remove %s", session->session_path);
760 g_hash_table_destroy(session->service_hash);
761 g_sequence_free(session->service_list);
763 if (info->entry != NULL &&
764 info->entry->reason == CONNMAN_SESSION_REASON_CONNECT) {
765 __connman_service_disconnect(info->entry->service);
768 g_slist_foreach(info->allowed_bearers, cleanup_bearer_info, NULL);
769 g_slist_free(info->allowed_bearers);
771 g_free(session->owner);
772 g_free(session->session_path);
773 g_free(session->notify_path);
774 g_free(session->info);
775 g_free(session->info_last);
780 static enum connman_session_state service_to_session_state(enum connman_service_state state)
783 case CONNMAN_SERVICE_STATE_UNKNOWN:
784 case CONNMAN_SERVICE_STATE_IDLE:
785 case CONNMAN_SERVICE_STATE_ASSOCIATION:
786 case CONNMAN_SERVICE_STATE_CONFIGURATION:
787 case CONNMAN_SERVICE_STATE_DISCONNECT:
788 case CONNMAN_SERVICE_STATE_FAILURE:
790 case CONNMAN_SERVICE_STATE_READY:
791 return CONNMAN_SESSION_STATE_CONNECTED;
792 case CONNMAN_SERVICE_STATE_ONLINE:
793 return CONNMAN_SESSION_STATE_ONLINE;
796 return CONNMAN_SESSION_STATE_DISCONNECTED;
799 static connman_bool_t is_connected(enum connman_service_state state)
802 case CONNMAN_SERVICE_STATE_UNKNOWN:
803 case CONNMAN_SERVICE_STATE_IDLE:
804 case CONNMAN_SERVICE_STATE_ASSOCIATION:
805 case CONNMAN_SERVICE_STATE_CONFIGURATION:
806 case CONNMAN_SERVICE_STATE_DISCONNECT:
807 case CONNMAN_SERVICE_STATE_FAILURE:
809 case CONNMAN_SERVICE_STATE_READY:
810 case CONNMAN_SERVICE_STATE_ONLINE:
817 static connman_bool_t is_connecting(enum connman_service_state state)
820 case CONNMAN_SERVICE_STATE_UNKNOWN:
821 case CONNMAN_SERVICE_STATE_IDLE:
823 case CONNMAN_SERVICE_STATE_ASSOCIATION:
824 case CONNMAN_SERVICE_STATE_CONFIGURATION:
826 case CONNMAN_SERVICE_STATE_DISCONNECT:
827 case CONNMAN_SERVICE_STATE_FAILURE:
828 case CONNMAN_SERVICE_STATE_READY:
829 case CONNMAN_SERVICE_STATE_ONLINE:
836 static connman_bool_t explicit_connect(enum connman_session_reason reason)
839 case CONNMAN_SESSION_REASON_UNKNOWN:
840 case CONNMAN_SESSION_REASON_FREE_RIDE:
841 case CONNMAN_SESSION_REASON_DISCONNECT:
843 case CONNMAN_SESSION_REASON_CONNECT:
844 case CONNMAN_SESSION_REASON_PERIODIC:
851 static connman_bool_t explicit_disconnect(struct session_info *info)
853 if (info->entry == NULL)
856 DBG("reason %s service %p state %d",
857 reason2string(info->entry->reason),
858 info->entry->service, info->entry->state);
860 if (info->entry->reason == CONNMAN_SESSION_REASON_UNKNOWN)
863 if (explicit_connect(info->entry->reason) == FALSE)
866 if (__connman_service_session_dec(info->entry->service) == FALSE)
869 if (ecall_info != NULL && ecall_info != info)
875 struct pending_data {
876 unsigned int timeout;
877 struct service_entry *entry;
878 gboolean (*cb)(gpointer);
881 static void pending_timeout_free(gpointer data, gpointer user_data)
883 struct pending_data *pending = data;
885 DBG("pending %p timeout %d", pending, pending->timeout);
886 g_source_remove(pending->timeout);
890 static void pending_timeout_remove_all(struct service_entry *entry)
894 g_slist_foreach(entry->pending_timeouts, pending_timeout_free, NULL);
895 g_slist_free(entry->pending_timeouts);
896 entry->pending_timeouts = NULL;
899 static gboolean pending_timeout_cb(gpointer data)
901 struct pending_data *pending = data;
902 struct service_entry *entry = pending->entry;
905 DBG("pending %p timeout %d", pending, pending->timeout);
907 ret = pending->cb(pending->entry);
909 entry->pending_timeouts =
910 g_slist_remove(entry->pending_timeouts,
917 static connman_bool_t pending_timeout_add(unsigned int seconds,
918 gboolean (*cb)(gpointer),
919 struct service_entry *entry)
921 struct pending_data *pending = g_try_new0(struct pending_data, 1);
923 if (pending == NULL || cb == NULL || entry == NULL) {
929 pending->entry = entry;
930 pending->timeout = g_timeout_add_seconds(seconds, pending_timeout_cb,
932 entry->pending_timeouts = g_slist_prepend(entry->pending_timeouts,
935 DBG("pending %p entry %p timeout id %d", pending, entry,
941 static gboolean call_disconnect(gpointer user_data)
943 struct service_entry *entry = user_data;
944 struct connman_service *service = entry->service;
947 * TODO: We should mark this entry as pending work. In case
948 * disconnect fails we just unassign this session from the
949 * service and can't do anything later on it
951 DBG("disconnect service %p", service);
952 __connman_service_disconnect(service);
957 static gboolean call_connect(gpointer user_data)
959 struct service_entry *entry = user_data;
960 struct connman_service *service = entry->service;
962 DBG("connect service %p", service);
963 __connman_service_connect(service);
968 static void deselect_service(struct session_info *info)
970 struct service_entry *entry;
971 connman_bool_t disconnect, connected;
975 if (info->entry == NULL)
978 disconnect = explicit_disconnect(info);
980 connected = is_connecting(info->entry->state) == TRUE ||
981 is_connected(info->entry->state) == TRUE;
983 info->state = CONNMAN_SESSION_STATE_DISCONNECTED;
984 info->entry->reason = CONNMAN_SESSION_REASON_UNKNOWN;
989 DBG("disconnect %d connected %d", disconnect, connected);
991 if (disconnect == TRUE && connected == TRUE)
992 pending_timeout_add(0, call_disconnect, entry);
995 static void deselect_and_disconnect(struct connman_session *session,
996 enum connman_session_reason reason)
998 struct session_info *info = session->info;
1000 deselect_service(info);
1002 info->reason = reason;
1005 static void select_connected_service(struct session_info *info,
1006 struct service_entry *entry)
1008 info->state = service_to_session_state(entry->state);
1010 info->entry = entry;
1011 info->entry->reason = info->reason;
1013 if (explicit_connect(info->reason) == FALSE)
1016 __connman_service_session_inc(info->entry->service);
1019 static void select_offline_service(struct session_info *info,
1020 struct service_entry *entry)
1022 if (explicit_connect(info->reason) == FALSE)
1025 info->state = service_to_session_state(entry->state);
1027 info->entry = entry;
1028 info->entry->reason = info->reason;
1030 __connman_service_session_inc(info->entry->service);
1031 pending_timeout_add(0, call_connect, entry);
1034 static void select_service(struct session_info *info,
1035 struct service_entry *entry)
1037 DBG("service %p", entry->service);
1039 if (is_connected(entry->state) == TRUE)
1040 select_connected_service(info, entry);
1042 select_offline_service(info, entry);
1045 static void select_and_connect(struct connman_session *session,
1046 enum connman_session_reason reason)
1048 struct session_info *info = session->info;
1049 struct service_entry *entry = NULL;
1050 GSequenceIter *iter;
1052 DBG("session %p reason %s", session, reason2string(reason));
1054 info->reason = reason;
1056 iter = g_sequence_get_begin_iter(session->service_list);
1058 while (g_sequence_iter_is_end(iter) == FALSE) {
1059 entry = g_sequence_get(iter);
1061 switch (entry->state) {
1062 case CONNMAN_SERVICE_STATE_ASSOCIATION:
1063 case CONNMAN_SERVICE_STATE_CONFIGURATION:
1064 case CONNMAN_SERVICE_STATE_READY:
1065 case CONNMAN_SERVICE_STATE_ONLINE:
1066 case CONNMAN_SERVICE_STATE_IDLE:
1067 case CONNMAN_SERVICE_STATE_DISCONNECT:
1068 select_service(info, entry);
1070 case CONNMAN_SERVICE_STATE_UNKNOWN:
1071 case CONNMAN_SERVICE_STATE_FAILURE:
1075 iter = g_sequence_iter_next(iter);
1079 static struct service_entry *create_service_entry(struct connman_service *service,
1081 enum connman_service_state state)
1083 struct service_entry *entry;
1084 enum connman_service_type type;
1087 entry = g_try_new0(struct service_entry, 1);
1091 entry->reason = CONNMAN_SESSION_REASON_UNKNOWN;
1092 entry->state = state;
1097 entry->service = service;
1099 idx = __connman_service_get_index(entry->service);
1100 entry->ifname = connman_inet_ifname(idx);
1101 if (entry->ifname == NULL)
1102 entry->ifname = g_strdup("");
1104 type = connman_service_get_type(entry->service);
1105 entry->bearer = service2bearer(type);
1110 static void destroy_service_entry(gpointer data)
1112 struct service_entry *entry = data;
1114 pending_timeout_remove_all(entry);
1115 g_free(entry->ifname);
1120 static void populate_service_list(struct connman_session *session)
1122 struct service_entry *entry;
1123 GSequenceIter *iter;
1125 session->service_hash =
1126 g_hash_table_new_full(g_direct_hash, g_direct_equal,
1128 session->service_list = __connman_service_get_list(session,
1130 create_service_entry,
1131 destroy_service_entry);
1133 g_sequence_sort(session->service_list, sort_services, session);
1135 iter = g_sequence_get_begin_iter(session->service_list);
1137 while (g_sequence_iter_is_end(iter) == FALSE) {
1138 entry = g_sequence_get(iter);
1140 DBG("service %p type %s name %s", entry->service,
1141 service2bearer(connman_service_get_type(entry->service)),
1144 g_hash_table_replace(session->service_hash,
1145 entry->service, iter);
1147 iter = g_sequence_iter_next(iter);
1151 static void session_changed(struct connman_session *session,
1152 enum connman_session_trigger trigger)
1154 struct session_info *info = session->info;
1155 struct session_info *info_last = session->info_last;
1156 GSequenceIter *service_iter = NULL, *service_iter_last = NULL;
1157 GSequence *service_list_last;
1158 GHashTable *service_hash_last;
1161 * TODO: This only a placeholder for the 'real' algorithm to
1162 * play a bit around. So we are going to improve it step by step.
1165 DBG("session %p trigger %s reason %s", session, trigger2string(trigger),
1166 reason2string(info->reason));
1168 if (info->entry != NULL)
1169 info->state = service_to_session_state(info->entry->state);
1172 case CONNMAN_SESSION_TRIGGER_UNKNOWN:
1173 DBG("ignore session changed event");
1175 case CONNMAN_SESSION_TRIGGER_SETTING:
1176 if (info->allowed_bearers != info_last->allowed_bearers) {
1178 service_hash_last = session->service_hash;
1179 service_list_last = session->service_list;
1181 populate_service_list(session);
1183 if (info->entry != NULL) {
1184 service_iter_last = g_hash_table_lookup(
1186 info->entry->service);
1187 service_iter = g_hash_table_lookup(
1188 session->service_hash,
1189 info->entry->service);
1192 if (service_iter == NULL && service_iter_last != NULL) {
1194 * The currently selected service is
1195 * not part of this session anymore.
1197 deselect_and_disconnect(session, info->reason);
1200 g_hash_table_remove_all(service_hash_last);
1201 g_sequence_free(service_list_last);
1204 if (info->state == CONNMAN_SESSION_STATE_DISCONNECTED) {
1205 select_and_connect(session,
1206 CONNMAN_SESSION_REASON_FREE_RIDE);
1210 case CONNMAN_SESSION_TRIGGER_CONNECT:
1211 if (info->state >= CONNMAN_SESSION_STATE_CONNECTED) {
1212 if (info->entry->reason == CONNMAN_SESSION_REASON_CONNECT)
1214 info->entry->reason = CONNMAN_SESSION_REASON_CONNECT;
1215 __connman_service_session_inc(info->entry->service);
1219 if (info->entry != NULL &&
1220 is_connecting(info->entry->state) == TRUE) {
1224 select_and_connect(session,
1225 CONNMAN_SESSION_REASON_CONNECT);
1228 case CONNMAN_SESSION_TRIGGER_DISCONNECT:
1229 deselect_and_disconnect(session,
1230 CONNMAN_SESSION_REASON_DISCONNECT);
1233 case CONNMAN_SESSION_TRIGGER_PERIODIC:
1234 if (info->state >= CONNMAN_SESSION_STATE_CONNECTED) {
1235 info->entry->reason = CONNMAN_SESSION_REASON_PERIODIC;
1236 __connman_service_session_inc(info->entry->service);
1240 select_and_connect(session,
1241 CONNMAN_SESSION_REASON_PERIODIC);
1244 case CONNMAN_SESSION_TRIGGER_SERVICE:
1245 if (info->entry != NULL &&
1246 (is_connecting(info->entry->state) == TRUE ||
1247 is_connected(info->entry->state) == TRUE)) {
1251 deselect_and_disconnect(session, info->reason);
1253 if (info->reason == CONNMAN_SESSION_REASON_FREE_RIDE ||
1254 info->stay_connected == TRUE) {
1255 select_and_connect(session, info->reason);
1259 case CONNMAN_SESSION_TRIGGER_ECALL:
1260 if (info->state == CONNMAN_SESSION_STATE_DISCONNECTED &&
1261 info->entry != NULL &&
1262 info->entry->service != NULL) {
1263 deselect_and_disconnect(session, info->reason);
1269 session_notify(session);
1272 static DBusMessage *connect_session(DBusConnection *conn,
1273 DBusMessage *msg, void *user_data)
1275 struct connman_session *session = user_data;
1276 struct session_info *info = session->info;
1278 DBG("session %p", session);
1280 if (ecall_info != NULL && ecall_info != info)
1281 return __connman_error_failed(msg, EBUSY);
1283 session_changed(session, CONNMAN_SESSION_TRIGGER_CONNECT);
1285 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1288 static DBusMessage *disconnect_session(DBusConnection *conn,
1289 DBusMessage *msg, void *user_data)
1291 struct connman_session *session = user_data;
1292 struct session_info *info = session->info;
1294 DBG("session %p", session);
1296 if (ecall_info != NULL && ecall_info != info)
1297 return __connman_error_failed(msg, EBUSY);
1299 session_changed(session, CONNMAN_SESSION_TRIGGER_DISCONNECT);
1301 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1304 static void update_ecall_sessions(struct connman_session *session)
1306 struct session_info *info = session->info;
1307 struct connman_session *session_iter;
1308 GHashTableIter iter;
1309 gpointer key, value;
1311 g_hash_table_iter_init(&iter, session_hash);
1313 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1314 session_iter = value;
1316 if (session_iter == session)
1319 session_iter->info->ecall = info->ecall;
1321 session_changed(session_iter, CONNMAN_SESSION_TRIGGER_ECALL);
1325 static void update_ecall(struct connman_session *session)
1327 struct session_info *info = session->info;
1328 struct session_info *info_last = session->info_last;
1330 DBG("session %p ecall_info %p ecall %d -> %d", session,
1331 ecall_info, info_last->ecall, info->ecall);
1333 if (ecall_info == NULL) {
1334 if (!(info_last->ecall == FALSE && info->ecall == TRUE))
1338 } else if (ecall_info == info) {
1339 if (!(info_last->ecall == TRUE && info->ecall == FALSE))
1347 update_ecall_sessions(session);
1352 /* not a valid transition */
1353 info->ecall = info_last->ecall;
1356 static DBusMessage *change_session(DBusConnection *conn,
1357 DBusMessage *msg, void *user_data)
1359 struct connman_session *session = user_data;
1360 struct session_info *info = session->info;
1361 DBusMessageIter iter, value;
1364 GSList *allowed_bearers;
1366 DBG("session %p", session);
1367 if (dbus_message_iter_init(msg, &iter) == FALSE)
1368 return __connman_error_invalid_arguments(msg);
1370 dbus_message_iter_get_basic(&iter, &name);
1371 dbus_message_iter_next(&iter);
1372 dbus_message_iter_recurse(&iter, &value);
1374 switch (dbus_message_iter_get_arg_type(&value)) {
1375 case DBUS_TYPE_ARRAY:
1376 if (g_str_equal(name, "AllowedBearers") == TRUE) {
1377 allowed_bearers = session_parse_allowed_bearers(&value);
1379 g_slist_foreach(info->allowed_bearers,
1380 cleanup_bearer_info, NULL);
1381 g_slist_free(info->allowed_bearers);
1383 if (allowed_bearers == NULL) {
1384 allowed_bearers = session_allowed_bearers_any();
1386 if (allowed_bearers == NULL)
1387 return __connman_error_failed(msg, ENOMEM);
1390 info->allowed_bearers = allowed_bearers;
1395 case DBUS_TYPE_BOOLEAN:
1396 if (g_str_equal(name, "Priority") == TRUE) {
1397 dbus_message_iter_get_basic(&value,
1399 } else if (g_str_equal(name, "AvoidHandover") == TRUE) {
1400 dbus_message_iter_get_basic(&value,
1401 &info->avoid_handover);
1402 } else if (g_str_equal(name, "StayConnected") == TRUE) {
1403 dbus_message_iter_get_basic(&value,
1404 &info->stay_connected);
1405 } else if (g_str_equal(name, "EmergencyCall") == TRUE) {
1406 dbus_message_iter_get_basic(&value,
1409 update_ecall(session);
1414 case DBUS_TYPE_UINT32:
1415 if (g_str_equal(name, "PeriodicConnect") == TRUE) {
1416 dbus_message_iter_get_basic(&value,
1417 &info->periodic_connect);
1418 } else if (g_str_equal(name, "IdleTimeout") == TRUE) {
1419 dbus_message_iter_get_basic(&value,
1420 &info->idle_timeout);
1425 case DBUS_TYPE_STRING:
1426 if (g_str_equal(name, "ConnectionType") == TRUE) {
1427 dbus_message_iter_get_basic(&value, &val);
1428 info->type = string2type(val);
1429 } else if (g_str_equal(name, "RoamingPolicy") == TRUE) {
1430 dbus_message_iter_get_basic(&value, &val);
1431 info->roaming_policy =
1432 string2roamingpolicy(val);
1441 session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING);
1443 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1446 return __connman_error_invalid_arguments(msg);
1449 static void release_session(gpointer key, gpointer value, gpointer user_data)
1451 struct connman_session *session = value;
1452 DBusMessage *message;
1454 DBG("owner %s path %s", session->owner, session->notify_path);
1456 if (session->notify_watch > 0)
1457 g_dbus_remove_watch(connection, session->notify_watch);
1459 g_dbus_unregister_interface(connection, session->session_path,
1460 CONNMAN_SESSION_INTERFACE);
1462 message = dbus_message_new_method_call(session->owner,
1463 session->notify_path,
1464 CONNMAN_NOTIFICATION_INTERFACE,
1466 if (message == NULL)
1469 dbus_message_set_no_reply(message, TRUE);
1471 g_dbus_send_message(connection, message);
1474 static int session_disconnect(struct connman_session *session)
1476 DBG("session %p, %s", session, session->owner);
1478 if (session->notify_watch > 0)
1479 g_dbus_remove_watch(connection, session->notify_watch);
1481 g_dbus_unregister_interface(connection, session->session_path,
1482 CONNMAN_SESSION_INTERFACE);
1484 deselect_and_disconnect(session,
1485 CONNMAN_SESSION_REASON_DISCONNECT);
1487 g_hash_table_remove(session_hash, session->session_path);
1492 static void owner_disconnect(DBusConnection *conn, void *user_data)
1494 struct connman_session *session = user_data;
1496 DBG("session %p, %s died", session, session->owner);
1498 session_disconnect(session);
1501 static DBusMessage *destroy_session(DBusConnection *conn,
1502 DBusMessage *msg, void *user_data)
1504 struct connman_session *session = user_data;
1505 struct session_info *info = session->info;
1507 DBG("session %p", session);
1509 if (ecall_info != NULL && ecall_info != info)
1510 return __connman_error_failed(msg, EBUSY);
1512 session_disconnect(session);
1514 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1517 static GDBusMethodTable session_methods[] = {
1518 { "Destroy", "", "", destroy_session },
1519 { "Connect", "", "", connect_session },
1520 { "Disconnect", "", "", disconnect_session },
1521 { "Change", "sv", "", change_session },
1525 int __connman_session_create(DBusMessage *msg)
1527 const char *owner, *notify_path;
1528 char *session_path = NULL;
1529 DBusMessageIter iter, array;
1530 struct connman_session *session = NULL;
1531 struct session_info *info, *info_last;
1533 enum connman_session_type type = CONNMAN_SESSION_TYPE_ANY;
1534 connman_bool_t priority = FALSE, avoid_handover = FALSE;
1535 connman_bool_t stay_connected = FALSE, ecall = FALSE;
1536 enum connman_session_roaming_policy roaming_policy =
1537 CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN;
1538 GSList *allowed_bearers = NULL;
1539 unsigned int periodic_connect = 0;
1540 unsigned int idle_timeout = 0;
1544 owner = dbus_message_get_sender(msg);
1546 DBG("owner %s", owner);
1548 if (ecall_info != NULL) {
1550 * If there is an emergency call already going on,
1551 * ignore session creation attempt
1557 dbus_message_iter_init(msg, &iter);
1558 dbus_message_iter_recurse(&iter, &array);
1560 while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
1561 DBusMessageIter entry, value;
1562 const char *key, *val;
1564 dbus_message_iter_recurse(&array, &entry);
1565 dbus_message_iter_get_basic(&entry, &key);
1567 dbus_message_iter_next(&entry);
1568 dbus_message_iter_recurse(&entry, &value);
1570 switch (dbus_message_iter_get_arg_type(&value)) {
1571 case DBUS_TYPE_ARRAY:
1572 if (g_str_equal(key, "AllowedBearers") == TRUE) {
1574 session_parse_allowed_bearers(&value);
1579 case DBUS_TYPE_BOOLEAN:
1580 if (g_str_equal(key, "Priority") == TRUE) {
1581 dbus_message_iter_get_basic(&value,
1583 } else if (g_str_equal(key, "AvoidHandover") == TRUE) {
1584 dbus_message_iter_get_basic(&value,
1586 } else if (g_str_equal(key, "StayConnected") == TRUE) {
1587 dbus_message_iter_get_basic(&value,
1589 } else if (g_str_equal(key, "EmergencyCall") == TRUE) {
1590 dbus_message_iter_get_basic(&value,
1596 case DBUS_TYPE_UINT32:
1597 if (g_str_equal(key, "PeriodicConnect") == TRUE) {
1598 dbus_message_iter_get_basic(&value,
1600 } else if (g_str_equal(key, "IdleTimeout") == TRUE) {
1601 dbus_message_iter_get_basic(&value,
1607 case DBUS_TYPE_STRING:
1608 if (g_str_equal(key, "ConnectionType") == TRUE) {
1609 dbus_message_iter_get_basic(&value, &val);
1610 type = string2type(val);
1611 } else if (g_str_equal(key, "RoamingPolicy") == TRUE) {
1612 dbus_message_iter_get_basic(&value, &val);
1613 roaming_policy = string2roamingpolicy(val);
1618 dbus_message_iter_next(&array);
1621 dbus_message_iter_next(&iter);
1622 dbus_message_iter_get_basic(&iter, ¬ify_path);
1624 if (notify_path == NULL) {
1629 session_path = g_strdup_printf("/sessions%s", notify_path);
1630 if (session_path == NULL) {
1635 session = g_hash_table_lookup(session_hash, session_path);
1636 if (session != NULL) {
1642 session = g_try_new0(struct connman_session, 1);
1643 if (session == NULL) {
1648 session->info = g_try_new0(struct session_info, 1);
1649 if (session->info == NULL) {
1654 session->info_last = g_try_new0(struct session_info, 1);
1655 if (session->info_last == NULL) {
1660 info = session->info;
1661 info_last = session->info_last;
1663 session->owner = g_strdup(owner);
1664 session->session_path = session_path;
1665 session->notify_path = g_strdup(notify_path);
1666 session->notify_watch =
1667 g_dbus_add_disconnect_watch(connection, session->owner,
1668 owner_disconnect, session, NULL);
1670 info->state = CONNMAN_SESSION_STATE_DISCONNECTED;
1672 info->priority = priority;
1673 info->avoid_handover = avoid_handover;
1674 info->stay_connected = stay_connected;
1675 info->periodic_connect = periodic_connect;
1676 info->idle_timeout = idle_timeout;
1677 info->ecall = ecall;
1678 info->roaming_policy = roaming_policy;
1682 if (allowed_bearers == NULL) {
1683 info->allowed_bearers =
1684 session_allowed_bearers_any();
1686 if (info->allowed_bearers == NULL) {
1691 info->allowed_bearers = allowed_bearers;
1694 g_hash_table_replace(session_hash, session->session_path, session);
1696 DBG("add %s", session->session_path);
1698 if (g_dbus_register_interface(connection, session->session_path,
1699 CONNMAN_SESSION_INTERFACE,
1700 session_methods, NULL,
1701 NULL, session, NULL) == FALSE) {
1702 connman_error("Failed to register %s", session->session_path);
1703 g_hash_table_remove(session_hash, session->session_path);
1710 g_dbus_send_reply(connection, msg,
1711 DBUS_TYPE_OBJECT_PATH, &session->session_path,
1715 populate_service_list(session);
1716 if (info->ecall == TRUE) {
1718 update_ecall_sessions(session);
1721 info_last->state = info->state;
1722 info_last->priority = info->priority;
1723 info_last->avoid_handover = info->avoid_handover;
1724 info_last->stay_connected = info->stay_connected;
1725 info_last->periodic_connect = info->periodic_connect;
1726 info_last->idle_timeout = info->idle_timeout;
1727 info_last->ecall = info->ecall;
1728 info_last->roaming_policy = info->roaming_policy;
1729 info_last->entry = info->entry;
1730 info_last->marker = info->marker;
1731 info_last->allowed_bearers = info->allowed_bearers;
1733 session->append_all = TRUE;
1735 session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING);
1740 connman_error("Failed to create session");
1742 if (session != NULL) {
1743 if (session->info_last != NULL)
1744 g_free(session->info_last);
1745 if (session->info != NULL)
1746 g_free(session->info);
1750 g_free(session_path);
1752 g_slist_foreach(allowed_bearers, cleanup_bearer_info, NULL);
1753 g_slist_free(allowed_bearers);
1758 int __connman_session_destroy(DBusMessage *msg)
1760 const char *owner, *session_path;
1761 struct connman_session *session;
1763 owner = dbus_message_get_sender(msg);
1765 DBG("owner %s", owner);
1767 dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &session_path,
1769 if (session_path == NULL)
1772 session = g_hash_table_lookup(session_hash, session_path);
1773 if (session == NULL)
1776 if (g_strcmp0(owner, session->owner) != 0)
1779 session_disconnect(session);
1784 connman_bool_t __connman_session_mode()
1789 void __connman_session_set_mode(connman_bool_t enable)
1791 DBG("enable %d", enable);
1793 if (sessionmode != enable) {
1794 sessionmode = enable;
1796 connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
1797 CONNMAN_MANAGER_INTERFACE, "SessionMode",
1798 DBUS_TYPE_BOOLEAN, &sessionmode);
1801 if (sessionmode == TRUE)
1802 __connman_service_disconnect_all();
1805 static void service_add(struct connman_service *service,
1808 GHashTableIter iter;
1809 GSequenceIter *iter_service_list;
1810 gpointer key, value;
1811 struct connman_session *session;
1812 struct service_entry *entry;
1814 DBG("service %p", service);
1816 g_hash_table_iter_init(&iter, session_hash);
1818 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1821 if (service_match(session, service) == FALSE)
1824 entry = create_service_entry(service, name,
1825 CONNMAN_SERVICE_STATE_IDLE);
1830 g_sequence_insert_sorted(session->service_list,
1831 entry, sort_services,
1834 g_hash_table_replace(session->service_hash, service,
1837 session_changed(session, CONNMAN_SESSION_TRIGGER_SERVICE);
1841 static void service_remove(struct connman_service *service)
1844 GHashTableIter iter;
1845 gpointer key, value;
1846 struct connman_session *session;
1847 struct session_info *info;
1849 DBG("service %p", service);
1851 g_hash_table_iter_init(&iter, session_hash);
1853 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1854 GSequenceIter *iter;
1856 info = session->info;
1858 iter = g_hash_table_lookup(session->service_hash, service);
1862 g_sequence_remove(iter);
1864 if (info->entry != NULL && info->entry->service == service)
1866 session_changed(session, CONNMAN_SESSION_TRIGGER_SERVICE);
1870 static void service_state_changed(struct connman_service *service,
1871 enum connman_service_state state)
1873 GHashTableIter iter;
1874 gpointer key, value;
1876 DBG("service %p state %d", service, state);
1878 g_hash_table_iter_init(&iter, session_hash);
1880 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1881 struct connman_session *session = value;
1882 GSequenceIter *service_iter;
1884 service_iter = g_hash_table_lookup(session->service_hash, service);
1885 if (service_iter != NULL) {
1886 struct service_entry *entry;
1888 entry = g_sequence_get(service_iter);
1889 entry->state = state;
1892 session_changed(session,
1893 CONNMAN_SESSION_TRIGGER_SERVICE);
1897 static void ipconfig_changed(struct connman_service *service,
1898 struct connman_ipconfig *ipconfig)
1900 GHashTableIter iter;
1901 gpointer key, value;
1902 struct connman_session *session;
1903 struct session_info *info;
1904 enum connman_ipconfig_type type;
1906 DBG("service %p ipconfig %p", service, ipconfig);
1908 type = __connman_ipconfig_get_config_type(ipconfig);
1910 g_hash_table_iter_init(&iter, session_hash);
1912 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1914 info = session->info;
1916 if (info->state == CONNMAN_SESSION_STATE_DISCONNECTED)
1919 if (info->entry != NULL && info->entry->service == service) {
1920 if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
1921 ipconfig_ipv4_changed(session);
1922 else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
1923 ipconfig_ipv6_changed(session);
1928 static struct connman_notifier session_notifier = {
1930 .service_add = service_add,
1931 .service_remove = service_remove,
1932 .service_state_changed = service_state_changed,
1933 .ipconfig_changed = ipconfig_changed,
1936 int __connman_session_init(void)
1942 connection = connman_dbus_get_connection();
1943 if (connection == NULL)
1946 err = connman_notifier_register(&session_notifier);
1948 dbus_connection_unref(connection);
1952 session_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1953 NULL, cleanup_session);
1955 sessionmode = FALSE;
1959 void __connman_session_cleanup(void)
1963 if (connection == NULL)
1966 connman_notifier_unregister(&session_notifier);
1968 g_hash_table_foreach(session_hash, release_session, NULL);
1969 g_hash_table_destroy(session_hash);
1970 session_hash = NULL;
1972 dbus_connection_unref(connection);