5 * Copyright (C) 2007-2012 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 is_type_matching_state(enum connman_session_state *state,
551 enum connman_session_type type)
554 case CONNMAN_SESSION_TYPE_ANY:
556 case CONNMAN_SESSION_TYPE_LOCAL:
557 if (*state >= CONNMAN_SESSION_STATE_CONNECTED) {
558 *state = CONNMAN_SESSION_STATE_CONNECTED;
563 case CONNMAN_SESSION_TYPE_INTERNET:
564 if (*state == CONNMAN_SESSION_STATE_ONLINE)
572 static connman_bool_t compute_notifiable_changes(struct connman_session *session)
574 struct session_info *info_last = session->info_last;
575 struct session_info *info = session->info;
577 if (session->append_all == TRUE)
580 if (info->state != info_last->state)
583 if (info->entry != info_last->entry &&
584 info->state >= CONNMAN_SESSION_STATE_CONNECTED)
587 if (info->periodic_connect != info_last->periodic_connect ||
588 info->allowed_bearers != info_last->allowed_bearers ||
589 info->avoid_handover != info_last->avoid_handover ||
590 info->stay_connected != info_last->stay_connected ||
591 info->roaming_policy != info_last->roaming_policy ||
592 info->idle_timeout != info_last->idle_timeout ||
593 info->priority != info_last->priority ||
594 info->marker != info_last->marker ||
595 info->ecall != info_last->ecall ||
596 info->type != info_last->type)
602 static gboolean session_notify(gpointer user_data)
604 struct connman_session *session = user_data;
606 DBusMessageIter array, dict;
608 if (compute_notifiable_changes(session) == FALSE)
611 DBG("session %p owner %s notify_path %s", session,
612 session->owner, session->notify_path);
614 msg = dbus_message_new_method_call(session->owner, session->notify_path,
615 CONNMAN_NOTIFICATION_INTERFACE,
620 dbus_message_iter_init_append(msg, &array);
621 connman_dbus_dict_open(&array, &dict);
623 append_notify(&dict, session);
625 connman_dbus_dict_close(&array, &dict);
627 g_dbus_send_message(connection, msg);
632 static void ipconfig_ipv4_changed(struct connman_session *session)
634 struct session_info *info = session->info;
636 connman_dbus_setting_changed_dict(session->owner, session->notify_path,
637 "IPv4", append_ipconfig_ipv4,
638 info->entry->service);
641 static void ipconfig_ipv6_changed(struct connman_session *session)
643 struct session_info *info = session->info;
645 connman_dbus_setting_changed_dict(session->owner, session->notify_path,
646 "IPv6", append_ipconfig_ipv6,
647 info->entry->service);
650 static connman_bool_t service_type_match(struct connman_session *session,
651 struct connman_service *service)
653 struct session_info *info = session->info;
656 for (list = info->allowed_bearers;
657 list != NULL; list = list->next) {
658 struct bearer_info *info = list->data;
659 enum connman_service_type service_type;
661 if (info->match_all == TRUE)
664 service_type = connman_service_get_type(service);
665 if (info->service_type == service_type)
672 static connman_bool_t service_match(struct connman_session *session,
673 struct connman_service *service)
675 if (service_type_match(session, service) == FALSE)
681 static int service_type_weight(enum connman_service_type type)
684 * The session doesn't care which service
685 * to use. Nevertheless we have to sort them
686 * according their type. The ordering is
695 case CONNMAN_SERVICE_TYPE_ETHERNET:
697 case CONNMAN_SERVICE_TYPE_BLUETOOTH:
699 case CONNMAN_SERVICE_TYPE_WIFI:
700 case CONNMAN_SERVICE_TYPE_WIMAX:
702 case CONNMAN_SERVICE_TYPE_CELLULAR:
704 case CONNMAN_SERVICE_TYPE_UNKNOWN:
705 case CONNMAN_SERVICE_TYPE_SYSTEM:
706 case CONNMAN_SERVICE_TYPE_GPS:
707 case CONNMAN_SERVICE_TYPE_VPN:
708 case CONNMAN_SERVICE_TYPE_GADGET:
715 static gint sort_allowed_bearers(struct connman_service *service_a,
716 struct connman_service *service_b,
717 struct connman_session *session)
719 struct session_info *info = session->info;
721 enum connman_service_type type_a, type_b;
722 int weight_a, weight_b;
724 type_a = connman_service_get_type(service_a);
725 type_b = connman_service_get_type(service_b);
727 for (list = info->allowed_bearers;
728 list != NULL; list = list->next) {
729 struct bearer_info *info = list->data;
731 if (info->match_all == TRUE) {
732 if (type_a != type_b) {
733 weight_a = service_type_weight(type_a);
734 weight_b = service_type_weight(type_b);
736 if (weight_a > weight_b)
739 if (weight_a < weight_b)
746 if (type_a == info->service_type &&
747 type_b == info->service_type) {
751 if (type_a == info->service_type &&
752 type_b != info->service_type) {
756 if (type_a != info->service_type &&
757 type_b == info->service_type) {
765 static gint sort_services(gconstpointer a, gconstpointer b, gpointer user_data)
767 struct service_entry *entry_a = (void *)a;
768 struct service_entry *entry_b = (void *)b;
769 struct connman_session *session = user_data;
771 return sort_allowed_bearers(entry_a->service, entry_b->service,
775 static void cleanup_session(gpointer user_data)
777 struct connman_session *session = user_data;
778 struct session_info *info = session->info;
780 DBG("remove %s", session->session_path);
782 g_hash_table_destroy(session->service_hash);
783 g_sequence_free(session->service_list);
785 if (info->entry != NULL &&
786 info->entry->reason == CONNMAN_SESSION_REASON_CONNECT) {
787 __connman_service_disconnect(info->entry->service);
790 g_slist_foreach(info->allowed_bearers, cleanup_bearer_info, NULL);
791 g_slist_free(info->allowed_bearers);
793 g_free(session->owner);
794 g_free(session->session_path);
795 g_free(session->notify_path);
796 g_free(session->info);
797 g_free(session->info_last);
802 static enum connman_session_state service_to_session_state(enum connman_service_state state)
805 case CONNMAN_SERVICE_STATE_UNKNOWN:
806 case CONNMAN_SERVICE_STATE_IDLE:
807 case CONNMAN_SERVICE_STATE_ASSOCIATION:
808 case CONNMAN_SERVICE_STATE_CONFIGURATION:
809 case CONNMAN_SERVICE_STATE_DISCONNECT:
810 case CONNMAN_SERVICE_STATE_FAILURE:
812 case CONNMAN_SERVICE_STATE_READY:
813 return CONNMAN_SESSION_STATE_CONNECTED;
814 case CONNMAN_SERVICE_STATE_ONLINE:
815 return CONNMAN_SESSION_STATE_ONLINE;
818 return CONNMAN_SESSION_STATE_DISCONNECTED;
821 static connman_bool_t is_connected(enum connman_service_state state)
824 case CONNMAN_SERVICE_STATE_UNKNOWN:
825 case CONNMAN_SERVICE_STATE_IDLE:
826 case CONNMAN_SERVICE_STATE_ASSOCIATION:
827 case CONNMAN_SERVICE_STATE_CONFIGURATION:
828 case CONNMAN_SERVICE_STATE_DISCONNECT:
829 case CONNMAN_SERVICE_STATE_FAILURE:
831 case CONNMAN_SERVICE_STATE_READY:
832 case CONNMAN_SERVICE_STATE_ONLINE:
839 static connman_bool_t is_connecting(enum connman_service_state state)
842 case CONNMAN_SERVICE_STATE_UNKNOWN:
843 case CONNMAN_SERVICE_STATE_IDLE:
845 case CONNMAN_SERVICE_STATE_ASSOCIATION:
846 case CONNMAN_SERVICE_STATE_CONFIGURATION:
848 case CONNMAN_SERVICE_STATE_DISCONNECT:
849 case CONNMAN_SERVICE_STATE_FAILURE:
850 case CONNMAN_SERVICE_STATE_READY:
851 case CONNMAN_SERVICE_STATE_ONLINE:
858 static connman_bool_t explicit_connect(enum connman_session_reason reason)
861 case CONNMAN_SESSION_REASON_UNKNOWN:
862 case CONNMAN_SESSION_REASON_FREE_RIDE:
863 case CONNMAN_SESSION_REASON_DISCONNECT:
865 case CONNMAN_SESSION_REASON_CONNECT:
866 case CONNMAN_SESSION_REASON_PERIODIC:
873 static connman_bool_t explicit_disconnect(struct session_info *info)
875 if (info->entry == NULL)
878 DBG("reason %s service %p state %d",
879 reason2string(info->entry->reason),
880 info->entry->service, info->entry->state);
882 if (info->entry->reason == CONNMAN_SESSION_REASON_UNKNOWN)
885 if (explicit_connect(info->entry->reason) == FALSE)
888 if (__connman_service_session_dec(info->entry->service) == FALSE)
891 if (ecall_info != NULL && ecall_info != info)
897 struct pending_data {
898 unsigned int timeout;
899 struct service_entry *entry;
900 gboolean (*cb)(gpointer);
903 static void pending_timeout_free(gpointer data, gpointer user_data)
905 struct pending_data *pending = data;
907 DBG("pending %p timeout %d", pending, pending->timeout);
908 g_source_remove(pending->timeout);
912 static void pending_timeout_remove_all(struct service_entry *entry)
916 g_slist_foreach(entry->pending_timeouts, pending_timeout_free, NULL);
917 g_slist_free(entry->pending_timeouts);
918 entry->pending_timeouts = NULL;
921 static gboolean pending_timeout_cb(gpointer data)
923 struct pending_data *pending = data;
924 struct service_entry *entry = pending->entry;
927 DBG("pending %p timeout %d", pending, pending->timeout);
929 ret = pending->cb(pending->entry);
931 entry->pending_timeouts =
932 g_slist_remove(entry->pending_timeouts,
939 static connman_bool_t pending_timeout_add(unsigned int seconds,
940 gboolean (*cb)(gpointer),
941 struct service_entry *entry)
943 struct pending_data *pending = g_try_new0(struct pending_data, 1);
945 if (pending == NULL || cb == NULL || entry == NULL) {
951 pending->entry = entry;
952 pending->timeout = g_timeout_add_seconds(seconds, pending_timeout_cb,
954 entry->pending_timeouts = g_slist_prepend(entry->pending_timeouts,
957 DBG("pending %p entry %p timeout id %d", pending, entry,
963 static gboolean call_disconnect(gpointer user_data)
965 struct service_entry *entry = user_data;
966 struct connman_service *service = entry->service;
969 * TODO: We should mark this entry as pending work. In case
970 * disconnect fails we just unassign this session from the
971 * service and can't do anything later on it
973 DBG("disconnect service %p", service);
974 __connman_service_disconnect(service);
979 static gboolean call_connect(gpointer user_data)
981 struct service_entry *entry = user_data;
982 struct connman_service *service = entry->service;
984 DBG("connect service %p", service);
985 __connman_service_connect(service);
990 static void deselect_service(struct session_info *info)
992 struct service_entry *entry;
993 connman_bool_t disconnect, connected;
997 if (info->entry == NULL)
1000 disconnect = explicit_disconnect(info);
1002 connected = is_connecting(info->entry->state) == TRUE ||
1003 is_connected(info->entry->state) == TRUE;
1005 info->state = CONNMAN_SESSION_STATE_DISCONNECTED;
1006 info->entry->reason = CONNMAN_SESSION_REASON_UNKNOWN;
1008 entry = info->entry;
1011 DBG("disconnect %d connected %d", disconnect, connected);
1013 if (disconnect == TRUE && connected == TRUE)
1014 pending_timeout_add(0, call_disconnect, entry);
1017 static void deselect_and_disconnect(struct connman_session *session,
1018 enum connman_session_reason reason)
1020 struct session_info *info = session->info;
1022 deselect_service(info);
1024 info->reason = reason;
1027 static void select_connected_service(struct session_info *info,
1028 struct service_entry *entry)
1030 enum connman_session_state state;
1032 state = service_to_session_state(entry->state);
1033 if (is_type_matching_state(&state, info->type) == FALSE)
1036 info->state = state;
1038 info->entry = entry;
1039 info->entry->reason = info->reason;
1041 if (explicit_connect(info->reason) == FALSE)
1044 __connman_service_session_inc(info->entry->service);
1047 static void select_offline_service(struct session_info *info,
1048 struct service_entry *entry)
1050 if (explicit_connect(info->reason) == FALSE)
1053 info->state = service_to_session_state(entry->state);
1055 info->entry = entry;
1056 info->entry->reason = info->reason;
1058 __connman_service_session_inc(info->entry->service);
1059 pending_timeout_add(0, call_connect, entry);
1062 static void select_service(struct session_info *info,
1063 struct service_entry *entry)
1065 DBG("service %p", entry->service);
1067 if (is_connected(entry->state) == TRUE)
1068 select_connected_service(info, entry);
1070 select_offline_service(info, entry);
1073 static void select_and_connect(struct connman_session *session,
1074 enum connman_session_reason reason)
1076 struct session_info *info = session->info;
1077 struct service_entry *entry = NULL;
1078 GSequenceIter *iter;
1080 DBG("session %p reason %s", session, reason2string(reason));
1082 info->reason = reason;
1084 iter = g_sequence_get_begin_iter(session->service_list);
1086 while (g_sequence_iter_is_end(iter) == FALSE) {
1087 entry = g_sequence_get(iter);
1089 switch (entry->state) {
1090 case CONNMAN_SERVICE_STATE_ASSOCIATION:
1091 case CONNMAN_SERVICE_STATE_CONFIGURATION:
1092 case CONNMAN_SERVICE_STATE_READY:
1093 case CONNMAN_SERVICE_STATE_ONLINE:
1094 case CONNMAN_SERVICE_STATE_IDLE:
1095 case CONNMAN_SERVICE_STATE_DISCONNECT:
1096 select_service(info, entry);
1098 case CONNMAN_SERVICE_STATE_UNKNOWN:
1099 case CONNMAN_SERVICE_STATE_FAILURE:
1103 iter = g_sequence_iter_next(iter);
1107 static struct service_entry *create_service_entry(struct connman_service *service,
1109 enum connman_service_state state)
1111 struct service_entry *entry;
1112 enum connman_service_type type;
1115 entry = g_try_new0(struct service_entry, 1);
1119 entry->reason = CONNMAN_SESSION_REASON_UNKNOWN;
1120 entry->state = state;
1125 entry->service = service;
1127 idx = __connman_service_get_index(entry->service);
1128 entry->ifname = connman_inet_ifname(idx);
1129 if (entry->ifname == NULL)
1130 entry->ifname = g_strdup("");
1132 type = connman_service_get_type(entry->service);
1133 entry->bearer = service2bearer(type);
1138 static void destroy_service_entry(gpointer data)
1140 struct service_entry *entry = data;
1142 pending_timeout_remove_all(entry);
1143 g_free(entry->ifname);
1148 static void populate_service_list(struct connman_session *session)
1150 struct service_entry *entry;
1151 GSequenceIter *iter;
1153 session->service_hash =
1154 g_hash_table_new_full(g_direct_hash, g_direct_equal,
1156 session->service_list = __connman_service_get_list(session,
1158 create_service_entry,
1159 destroy_service_entry);
1161 g_sequence_sort(session->service_list, sort_services, session);
1163 iter = g_sequence_get_begin_iter(session->service_list);
1165 while (g_sequence_iter_is_end(iter) == FALSE) {
1166 entry = g_sequence_get(iter);
1168 DBG("service %p type %s name %s", entry->service,
1169 service2bearer(connman_service_get_type(entry->service)),
1172 g_hash_table_replace(session->service_hash,
1173 entry->service, iter);
1175 iter = g_sequence_iter_next(iter);
1179 static void session_changed(struct connman_session *session,
1180 enum connman_session_trigger trigger)
1182 struct session_info *info = session->info;
1183 struct session_info *info_last = session->info_last;
1184 GSequenceIter *service_iter = NULL, *service_iter_last = NULL;
1185 GSequence *service_list_last;
1186 GHashTable *service_hash_last;
1189 * TODO: This only a placeholder for the 'real' algorithm to
1190 * play a bit around. So we are going to improve it step by step.
1193 DBG("session %p trigger %s reason %s", session, trigger2string(trigger),
1194 reason2string(info->reason));
1196 if (info->entry != NULL) {
1197 enum connman_session_state state;
1199 state = service_to_session_state(info->entry->state);
1201 if (is_type_matching_state(&state, info->type) == TRUE)
1202 info->state = state;
1206 case CONNMAN_SESSION_TRIGGER_UNKNOWN:
1207 DBG("ignore session changed event");
1209 case CONNMAN_SESSION_TRIGGER_SETTING:
1210 if (info->allowed_bearers != info_last->allowed_bearers) {
1212 service_hash_last = session->service_hash;
1213 service_list_last = session->service_list;
1215 populate_service_list(session);
1217 if (info->entry != NULL) {
1218 service_iter_last = g_hash_table_lookup(
1220 info->entry->service);
1221 service_iter = g_hash_table_lookup(
1222 session->service_hash,
1223 info->entry->service);
1226 if (service_iter == NULL && service_iter_last != NULL) {
1228 * The currently selected service is
1229 * not part of this session anymore.
1231 deselect_and_disconnect(session, info->reason);
1234 g_hash_table_remove_all(service_hash_last);
1235 g_sequence_free(service_list_last);
1238 if (info->type != info_last->type) {
1239 if (info->state >= CONNMAN_SESSION_STATE_CONNECTED &&
1240 is_type_matching_state(&info->state,
1241 info->type) == FALSE)
1242 deselect_and_disconnect(session, info->reason);
1245 if (info->state == CONNMAN_SESSION_STATE_DISCONNECTED) {
1246 select_and_connect(session,
1247 CONNMAN_SESSION_REASON_FREE_RIDE);
1251 case CONNMAN_SESSION_TRIGGER_CONNECT:
1252 if (info->state >= CONNMAN_SESSION_STATE_CONNECTED) {
1253 if (info->entry->reason == CONNMAN_SESSION_REASON_CONNECT)
1255 info->entry->reason = CONNMAN_SESSION_REASON_CONNECT;
1256 __connman_service_session_inc(info->entry->service);
1260 if (info->entry != NULL &&
1261 is_connecting(info->entry->state) == TRUE) {
1265 select_and_connect(session,
1266 CONNMAN_SESSION_REASON_CONNECT);
1269 case CONNMAN_SESSION_TRIGGER_DISCONNECT:
1270 deselect_and_disconnect(session,
1271 CONNMAN_SESSION_REASON_DISCONNECT);
1274 case CONNMAN_SESSION_TRIGGER_PERIODIC:
1275 if (info->state >= CONNMAN_SESSION_STATE_CONNECTED) {
1276 info->entry->reason = CONNMAN_SESSION_REASON_PERIODIC;
1277 __connman_service_session_inc(info->entry->service);
1281 select_and_connect(session,
1282 CONNMAN_SESSION_REASON_PERIODIC);
1285 case CONNMAN_SESSION_TRIGGER_SERVICE:
1286 if (info->entry != NULL &&
1287 (is_connecting(info->entry->state) == TRUE ||
1288 is_connected(info->entry->state) == TRUE)) {
1292 deselect_and_disconnect(session, info->reason);
1294 if (info->reason == CONNMAN_SESSION_REASON_FREE_RIDE ||
1295 info->stay_connected == TRUE) {
1296 select_and_connect(session, info->reason);
1300 case CONNMAN_SESSION_TRIGGER_ECALL:
1301 if (info->state == CONNMAN_SESSION_STATE_DISCONNECTED &&
1302 info->entry != NULL &&
1303 info->entry->service != NULL) {
1304 deselect_and_disconnect(session, info->reason);
1310 session_notify(session);
1313 static DBusMessage *connect_session(DBusConnection *conn,
1314 DBusMessage *msg, void *user_data)
1316 struct connman_session *session = user_data;
1317 struct session_info *info = session->info;
1319 DBG("session %p", session);
1321 if (ecall_info != NULL && ecall_info != info)
1322 return __connman_error_failed(msg, EBUSY);
1324 session_changed(session, CONNMAN_SESSION_TRIGGER_CONNECT);
1326 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1329 static DBusMessage *disconnect_session(DBusConnection *conn,
1330 DBusMessage *msg, void *user_data)
1332 struct connman_session *session = user_data;
1333 struct session_info *info = session->info;
1335 DBG("session %p", session);
1337 if (ecall_info != NULL && ecall_info != info)
1338 return __connman_error_failed(msg, EBUSY);
1340 session_changed(session, CONNMAN_SESSION_TRIGGER_DISCONNECT);
1342 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1345 static void update_ecall_sessions(struct connman_session *session)
1347 struct session_info *info = session->info;
1348 struct connman_session *session_iter;
1349 GHashTableIter iter;
1350 gpointer key, value;
1352 g_hash_table_iter_init(&iter, session_hash);
1354 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1355 session_iter = value;
1357 if (session_iter == session)
1360 session_iter->info->ecall = info->ecall;
1362 session_changed(session_iter, CONNMAN_SESSION_TRIGGER_ECALL);
1366 static void update_ecall(struct connman_session *session)
1368 struct session_info *info = session->info;
1369 struct session_info *info_last = session->info_last;
1371 DBG("session %p ecall_info %p ecall %d -> %d", session,
1372 ecall_info, info_last->ecall, info->ecall);
1374 if (ecall_info == NULL) {
1375 if (!(info_last->ecall == FALSE && info->ecall == TRUE))
1379 } else if (ecall_info == info) {
1380 if (!(info_last->ecall == TRUE && info->ecall == FALSE))
1388 update_ecall_sessions(session);
1393 /* not a valid transition */
1394 info->ecall = info_last->ecall;
1397 static DBusMessage *change_session(DBusConnection *conn,
1398 DBusMessage *msg, void *user_data)
1400 struct connman_session *session = user_data;
1401 struct session_info *info = session->info;
1402 DBusMessageIter iter, value;
1405 GSList *allowed_bearers;
1407 DBG("session %p", session);
1408 if (dbus_message_iter_init(msg, &iter) == FALSE)
1409 return __connman_error_invalid_arguments(msg);
1411 dbus_message_iter_get_basic(&iter, &name);
1412 dbus_message_iter_next(&iter);
1413 dbus_message_iter_recurse(&iter, &value);
1415 switch (dbus_message_iter_get_arg_type(&value)) {
1416 case DBUS_TYPE_ARRAY:
1417 if (g_str_equal(name, "AllowedBearers") == TRUE) {
1418 allowed_bearers = session_parse_allowed_bearers(&value);
1420 g_slist_foreach(info->allowed_bearers,
1421 cleanup_bearer_info, NULL);
1422 g_slist_free(info->allowed_bearers);
1424 if (allowed_bearers == NULL) {
1425 allowed_bearers = session_allowed_bearers_any();
1427 if (allowed_bearers == NULL)
1428 return __connman_error_failed(msg, ENOMEM);
1431 info->allowed_bearers = allowed_bearers;
1436 case DBUS_TYPE_BOOLEAN:
1437 if (g_str_equal(name, "Priority") == TRUE) {
1438 dbus_message_iter_get_basic(&value,
1440 } else if (g_str_equal(name, "AvoidHandover") == TRUE) {
1441 dbus_message_iter_get_basic(&value,
1442 &info->avoid_handover);
1443 } else if (g_str_equal(name, "StayConnected") == TRUE) {
1444 dbus_message_iter_get_basic(&value,
1445 &info->stay_connected);
1446 } else if (g_str_equal(name, "EmergencyCall") == TRUE) {
1447 dbus_message_iter_get_basic(&value,
1450 update_ecall(session);
1455 case DBUS_TYPE_UINT32:
1456 if (g_str_equal(name, "PeriodicConnect") == TRUE) {
1457 dbus_message_iter_get_basic(&value,
1458 &info->periodic_connect);
1459 } else if (g_str_equal(name, "IdleTimeout") == TRUE) {
1460 dbus_message_iter_get_basic(&value,
1461 &info->idle_timeout);
1466 case DBUS_TYPE_STRING:
1467 if (g_str_equal(name, "ConnectionType") == TRUE) {
1468 dbus_message_iter_get_basic(&value, &val);
1469 info->type = string2type(val);
1470 } else if (g_str_equal(name, "RoamingPolicy") == TRUE) {
1471 dbus_message_iter_get_basic(&value, &val);
1472 info->roaming_policy =
1473 string2roamingpolicy(val);
1482 session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING);
1484 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1487 return __connman_error_invalid_arguments(msg);
1490 static void release_session(gpointer key, gpointer value, gpointer user_data)
1492 struct connman_session *session = value;
1493 DBusMessage *message;
1495 DBG("owner %s path %s", session->owner, session->notify_path);
1497 if (session->notify_watch > 0)
1498 g_dbus_remove_watch(connection, session->notify_watch);
1500 g_dbus_unregister_interface(connection, session->session_path,
1501 CONNMAN_SESSION_INTERFACE);
1503 message = dbus_message_new_method_call(session->owner,
1504 session->notify_path,
1505 CONNMAN_NOTIFICATION_INTERFACE,
1507 if (message == NULL)
1510 dbus_message_set_no_reply(message, TRUE);
1512 g_dbus_send_message(connection, message);
1515 static int session_disconnect(struct connman_session *session)
1517 DBG("session %p, %s", session, session->owner);
1519 if (session->notify_watch > 0)
1520 g_dbus_remove_watch(connection, session->notify_watch);
1522 g_dbus_unregister_interface(connection, session->session_path,
1523 CONNMAN_SESSION_INTERFACE);
1525 deselect_and_disconnect(session,
1526 CONNMAN_SESSION_REASON_DISCONNECT);
1528 g_hash_table_remove(session_hash, session->session_path);
1533 static void owner_disconnect(DBusConnection *conn, void *user_data)
1535 struct connman_session *session = user_data;
1537 DBG("session %p, %s died", session, session->owner);
1539 session_disconnect(session);
1542 static DBusMessage *destroy_session(DBusConnection *conn,
1543 DBusMessage *msg, void *user_data)
1545 struct connman_session *session = user_data;
1546 struct session_info *info = session->info;
1548 DBG("session %p", session);
1550 if (ecall_info != NULL && ecall_info != info)
1551 return __connman_error_failed(msg, EBUSY);
1553 session_disconnect(session);
1555 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1558 static const GDBusMethodTable session_methods[] = {
1559 { "Destroy", "", "", destroy_session },
1560 { "Connect", "", "", connect_session },
1561 { "Disconnect", "", "", disconnect_session },
1562 { "Change", "sv", "", change_session },
1566 int __connman_session_create(DBusMessage *msg)
1568 const char *owner, *notify_path;
1569 char *session_path = NULL;
1570 DBusMessageIter iter, array;
1571 struct connman_session *session = NULL;
1572 struct session_info *info, *info_last;
1574 enum connman_session_type type = CONNMAN_SESSION_TYPE_ANY;
1575 connman_bool_t priority = FALSE, avoid_handover = FALSE;
1576 connman_bool_t stay_connected = FALSE, ecall = FALSE;
1577 enum connman_session_roaming_policy roaming_policy =
1578 CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN;
1579 GSList *allowed_bearers = NULL;
1580 unsigned int periodic_connect = 0;
1581 unsigned int idle_timeout = 0;
1585 owner = dbus_message_get_sender(msg);
1587 DBG("owner %s", owner);
1589 if (ecall_info != NULL) {
1591 * If there is an emergency call already going on,
1592 * ignore session creation attempt
1598 dbus_message_iter_init(msg, &iter);
1599 dbus_message_iter_recurse(&iter, &array);
1601 while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
1602 DBusMessageIter entry, value;
1603 const char *key, *val;
1605 dbus_message_iter_recurse(&array, &entry);
1606 dbus_message_iter_get_basic(&entry, &key);
1608 dbus_message_iter_next(&entry);
1609 dbus_message_iter_recurse(&entry, &value);
1611 switch (dbus_message_iter_get_arg_type(&value)) {
1612 case DBUS_TYPE_ARRAY:
1613 if (g_str_equal(key, "AllowedBearers") == TRUE) {
1615 session_parse_allowed_bearers(&value);
1620 case DBUS_TYPE_BOOLEAN:
1621 if (g_str_equal(key, "Priority") == TRUE) {
1622 dbus_message_iter_get_basic(&value,
1624 } else if (g_str_equal(key, "AvoidHandover") == TRUE) {
1625 dbus_message_iter_get_basic(&value,
1627 } else if (g_str_equal(key, "StayConnected") == TRUE) {
1628 dbus_message_iter_get_basic(&value,
1630 } else if (g_str_equal(key, "EmergencyCall") == TRUE) {
1631 dbus_message_iter_get_basic(&value,
1637 case DBUS_TYPE_UINT32:
1638 if (g_str_equal(key, "PeriodicConnect") == TRUE) {
1639 dbus_message_iter_get_basic(&value,
1641 } else if (g_str_equal(key, "IdleTimeout") == TRUE) {
1642 dbus_message_iter_get_basic(&value,
1648 case DBUS_TYPE_STRING:
1649 if (g_str_equal(key, "ConnectionType") == TRUE) {
1650 dbus_message_iter_get_basic(&value, &val);
1651 type = string2type(val);
1652 } else if (g_str_equal(key, "RoamingPolicy") == TRUE) {
1653 dbus_message_iter_get_basic(&value, &val);
1654 roaming_policy = string2roamingpolicy(val);
1659 dbus_message_iter_next(&array);
1662 dbus_message_iter_next(&iter);
1663 dbus_message_iter_get_basic(&iter, ¬ify_path);
1665 if (notify_path == NULL) {
1670 session_path = g_strdup_printf("/sessions%s", notify_path);
1671 if (session_path == NULL) {
1676 session = g_hash_table_lookup(session_hash, session_path);
1677 if (session != NULL) {
1683 session = g_try_new0(struct connman_session, 1);
1684 if (session == NULL) {
1689 session->info = g_try_new0(struct session_info, 1);
1690 if (session->info == NULL) {
1695 session->info_last = g_try_new0(struct session_info, 1);
1696 if (session->info_last == NULL) {
1701 info = session->info;
1702 info_last = session->info_last;
1704 session->owner = g_strdup(owner);
1705 session->session_path = session_path;
1706 session->notify_path = g_strdup(notify_path);
1707 session->notify_watch =
1708 g_dbus_add_disconnect_watch(connection, session->owner,
1709 owner_disconnect, session, NULL);
1711 info->state = CONNMAN_SESSION_STATE_DISCONNECTED;
1713 info->priority = priority;
1714 info->avoid_handover = avoid_handover;
1715 info->stay_connected = stay_connected;
1716 info->periodic_connect = periodic_connect;
1717 info->idle_timeout = idle_timeout;
1718 info->ecall = ecall;
1719 info->roaming_policy = roaming_policy;
1723 if (allowed_bearers == NULL) {
1724 info->allowed_bearers =
1725 session_allowed_bearers_any();
1727 if (info->allowed_bearers == NULL) {
1732 info->allowed_bearers = allowed_bearers;
1735 g_hash_table_replace(session_hash, session->session_path, session);
1737 DBG("add %s", session->session_path);
1739 if (g_dbus_register_interface(connection, session->session_path,
1740 CONNMAN_SESSION_INTERFACE,
1741 session_methods, NULL,
1742 NULL, session, NULL) == FALSE) {
1743 connman_error("Failed to register %s", session->session_path);
1744 g_hash_table_remove(session_hash, session->session_path);
1751 g_dbus_send_reply(connection, msg,
1752 DBUS_TYPE_OBJECT_PATH, &session->session_path,
1756 populate_service_list(session);
1757 if (info->ecall == TRUE) {
1759 update_ecall_sessions(session);
1762 info_last->state = info->state;
1763 info_last->priority = info->priority;
1764 info_last->avoid_handover = info->avoid_handover;
1765 info_last->stay_connected = info->stay_connected;
1766 info_last->periodic_connect = info->periodic_connect;
1767 info_last->idle_timeout = info->idle_timeout;
1768 info_last->ecall = info->ecall;
1769 info_last->roaming_policy = info->roaming_policy;
1770 info_last->entry = info->entry;
1771 info_last->marker = info->marker;
1772 info_last->allowed_bearers = info->allowed_bearers;
1774 session->append_all = TRUE;
1776 session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING);
1781 connman_error("Failed to create session");
1783 if (session != NULL) {
1784 if (session->info_last != NULL)
1785 g_free(session->info_last);
1786 if (session->info != NULL)
1787 g_free(session->info);
1791 g_free(session_path);
1793 g_slist_foreach(allowed_bearers, cleanup_bearer_info, NULL);
1794 g_slist_free(allowed_bearers);
1799 int __connman_session_destroy(DBusMessage *msg)
1801 const char *owner, *session_path;
1802 struct connman_session *session;
1804 owner = dbus_message_get_sender(msg);
1806 DBG("owner %s", owner);
1808 dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &session_path,
1810 if (session_path == NULL)
1813 session = g_hash_table_lookup(session_hash, session_path);
1814 if (session == NULL)
1817 if (g_strcmp0(owner, session->owner) != 0)
1820 session_disconnect(session);
1825 connman_bool_t __connman_session_mode()
1830 void __connman_session_set_mode(connman_bool_t enable)
1832 DBG("enable %d", enable);
1834 if (sessionmode != enable) {
1835 sessionmode = enable;
1837 connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
1838 CONNMAN_MANAGER_INTERFACE, "SessionMode",
1839 DBUS_TYPE_BOOLEAN, &sessionmode);
1842 if (sessionmode == TRUE)
1843 __connman_service_disconnect_all();
1846 static void service_add(struct connman_service *service,
1849 GHashTableIter iter;
1850 GSequenceIter *iter_service_list;
1851 gpointer key, value;
1852 struct connman_session *session;
1853 struct service_entry *entry;
1855 DBG("service %p", service);
1857 g_hash_table_iter_init(&iter, session_hash);
1859 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1862 if (service_match(session, service) == FALSE)
1865 entry = create_service_entry(service, name,
1866 CONNMAN_SERVICE_STATE_IDLE);
1871 g_sequence_insert_sorted(session->service_list,
1872 entry, sort_services,
1875 g_hash_table_replace(session->service_hash, service,
1878 session_changed(session, CONNMAN_SESSION_TRIGGER_SERVICE);
1882 static void service_remove(struct connman_service *service)
1885 GHashTableIter iter;
1886 gpointer key, value;
1887 struct connman_session *session;
1888 struct session_info *info;
1890 DBG("service %p", service);
1892 g_hash_table_iter_init(&iter, session_hash);
1894 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1895 GSequenceIter *iter;
1897 info = session->info;
1899 iter = g_hash_table_lookup(session->service_hash, service);
1903 g_sequence_remove(iter);
1905 if (info->entry != NULL && info->entry->service == service)
1907 session_changed(session, CONNMAN_SESSION_TRIGGER_SERVICE);
1911 static void service_state_changed(struct connman_service *service,
1912 enum connman_service_state state)
1914 GHashTableIter iter;
1915 gpointer key, value;
1917 DBG("service %p state %d", service, state);
1919 g_hash_table_iter_init(&iter, session_hash);
1921 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1922 struct connman_session *session = value;
1923 GSequenceIter *service_iter;
1925 service_iter = g_hash_table_lookup(session->service_hash, service);
1926 if (service_iter != NULL) {
1927 struct service_entry *entry;
1929 entry = g_sequence_get(service_iter);
1930 entry->state = state;
1933 session_changed(session,
1934 CONNMAN_SESSION_TRIGGER_SERVICE);
1938 static void ipconfig_changed(struct connman_service *service,
1939 struct connman_ipconfig *ipconfig)
1941 GHashTableIter iter;
1942 gpointer key, value;
1943 struct connman_session *session;
1944 struct session_info *info;
1945 enum connman_ipconfig_type type;
1947 DBG("service %p ipconfig %p", service, ipconfig);
1949 type = __connman_ipconfig_get_config_type(ipconfig);
1951 g_hash_table_iter_init(&iter, session_hash);
1953 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1955 info = session->info;
1957 if (info->state == CONNMAN_SESSION_STATE_DISCONNECTED)
1960 if (info->entry != NULL && info->entry->service == service) {
1961 if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
1962 ipconfig_ipv4_changed(session);
1963 else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
1964 ipconfig_ipv6_changed(session);
1969 static struct connman_notifier session_notifier = {
1971 .service_add = service_add,
1972 .service_remove = service_remove,
1973 .service_state_changed = service_state_changed,
1974 .ipconfig_changed = ipconfig_changed,
1977 int __connman_session_init(void)
1983 connection = connman_dbus_get_connection();
1984 if (connection == NULL)
1987 err = connman_notifier_register(&session_notifier);
1989 dbus_connection_unref(connection);
1993 session_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1994 NULL, cleanup_session);
1996 sessionmode = FALSE;
2000 void __connman_session_cleanup(void)
2004 if (connection == NULL)
2007 connman_notifier_unregister(&session_notifier);
2009 g_hash_table_foreach(session_hash, release_session, NULL);
2010 g_hash_table_destroy(session_hash);
2011 session_hash = NULL;
2013 dbus_connection_unref(connection);