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
31 #include <connman/session.h>
35 static DBusConnection *connection;
36 static GHashTable *session_hash;
37 static connman_bool_t sessionmode;
38 static struct connman_session *ecall_session;
39 static GSList *policy_list;
41 enum connman_session_trigger {
42 CONNMAN_SESSION_TRIGGER_UNKNOWN = 0,
43 CONNMAN_SESSION_TRIGGER_SETTING = 1,
44 CONNMAN_SESSION_TRIGGER_CONNECT = 2,
45 CONNMAN_SESSION_TRIGGER_DISCONNECT = 3,
46 CONNMAN_SESSION_TRIGGER_SERVICE = 4,
47 CONNMAN_SESSION_TRIGGER_ECALL = 5,
50 enum connman_session_reason {
51 CONNMAN_SESSION_REASON_UNKNOWN = 0,
52 CONNMAN_SESSION_REASON_CONNECT = 1,
53 CONNMAN_SESSION_REASON_FREE_RIDE = 2,
56 enum connman_session_state {
57 CONNMAN_SESSION_STATE_DISCONNECTED = 0,
58 CONNMAN_SESSION_STATE_CONNECTED = 1,
59 CONNMAN_SESSION_STATE_ONLINE = 2,
62 struct service_entry {
63 /* track why this service was selected */
64 enum connman_session_reason reason;
65 enum connman_service_state state;
67 struct connman_service *service;
70 GSList *pending_timeouts;
74 struct connman_session_config config;
75 enum connman_session_state state;
76 struct service_entry *entry;
77 enum connman_session_reason reason;
80 struct connman_session {
86 struct connman_session_policy *policy;
88 connman_bool_t append_all;
89 struct session_info *info;
90 struct session_info *info_last;
94 GSequence *service_list;
95 GHashTable *service_hash;
98 static const char *trigger2string(enum connman_session_trigger trigger)
101 case CONNMAN_SESSION_TRIGGER_UNKNOWN:
103 case CONNMAN_SESSION_TRIGGER_SETTING:
105 case CONNMAN_SESSION_TRIGGER_CONNECT:
107 case CONNMAN_SESSION_TRIGGER_DISCONNECT:
109 case CONNMAN_SESSION_TRIGGER_SERVICE:
111 case CONNMAN_SESSION_TRIGGER_ECALL:
118 static const char *reason2string(enum connman_session_reason reason)
121 case CONNMAN_SESSION_REASON_UNKNOWN:
123 case CONNMAN_SESSION_REASON_CONNECT:
125 case CONNMAN_SESSION_REASON_FREE_RIDE:
132 static const char *state2string(enum connman_session_state state)
135 case CONNMAN_SESSION_STATE_DISCONNECTED:
136 return "disconnected";
137 case CONNMAN_SESSION_STATE_CONNECTED:
139 case CONNMAN_SESSION_STATE_ONLINE:
146 static const char *type2string(enum connman_session_type type)
149 case CONNMAN_SESSION_TYPE_ANY:
151 case CONNMAN_SESSION_TYPE_LOCAL:
153 case CONNMAN_SESSION_TYPE_INTERNET:
160 static enum connman_session_type string2type(const char *type)
162 if (g_strcmp0(type, "local") == 0)
163 return CONNMAN_SESSION_TYPE_LOCAL;
164 else if (g_strcmp0(type, "internet") == 0)
165 return CONNMAN_SESSION_TYPE_INTERNET;
167 return CONNMAN_SESSION_TYPE_ANY;
170 static enum connman_session_roaming_policy string2roamingpolicy(const char *policy)
172 if (g_strcmp0(policy, "default") == 0)
173 return CONNMAN_SESSION_ROAMING_POLICY_DEFAULT;
174 else if (g_strcmp0(policy, "always") == 0)
175 return CONNMAN_SESSION_ROAMING_POLICY_ALWAYS;
176 else if (g_strcmp0(policy, "forbidden") == 0)
177 return CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN;
178 else if (g_strcmp0(policy, "national") == 0)
179 return CONNMAN_SESSION_ROAMING_POLICY_NATIONAL;
180 else if (g_strcmp0(policy, "international") == 0)
181 return CONNMAN_SESSION_ROAMING_POLICY_INTERNATIONAL;
183 return CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN;
186 static enum connman_service_type bearer2service(const char *bearer)
189 return CONNMAN_SERVICE_TYPE_UNKNOWN;
191 if (g_strcmp0(bearer, "ethernet") == 0)
192 return CONNMAN_SERVICE_TYPE_ETHERNET;
193 else if (g_strcmp0(bearer, "wifi") == 0)
194 return CONNMAN_SERVICE_TYPE_WIFI;
195 else if (g_strcmp0(bearer, "wimax") == 0)
196 return CONNMAN_SERVICE_TYPE_WIMAX;
197 else if (g_strcmp0(bearer, "bluetooth") == 0)
198 return CONNMAN_SERVICE_TYPE_BLUETOOTH;
199 else if (g_strcmp0(bearer, "cellular") == 0)
200 return CONNMAN_SERVICE_TYPE_CELLULAR;
201 else if (g_strcmp0(bearer, "vpn") == 0)
202 return CONNMAN_SERVICE_TYPE_VPN;
204 return CONNMAN_SERVICE_TYPE_UNKNOWN;
207 static char *service2bearer(enum connman_service_type type)
210 case CONNMAN_SERVICE_TYPE_ETHERNET:
212 case CONNMAN_SERVICE_TYPE_WIFI:
214 case CONNMAN_SERVICE_TYPE_WIMAX:
216 case CONNMAN_SERVICE_TYPE_BLUETOOTH:
218 case CONNMAN_SERVICE_TYPE_CELLULAR:
220 case CONNMAN_SERVICE_TYPE_VPN:
222 case CONNMAN_SERVICE_TYPE_UNKNOWN:
223 case CONNMAN_SERVICE_TYPE_SYSTEM:
224 case CONNMAN_SERVICE_TYPE_GPS:
225 case CONNMAN_SERVICE_TYPE_GADGET:
232 static int policy_get_bool(struct connman_session *session, const char *id,
233 const char *key, connman_bool_t *val)
235 if (session->policy == NULL) {
240 return (*session->policy->get_bool)(session, key, val);
243 static int policy_get_string(struct connman_session *session, const char *id,
244 const char *key, char **val)
246 if (session->policy == NULL) {
251 return (*session->policy->get_string)(session, key, val);
254 static int assign_policy_plugin(struct connman_session *session)
257 struct connman_session_policy *policy;
259 if (session->policy != NULL)
262 for (list = policy_list; list != NULL; list = list->next) {
265 session->policy = policy;
272 static void probe_policy(struct connman_session_policy *policy)
277 struct connman_session *session;
279 DBG("policy %p name %s", policy, policy->name);
281 g_hash_table_iter_init(&iter, session_hash);
283 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
286 if (session->policy != NULL)
289 assign_policy_plugin(session);
293 static void remove_policy(struct connman_session_policy *policy)
297 struct connman_session *session;
299 DBG("policy %p name %s", policy, policy->name);
301 g_hash_table_iter_init(&iter, session_hash);
303 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
306 if (session->policy != policy)
309 session->policy = NULL;
310 assign_policy_plugin(session);
314 static gint compare_priority(gconstpointer a, gconstpointer b)
316 const struct connman_session_policy *policy1 = a;
317 const struct connman_session_policy *policy2 = b;
319 return policy2->priority - policy1->priority;
322 int connman_session_update_bool(struct connman_session *session, const char *key,
325 struct session_info *info;
330 info = session->info;
334 DBG("%s %d", key, val);
339 int connman_session_update_string(struct connman_session *session, const char *key,
342 struct session_info *info;
347 info = session->info;
351 DBG("%s %s", key, val);
356 int connman_session_policy_register(struct connman_session_policy *policy)
358 DBG("name %s", policy->name);
360 policy_list = g_slist_insert_sorted(policy_list, policy,
363 probe_policy(policy);
368 void connman_session_policy_unregister(struct connman_session_policy *policy)
370 DBG("name %s", policy->name);
372 policy_list = g_slist_remove(policy_list, policy);
374 remove_policy(policy);
377 static void cleanup_bearer(gpointer data, gpointer user_data)
379 struct connman_session_bearer *bearer = data;
381 g_free(bearer->name);
385 static GSList *session_parse_allowed_bearers(DBusMessageIter *iter)
387 struct connman_session_bearer *bearer;
388 DBusMessageIter array;
391 dbus_message_iter_recurse(iter, &array);
393 while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) {
394 char *bearer_name = NULL;
396 dbus_message_iter_get_basic(&array, &bearer_name);
398 bearer = g_try_new0(struct connman_session_bearer, 1);
399 if (bearer == NULL) {
400 g_slist_foreach(list, cleanup_bearer, NULL);
406 bearer->name = g_strdup(bearer_name);
407 bearer->service_type = bearer2service(bearer->name);
409 if (bearer->service_type == CONNMAN_SERVICE_TYPE_UNKNOWN &&
410 g_strcmp0(bearer->name, "*") == 0) {
411 bearer->match_all = TRUE;
413 bearer->match_all = FALSE;
416 list = g_slist_append(list, bearer);
418 dbus_message_iter_next(&array);
424 GSList *connman_session_allowed_bearers_any(void)
426 struct connman_session_bearer *bearer;
429 bearer = g_try_new0(struct connman_session_bearer, 1);
433 bearer->name = g_strdup("");
434 bearer->match_all = TRUE;
435 bearer->service_type = CONNMAN_SERVICE_TYPE_UNKNOWN;
437 list = g_slist_append(list, bearer);
442 static void append_allowed_bearers(DBusMessageIter *iter, void *user_data)
444 struct session_info *info = user_data;
447 for (list = info->config.allowed_bearers;
448 list != NULL; list = list->next) {
449 struct connman_session_bearer *bearer = list->data;
451 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
456 static void append_ipconfig_ipv4(DBusMessageIter *iter, void *user_data)
458 struct connman_service *service = user_data;
459 struct connman_ipconfig *ipconfig_ipv4;
464 if (__connman_service_is_connected_state(service,
465 CONNMAN_IPCONFIG_TYPE_IPV4) == FALSE) {
469 ipconfig_ipv4 = __connman_service_get_ip4config(service);
470 if (ipconfig_ipv4 == NULL)
473 __connman_ipconfig_append_ipv4(ipconfig_ipv4, iter);
476 static void append_ipconfig_ipv6(DBusMessageIter *iter, void *user_data)
478 struct connman_service *service = user_data;
479 struct connman_ipconfig *ipconfig_ipv4, *ipconfig_ipv6;
484 if (__connman_service_is_connected_state(service,
485 CONNMAN_IPCONFIG_TYPE_IPV6) == FALSE) {
489 ipconfig_ipv4 = __connman_service_get_ip4config(service);
490 ipconfig_ipv6 = __connman_service_get_ip6config(service);
491 if (ipconfig_ipv6 == NULL)
494 __connman_ipconfig_append_ipv6(ipconfig_ipv6, iter, ipconfig_ipv4);
497 static void append_notify(DBusMessageIter *dict,
498 struct connman_session *session)
500 struct session_info *info = session->info;
501 struct session_info *info_last = session->info_last;
502 struct connman_service *service;
503 const char *name, *ifname, *bearer;
505 if (session->append_all == TRUE ||
506 info->state != info_last->state) {
507 const char *state = state2string(info->state);
509 connman_dbus_dict_append_basic(dict, "State",
512 info_last->state = info->state;
515 if (session->append_all == TRUE ||
516 info->entry != info_last->entry) {
517 if (info->entry == NULL) {
523 name = info->entry->name;
524 ifname = info->entry->ifname;
525 service = info->entry->service;
526 bearer = info->entry->bearer;
529 connman_dbus_dict_append_basic(dict, "Name",
533 connman_dbus_dict_append_dict(dict, "IPv4",
534 append_ipconfig_ipv4,
537 connman_dbus_dict_append_dict(dict, "IPv6",
538 append_ipconfig_ipv6,
541 connman_dbus_dict_append_basic(dict, "Interface",
545 connman_dbus_dict_append_basic(dict, "Bearer",
549 info_last->entry = info->entry;
552 if (session->append_all == TRUE ||
553 info->config.type != info_last->config.type) {
554 const char *type = type2string(info->config.type);
556 connman_dbus_dict_append_basic(dict, "ConnectionType",
559 info_last->config.type = info->config.type;
562 if (session->append_all == TRUE ||
563 info->config.allowed_bearers != info_last->config.allowed_bearers) {
564 connman_dbus_dict_append_array(dict, "AllowedBearers",
566 append_allowed_bearers,
568 info_last->config.allowed_bearers = info->config.allowed_bearers;
571 session->append_all = FALSE;
574 static connman_bool_t is_type_matching_state(enum connman_session_state *state,
575 enum connman_session_type type)
578 case CONNMAN_SESSION_TYPE_ANY:
580 case CONNMAN_SESSION_TYPE_LOCAL:
581 if (*state >= CONNMAN_SESSION_STATE_CONNECTED) {
582 *state = CONNMAN_SESSION_STATE_CONNECTED;
587 case CONNMAN_SESSION_TYPE_INTERNET:
588 if (*state == CONNMAN_SESSION_STATE_ONLINE)
596 static connman_bool_t compute_notifiable_changes(struct connman_session *session)
598 struct session_info *info_last = session->info_last;
599 struct session_info *info = session->info;
601 if (session->append_all == TRUE)
604 if (info->state != info_last->state)
607 if (info->entry != info_last->entry &&
608 info->state >= CONNMAN_SESSION_STATE_CONNECTED)
611 if (info->config.allowed_bearers != info_last->config.allowed_bearers ||
612 info->config.type != info_last->config.type)
618 static gboolean session_notify(gpointer user_data)
620 struct connman_session *session = user_data;
622 DBusMessageIter array, dict;
624 if (compute_notifiable_changes(session) == FALSE)
627 DBG("session %p owner %s notify_path %s", session,
628 session->owner, session->notify_path);
630 msg = dbus_message_new_method_call(session->owner, session->notify_path,
631 CONNMAN_NOTIFICATION_INTERFACE,
636 dbus_message_iter_init_append(msg, &array);
637 connman_dbus_dict_open(&array, &dict);
639 append_notify(&dict, session);
641 connman_dbus_dict_close(&array, &dict);
643 g_dbus_send_message(connection, msg);
648 static void ipconfig_ipv4_changed(struct connman_session *session)
650 struct session_info *info = session->info;
652 connman_dbus_setting_changed_dict(session->owner, session->notify_path,
653 "IPv4", append_ipconfig_ipv4,
654 info->entry->service);
657 static void ipconfig_ipv6_changed(struct connman_session *session)
659 struct session_info *info = session->info;
661 connman_dbus_setting_changed_dict(session->owner, session->notify_path,
662 "IPv6", append_ipconfig_ipv6,
663 info->entry->service);
666 static connman_bool_t service_type_match(struct connman_session *session,
667 struct connman_service *service)
669 struct session_info *info = session->info;
672 for (list = info->config.allowed_bearers;
673 list != NULL; list = list->next) {
674 struct connman_session_bearer *bearer = list->data;
675 enum connman_service_type service_type;
677 if (bearer->match_all == TRUE)
680 service_type = connman_service_get_type(service);
681 if (bearer->service_type == service_type)
688 static connman_bool_t service_match(struct connman_session *session,
689 struct connman_service *service)
691 if (service_type_match(session, service) == FALSE)
697 static int service_type_weight(enum connman_service_type type)
700 * The session doesn't care which service
701 * to use. Nevertheless we have to sort them
702 * according their type. The ordering is
711 case CONNMAN_SERVICE_TYPE_ETHERNET:
713 case CONNMAN_SERVICE_TYPE_BLUETOOTH:
715 case CONNMAN_SERVICE_TYPE_WIFI:
716 case CONNMAN_SERVICE_TYPE_WIMAX:
718 case CONNMAN_SERVICE_TYPE_CELLULAR:
720 case CONNMAN_SERVICE_TYPE_UNKNOWN:
721 case CONNMAN_SERVICE_TYPE_SYSTEM:
722 case CONNMAN_SERVICE_TYPE_GPS:
723 case CONNMAN_SERVICE_TYPE_VPN:
724 case CONNMAN_SERVICE_TYPE_GADGET:
731 static gint sort_allowed_bearers(struct connman_service *service_a,
732 struct connman_service *service_b,
733 struct connman_session *session)
735 struct session_info *info = session->info;
737 enum connman_service_type type_a, type_b;
738 int weight_a, weight_b;
740 type_a = connman_service_get_type(service_a);
741 type_b = connman_service_get_type(service_b);
743 for (list = info->config.allowed_bearers;
744 list != NULL; list = list->next) {
745 struct connman_session_bearer *bearer = list->data;
747 if (bearer->match_all == TRUE) {
748 if (type_a != type_b) {
749 weight_a = service_type_weight(type_a);
750 weight_b = service_type_weight(type_b);
752 if (weight_a > weight_b)
755 if (weight_a < weight_b)
762 if (type_a == bearer->service_type &&
763 type_b == bearer->service_type) {
767 if (type_a == bearer->service_type &&
768 type_b != bearer->service_type) {
772 if (type_a != bearer->service_type &&
773 type_b == bearer->service_type) {
781 static gint sort_services(gconstpointer a, gconstpointer b, gpointer user_data)
783 struct service_entry *entry_a = (void *)a;
784 struct service_entry *entry_b = (void *)b;
785 struct connman_session *session = user_data;
787 return sort_allowed_bearers(entry_a->service, entry_b->service,
791 static void cleanup_session(gpointer user_data)
793 struct connman_session *session = user_data;
794 struct session_info *info = session->info;
796 DBG("remove %s", session->session_path);
798 g_hash_table_destroy(session->service_hash);
799 g_sequence_free(session->service_list);
801 if (info->entry != NULL &&
802 info->entry->reason == CONNMAN_SESSION_REASON_CONNECT) {
803 __connman_service_disconnect(info->entry->service);
806 g_slist_foreach(info->config.allowed_bearers, cleanup_bearer, NULL);
807 g_slist_free(info->config.allowed_bearers);
809 g_free(session->owner);
810 g_free(session->session_path);
811 g_free(session->notify_path);
812 g_free(session->info);
813 g_free(session->info_last);
818 static enum connman_session_state service_to_session_state(enum connman_service_state state)
821 case CONNMAN_SERVICE_STATE_UNKNOWN:
822 case CONNMAN_SERVICE_STATE_IDLE:
823 case CONNMAN_SERVICE_STATE_ASSOCIATION:
824 case CONNMAN_SERVICE_STATE_CONFIGURATION:
825 case CONNMAN_SERVICE_STATE_DISCONNECT:
826 case CONNMAN_SERVICE_STATE_FAILURE:
828 case CONNMAN_SERVICE_STATE_READY:
829 return CONNMAN_SESSION_STATE_CONNECTED;
830 case CONNMAN_SERVICE_STATE_ONLINE:
831 return CONNMAN_SESSION_STATE_ONLINE;
834 return CONNMAN_SESSION_STATE_DISCONNECTED;
837 static connman_bool_t is_connected(enum connman_service_state state)
840 case CONNMAN_SERVICE_STATE_UNKNOWN:
841 case CONNMAN_SERVICE_STATE_IDLE:
842 case CONNMAN_SERVICE_STATE_ASSOCIATION:
843 case CONNMAN_SERVICE_STATE_CONFIGURATION:
844 case CONNMAN_SERVICE_STATE_DISCONNECT:
845 case CONNMAN_SERVICE_STATE_FAILURE:
847 case CONNMAN_SERVICE_STATE_READY:
848 case CONNMAN_SERVICE_STATE_ONLINE:
855 static connman_bool_t is_connecting(enum connman_service_state state)
858 case CONNMAN_SERVICE_STATE_UNKNOWN:
859 case CONNMAN_SERVICE_STATE_IDLE:
861 case CONNMAN_SERVICE_STATE_ASSOCIATION:
862 case CONNMAN_SERVICE_STATE_CONFIGURATION:
864 case CONNMAN_SERVICE_STATE_DISCONNECT:
865 case CONNMAN_SERVICE_STATE_FAILURE:
866 case CONNMAN_SERVICE_STATE_READY:
867 case CONNMAN_SERVICE_STATE_ONLINE:
874 static connman_bool_t explicit_connect(enum connman_session_reason reason)
877 case CONNMAN_SESSION_REASON_UNKNOWN:
878 case CONNMAN_SESSION_REASON_FREE_RIDE:
880 case CONNMAN_SESSION_REASON_CONNECT:
887 static connman_bool_t explicit_disconnect(struct session_info *info)
889 if (info->entry == NULL)
892 DBG("reason %s service %p state %d",
893 reason2string(info->entry->reason),
894 info->entry->service, info->entry->state);
896 if (info->entry->reason == CONNMAN_SESSION_REASON_UNKNOWN)
899 if (explicit_connect(info->entry->reason) == FALSE)
902 if (__connman_service_session_dec(info->entry->service) == FALSE)
908 struct pending_data {
909 unsigned int timeout;
910 struct service_entry *entry;
911 gboolean (*cb)(gpointer);
914 static void pending_timeout_free(gpointer data, gpointer user_data)
916 struct pending_data *pending = data;
918 DBG("pending %p timeout %d", pending, pending->timeout);
919 g_source_remove(pending->timeout);
923 static void pending_timeout_remove_all(struct service_entry *entry)
927 g_slist_foreach(entry->pending_timeouts, pending_timeout_free, NULL);
928 g_slist_free(entry->pending_timeouts);
929 entry->pending_timeouts = NULL;
932 static gboolean pending_timeout_cb(gpointer data)
934 struct pending_data *pending = data;
935 struct service_entry *entry = pending->entry;
938 DBG("pending %p timeout %d", pending, pending->timeout);
940 ret = pending->cb(pending->entry);
942 entry->pending_timeouts =
943 g_slist_remove(entry->pending_timeouts,
950 static connman_bool_t pending_timeout_add(unsigned int seconds,
951 gboolean (*cb)(gpointer),
952 struct service_entry *entry)
954 struct pending_data *pending = g_try_new0(struct pending_data, 1);
956 if (pending == NULL || cb == NULL || entry == NULL) {
962 pending->entry = entry;
963 pending->timeout = g_timeout_add_seconds(seconds, pending_timeout_cb,
965 entry->pending_timeouts = g_slist_prepend(entry->pending_timeouts,
968 DBG("pending %p entry %p timeout id %d", pending, entry,
974 static gboolean call_disconnect(gpointer user_data)
976 struct service_entry *entry = user_data;
977 struct connman_service *service = entry->service;
980 * TODO: We should mark this entry as pending work. In case
981 * disconnect fails we just unassign this session from the
982 * service and can't do anything later on it
984 DBG("disconnect service %p", service);
985 __connman_service_disconnect(service);
990 static gboolean call_connect(gpointer user_data)
992 struct service_entry *entry = user_data;
993 struct connman_service *service = entry->service;
995 DBG("connect service %p", service);
996 __connman_service_connect(service);
1001 static void deselect_service(struct session_info *info)
1003 struct service_entry *entry;
1004 connman_bool_t disconnect, connected;
1008 if (info->entry == NULL)
1011 disconnect = explicit_disconnect(info);
1013 connected = is_connecting(info->entry->state) == TRUE ||
1014 is_connected(info->entry->state) == TRUE;
1016 info->state = CONNMAN_SESSION_STATE_DISCONNECTED;
1017 info->entry->reason = CONNMAN_SESSION_REASON_UNKNOWN;
1019 entry = info->entry;
1022 DBG("disconnect %d connected %d", disconnect, connected);
1024 if (disconnect == TRUE && connected == TRUE)
1025 pending_timeout_add(0, call_disconnect, entry);
1028 static void deselect_and_disconnect(struct connman_session *session)
1030 struct session_info *info = session->info;
1032 deselect_service(info);
1034 info->reason = CONNMAN_SESSION_REASON_FREE_RIDE;
1037 static void select_connected_service(struct session_info *info,
1038 struct service_entry *entry)
1040 enum connman_session_state state;
1042 state = service_to_session_state(entry->state);
1043 if (is_type_matching_state(&state, info->config.type) == FALSE)
1046 info->state = state;
1048 info->entry = entry;
1049 info->entry->reason = info->reason;
1051 if (explicit_connect(info->reason) == FALSE)
1054 __connman_service_session_inc(info->entry->service);
1057 static void select_offline_service(struct session_info *info,
1058 struct service_entry *entry)
1060 if (explicit_connect(info->reason) == FALSE)
1063 info->state = service_to_session_state(entry->state);
1065 info->entry = entry;
1066 info->entry->reason = info->reason;
1068 __connman_service_session_inc(info->entry->service);
1069 pending_timeout_add(0, call_connect, entry);
1072 static void select_service(struct session_info *info,
1073 struct service_entry *entry)
1075 DBG("service %p", entry->service);
1077 if (is_connected(entry->state) == TRUE)
1078 select_connected_service(info, entry);
1080 select_offline_service(info, entry);
1083 static void select_and_connect(struct connman_session *session,
1084 enum connman_session_reason reason)
1086 struct session_info *info = session->info;
1087 struct service_entry *entry = NULL;
1088 GSequenceIter *iter;
1090 DBG("session %p reason %s", session, reason2string(reason));
1092 info->reason = reason;
1094 iter = g_sequence_get_begin_iter(session->service_list);
1096 while (g_sequence_iter_is_end(iter) == FALSE) {
1097 entry = g_sequence_get(iter);
1099 switch (entry->state) {
1100 case CONNMAN_SERVICE_STATE_ASSOCIATION:
1101 case CONNMAN_SERVICE_STATE_CONFIGURATION:
1102 case CONNMAN_SERVICE_STATE_READY:
1103 case CONNMAN_SERVICE_STATE_ONLINE:
1104 case CONNMAN_SERVICE_STATE_IDLE:
1105 case CONNMAN_SERVICE_STATE_DISCONNECT:
1106 select_service(info, entry);
1108 case CONNMAN_SERVICE_STATE_UNKNOWN:
1109 case CONNMAN_SERVICE_STATE_FAILURE:
1113 iter = g_sequence_iter_next(iter);
1117 static struct service_entry *create_service_entry(struct connman_service *service,
1119 enum connman_service_state state)
1121 struct service_entry *entry;
1122 enum connman_service_type type;
1125 entry = g_try_new0(struct service_entry, 1);
1129 entry->reason = CONNMAN_SESSION_REASON_UNKNOWN;
1130 entry->state = state;
1135 entry->service = service;
1137 idx = __connman_service_get_index(entry->service);
1138 entry->ifname = connman_inet_ifname(idx);
1139 if (entry->ifname == NULL)
1140 entry->ifname = g_strdup("");
1142 type = connman_service_get_type(entry->service);
1143 entry->bearer = service2bearer(type);
1148 static void destroy_service_entry(gpointer data)
1150 struct service_entry *entry = data;
1152 pending_timeout_remove_all(entry);
1153 g_free(entry->ifname);
1158 static void populate_service_list(struct connman_session *session)
1160 struct service_entry *entry;
1161 GSequenceIter *iter;
1163 session->service_hash =
1164 g_hash_table_new_full(g_direct_hash, g_direct_equal,
1166 session->service_list = __connman_service_get_list(session,
1168 create_service_entry,
1169 destroy_service_entry);
1171 g_sequence_sort(session->service_list, sort_services, session);
1173 iter = g_sequence_get_begin_iter(session->service_list);
1175 while (g_sequence_iter_is_end(iter) == FALSE) {
1176 entry = g_sequence_get(iter);
1178 DBG("service %p type %s name %s", entry->service,
1179 service2bearer(connman_service_get_type(entry->service)),
1182 g_hash_table_replace(session->service_hash,
1183 entry->service, iter);
1185 iter = g_sequence_iter_next(iter);
1189 static void session_changed(struct connman_session *session,
1190 enum connman_session_trigger trigger)
1192 struct session_info *info = session->info;
1193 struct session_info *info_last = session->info_last;
1194 GSequenceIter *service_iter = NULL, *service_iter_last = NULL;
1195 GSequence *service_list_last;
1196 GHashTable *service_hash_last;
1199 * TODO: This only a placeholder for the 'real' algorithm to
1200 * play a bit around. So we are going to improve it step by step.
1203 DBG("session %p trigger %s reason %s", session, trigger2string(trigger),
1204 reason2string(info->reason));
1206 if (info->entry != NULL) {
1207 enum connman_session_state state;
1209 state = service_to_session_state(info->entry->state);
1211 if (is_type_matching_state(&state, info->config.type) == TRUE)
1212 info->state = state;
1216 case CONNMAN_SESSION_TRIGGER_UNKNOWN:
1217 DBG("ignore session changed event");
1219 case CONNMAN_SESSION_TRIGGER_SETTING:
1220 if (info->config.allowed_bearers != info_last->config.allowed_bearers) {
1222 service_hash_last = session->service_hash;
1223 service_list_last = session->service_list;
1225 populate_service_list(session);
1227 if (info->entry != NULL) {
1228 service_iter_last = g_hash_table_lookup(
1230 info->entry->service);
1231 service_iter = g_hash_table_lookup(
1232 session->service_hash,
1233 info->entry->service);
1236 if (service_iter == NULL && service_iter_last != NULL) {
1238 * The currently selected service is
1239 * not part of this session anymore.
1241 deselect_and_disconnect(session);
1244 g_hash_table_remove_all(service_hash_last);
1245 g_sequence_free(service_list_last);
1248 if (info->config.type != info_last->config.type) {
1249 if (info->state >= CONNMAN_SESSION_STATE_CONNECTED &&
1250 is_type_matching_state(&info->state,
1251 info->config.type) == FALSE)
1252 deselect_and_disconnect(session);
1255 if (info->state == CONNMAN_SESSION_STATE_DISCONNECTED) {
1256 select_and_connect(session,
1257 CONNMAN_SESSION_REASON_FREE_RIDE);
1261 case CONNMAN_SESSION_TRIGGER_ECALL:
1263 * For the time beeing we fallback to normal connect
1266 case CONNMAN_SESSION_TRIGGER_CONNECT:
1267 if (info->state >= CONNMAN_SESSION_STATE_CONNECTED) {
1268 if (info->entry->reason == CONNMAN_SESSION_REASON_CONNECT)
1270 info->entry->reason = CONNMAN_SESSION_REASON_CONNECT;
1271 __connman_service_session_inc(info->entry->service);
1275 if (info->entry != NULL &&
1276 is_connecting(info->entry->state) == TRUE) {
1280 select_and_connect(session,
1281 CONNMAN_SESSION_REASON_CONNECT);
1284 case CONNMAN_SESSION_TRIGGER_DISCONNECT:
1285 deselect_and_disconnect(session);
1288 case CONNMAN_SESSION_TRIGGER_SERVICE:
1289 if (info->entry != NULL &&
1290 (is_connecting(info->entry->state) == TRUE ||
1291 is_connected(info->entry->state) == TRUE)) {
1295 deselect_and_disconnect(session);
1297 if (info->reason == CONNMAN_SESSION_REASON_FREE_RIDE) {
1298 select_and_connect(session, info->reason);
1304 session_notify(session);
1307 static DBusMessage *connect_session(DBusConnection *conn,
1308 DBusMessage *msg, void *user_data)
1310 struct connman_session *session = user_data;
1312 DBG("session %p", session);
1314 if (ecall_session != NULL) {
1315 if (ecall_session->ecall == TRUE && ecall_session != session)
1316 return __connman_error_failed(msg, EBUSY);
1318 session->ecall = TRUE;
1319 session_changed(session, CONNMAN_SESSION_TRIGGER_ECALL);
1321 session_changed(session, CONNMAN_SESSION_TRIGGER_CONNECT);
1323 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1326 static DBusMessage *disconnect_session(DBusConnection *conn,
1327 DBusMessage *msg, void *user_data)
1329 struct connman_session *session = user_data;
1331 DBG("session %p", session);
1333 if (ecall_session != NULL) {
1334 if (ecall_session->ecall == TRUE && ecall_session != session)
1335 return __connman_error_failed(msg, EBUSY);
1337 session->ecall = FALSE;
1340 session_changed(session, CONNMAN_SESSION_TRIGGER_DISCONNECT);
1342 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1345 static DBusMessage *change_session(DBusConnection *conn,
1346 DBusMessage *msg, void *user_data)
1348 struct connman_session *session = user_data;
1349 struct session_info *info = session->info;
1350 DBusMessageIter iter, value;
1353 GSList *allowed_bearers;
1355 DBG("session %p", session);
1356 if (dbus_message_iter_init(msg, &iter) == FALSE)
1357 return __connman_error_invalid_arguments(msg);
1359 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
1360 return __connman_error_invalid_arguments(msg);
1362 dbus_message_iter_get_basic(&iter, &name);
1363 dbus_message_iter_next(&iter);
1365 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
1366 return __connman_error_invalid_arguments(msg);
1368 dbus_message_iter_recurse(&iter, &value);
1370 switch (dbus_message_iter_get_arg_type(&value)) {
1371 case DBUS_TYPE_ARRAY:
1372 if (g_str_equal(name, "AllowedBearers") == TRUE) {
1373 allowed_bearers = session_parse_allowed_bearers(&value);
1375 g_slist_foreach(info->config.allowed_bearers,
1376 cleanup_bearer, NULL);
1377 g_slist_free(info->config.allowed_bearers);
1379 if (allowed_bearers == NULL) {
1380 allowed_bearers = connman_session_allowed_bearers_any();
1382 if (allowed_bearers == NULL)
1383 return __connman_error_failed(msg, ENOMEM);
1386 info->config.allowed_bearers = allowed_bearers;
1391 case DBUS_TYPE_STRING:
1392 if (g_str_equal(name, "ConnectionType") == TRUE) {
1393 dbus_message_iter_get_basic(&value, &val);
1394 info->config.type = string2type(val);
1403 session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING);
1405 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1408 return __connman_error_invalid_arguments(msg);
1411 static void release_session(gpointer key, gpointer value, gpointer user_data)
1413 struct connman_session *session = value;
1414 DBusMessage *message;
1416 DBG("owner %s path %s", session->owner, session->notify_path);
1418 if (session->notify_watch > 0)
1419 g_dbus_remove_watch(connection, session->notify_watch);
1421 g_dbus_unregister_interface(connection, session->session_path,
1422 CONNMAN_SESSION_INTERFACE);
1424 message = dbus_message_new_method_call(session->owner,
1425 session->notify_path,
1426 CONNMAN_NOTIFICATION_INTERFACE,
1428 if (message == NULL)
1431 dbus_message_set_no_reply(message, TRUE);
1433 g_dbus_send_message(connection, message);
1436 static int session_disconnect(struct connman_session *session)
1438 DBG("session %p, %s", session, session->owner);
1440 if (session->notify_watch > 0)
1441 g_dbus_remove_watch(connection, session->notify_watch);
1443 g_dbus_unregister_interface(connection, session->session_path,
1444 CONNMAN_SESSION_INTERFACE);
1446 deselect_and_disconnect(session);
1448 g_hash_table_remove(session_hash, session->session_path);
1453 static void owner_disconnect(DBusConnection *conn, void *user_data)
1455 struct connman_session *session = user_data;
1457 DBG("session %p, %s died", session, session->owner);
1459 session_disconnect(session);
1462 static DBusMessage *destroy_session(DBusConnection *conn,
1463 DBusMessage *msg, void *user_data)
1465 struct connman_session *session = user_data;
1467 DBG("session %p", session);
1469 if (ecall_session != NULL && ecall_session != session)
1470 return __connman_error_failed(msg, EBUSY);
1472 session_disconnect(session);
1474 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1477 static const GDBusMethodTable session_methods[] = {
1478 { GDBUS_METHOD("Destroy", NULL, NULL, destroy_session) },
1479 { GDBUS_METHOD("Connect", NULL, NULL, connect_session) },
1480 { GDBUS_METHOD("Disconnect", NULL, NULL,
1481 disconnect_session ) },
1482 { GDBUS_METHOD("Change",
1483 GDBUS_ARGS({ "name", "s" }, { "value", "v" }),
1484 NULL, change_session) },
1488 int __connman_session_create(DBusMessage *msg)
1490 const char *owner, *notify_path;
1491 char *session_path = NULL;
1492 DBusMessageIter iter, array;
1493 struct connman_session *session = NULL;
1494 struct session_info *info, *info_last;
1495 enum connman_session_type type = CONNMAN_SESSION_TYPE_ANY;
1496 connman_bool_t priority;
1497 connman_bool_t ecall_app;
1498 enum connman_session_roaming_policy roaming_policy;
1499 char *roaming_policy_str;
1500 GSList *allowed_bearers = NULL;
1503 owner = dbus_message_get_sender(msg);
1505 DBG("owner %s", owner);
1507 if (ecall_session != NULL && ecall_session->ecall == TRUE) {
1509 * If there is an emergency call already going on,
1510 * ignore session creation attempt
1516 dbus_message_iter_init(msg, &iter);
1517 dbus_message_iter_recurse(&iter, &array);
1519 while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
1520 DBusMessageIter entry, value;
1521 const char *key, *val;
1523 dbus_message_iter_recurse(&array, &entry);
1524 dbus_message_iter_get_basic(&entry, &key);
1526 dbus_message_iter_next(&entry);
1527 dbus_message_iter_recurse(&entry, &value);
1529 switch (dbus_message_iter_get_arg_type(&value)) {
1530 case DBUS_TYPE_ARRAY:
1531 if (g_str_equal(key, "AllowedBearers") == TRUE) {
1533 session_parse_allowed_bearers(&value);
1538 case DBUS_TYPE_STRING:
1539 if (g_str_equal(key, "ConnectionType") == TRUE) {
1540 dbus_message_iter_get_basic(&value, &val);
1541 type = string2type(val);
1546 dbus_message_iter_next(&array);
1549 dbus_message_iter_next(&iter);
1550 dbus_message_iter_get_basic(&iter, ¬ify_path);
1552 if (notify_path == NULL) {
1557 session_path = g_strdup_printf("/sessions%s", notify_path);
1558 if (session_path == NULL) {
1563 session = g_hash_table_lookup(session_hash, session_path);
1564 if (session != NULL) {
1570 session = g_try_new0(struct connman_session, 1);
1571 if (session == NULL) {
1576 session->info = g_try_new0(struct session_info, 1);
1577 if (session->info == NULL) {
1582 session->info_last = g_try_new0(struct session_info, 1);
1583 if (session->info_last == NULL) {
1588 info = session->info;
1589 info_last = session->info_last;
1591 session->owner = g_strdup(owner);
1592 session->session_path = session_path;
1593 session->notify_path = g_strdup(notify_path);
1594 session->notify_watch =
1595 g_dbus_add_disconnect_watch(connection, session->owner,
1596 owner_disconnect, session, NULL);
1598 assign_policy_plugin(session);
1600 policy_get_bool(session, owner, "Priority", &priority);
1601 policy_get_bool(session, owner, "EmergencyCall", &ecall_app);
1602 policy_get_string(session, owner, "RoamingPolicy", &roaming_policy_str);
1603 roaming_policy = string2roamingpolicy(roaming_policy_str);
1605 if (ecall_app == TRUE)
1606 ecall_session = session;
1608 info->state = CONNMAN_SESSION_STATE_DISCONNECTED;
1609 info->config.type = type;
1610 info->config.priority = priority;
1611 info->config.roaming_policy = roaming_policy;
1614 if (allowed_bearers == NULL) {
1615 info->config.allowed_bearers =
1616 connman_session_allowed_bearers_any();
1618 if (info->config.allowed_bearers == NULL) {
1623 info->config.allowed_bearers = allowed_bearers;
1626 g_hash_table_replace(session_hash, session->session_path, session);
1628 DBG("add %s", session->session_path);
1630 if (g_dbus_register_interface(connection, session->session_path,
1631 CONNMAN_SESSION_INTERFACE,
1632 session_methods, NULL,
1633 NULL, session, NULL) == FALSE) {
1634 connman_error("Failed to register %s", session->session_path);
1635 g_hash_table_remove(session_hash, session->session_path);
1642 g_dbus_send_reply(connection, msg,
1643 DBUS_TYPE_OBJECT_PATH, &session->session_path,
1647 populate_service_list(session);
1649 info_last->state = info->state;
1650 info_last->config.priority = info->config.priority;
1651 info_last->config.roaming_policy = info->config.roaming_policy;
1652 info_last->entry = info->entry;
1653 info_last->config.allowed_bearers = info->config.allowed_bearers;
1655 session->append_all = TRUE;
1657 session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING);
1662 connman_error("Failed to create session");
1664 if (session != NULL) {
1665 if (session->info_last != NULL)
1666 g_free(session->info_last);
1667 if (session->info != NULL)
1668 g_free(session->info);
1672 g_free(session_path);
1674 g_slist_foreach(allowed_bearers, cleanup_bearer, NULL);
1675 g_slist_free(allowed_bearers);
1680 int __connman_session_destroy(DBusMessage *msg)
1682 const char *owner, *session_path;
1683 struct connman_session *session;
1685 owner = dbus_message_get_sender(msg);
1687 DBG("owner %s", owner);
1689 dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &session_path,
1691 if (session_path == NULL)
1694 session = g_hash_table_lookup(session_hash, session_path);
1695 if (session == NULL)
1698 if (g_strcmp0(owner, session->owner) != 0)
1701 session_disconnect(session);
1706 connman_bool_t __connman_session_mode()
1711 void __connman_session_set_mode(connman_bool_t enable)
1713 DBG("enable %d", enable);
1715 if (sessionmode != enable) {
1716 sessionmode = enable;
1718 connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
1719 CONNMAN_MANAGER_INTERFACE, "SessionMode",
1720 DBUS_TYPE_BOOLEAN, &sessionmode);
1723 if (sessionmode == TRUE)
1724 __connman_service_disconnect_all();
1727 static void service_add(struct connman_service *service,
1730 GHashTableIter iter;
1731 GSequenceIter *iter_service_list;
1732 gpointer key, value;
1733 struct connman_session *session;
1734 struct service_entry *entry;
1736 DBG("service %p", service);
1738 g_hash_table_iter_init(&iter, session_hash);
1740 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1743 if (service_match(session, service) == FALSE)
1746 entry = create_service_entry(service, name,
1747 CONNMAN_SERVICE_STATE_IDLE);
1752 g_sequence_insert_sorted(session->service_list,
1753 entry, sort_services,
1756 g_hash_table_replace(session->service_hash, service,
1759 session_changed(session, CONNMAN_SESSION_TRIGGER_SERVICE);
1763 static void service_remove(struct connman_service *service)
1766 GHashTableIter iter;
1767 gpointer key, value;
1768 struct connman_session *session;
1769 struct session_info *info;
1771 DBG("service %p", service);
1773 g_hash_table_iter_init(&iter, session_hash);
1775 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1776 GSequenceIter *seq_iter;
1778 info = session->info;
1780 seq_iter = g_hash_table_lookup(session->service_hash, service);
1781 if (seq_iter == NULL)
1784 g_sequence_remove(seq_iter);
1786 if (info->entry != NULL && info->entry->service == service)
1788 session_changed(session, CONNMAN_SESSION_TRIGGER_SERVICE);
1792 static void service_state_changed(struct connman_service *service,
1793 enum connman_service_state state)
1795 GHashTableIter iter;
1796 gpointer key, value;
1798 DBG("service %p state %d", service, state);
1800 g_hash_table_iter_init(&iter, session_hash);
1802 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1803 struct connman_session *session = value;
1804 GSequenceIter *service_iter;
1806 service_iter = g_hash_table_lookup(session->service_hash, service);
1807 if (service_iter != NULL) {
1808 struct service_entry *entry;
1810 entry = g_sequence_get(service_iter);
1811 entry->state = state;
1814 session_changed(session,
1815 CONNMAN_SESSION_TRIGGER_SERVICE);
1819 static void ipconfig_changed(struct connman_service *service,
1820 struct connman_ipconfig *ipconfig)
1822 GHashTableIter iter;
1823 gpointer key, value;
1824 struct connman_session *session;
1825 struct session_info *info;
1826 enum connman_ipconfig_type type;
1828 DBG("service %p ipconfig %p", service, ipconfig);
1830 type = __connman_ipconfig_get_config_type(ipconfig);
1832 g_hash_table_iter_init(&iter, session_hash);
1834 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1836 info = session->info;
1838 if (info->state == CONNMAN_SESSION_STATE_DISCONNECTED)
1841 if (info->entry != NULL && info->entry->service == service) {
1842 if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
1843 ipconfig_ipv4_changed(session);
1844 else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
1845 ipconfig_ipv6_changed(session);
1850 static struct connman_notifier session_notifier = {
1852 .service_add = service_add,
1853 .service_remove = service_remove,
1854 .service_state_changed = service_state_changed,
1855 .ipconfig_changed = ipconfig_changed,
1858 int __connman_session_init(void)
1864 connection = connman_dbus_get_connection();
1865 if (connection == NULL)
1868 err = connman_notifier_register(&session_notifier);
1870 dbus_connection_unref(connection);
1874 session_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1875 NULL, cleanup_session);
1877 sessionmode = FALSE;
1881 void __connman_session_cleanup(void)
1885 if (connection == NULL)
1888 connman_notifier_unregister(&session_notifier);
1890 g_hash_table_foreach(session_hash, release_session, NULL);
1891 g_hash_table_destroy(session_hash);
1892 session_hash = NULL;
1894 dbus_connection_unref(connection);