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_roaming_policy {
57 CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN = 0,
58 CONNMAN_SESSION_ROAMING_POLICY_DEFAULT = 1,
59 CONNMAN_SESSION_ROAMING_POLICY_ALWAYS = 2,
60 CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN = 3,
61 CONNMAN_SESSION_ROAMING_POLICY_NATIONAL = 4,
62 CONNMAN_SESSION_ROAMING_POLICY_INTERNATIONAL = 5,
65 struct service_entry {
66 /* track why this service was selected */
67 enum connman_session_reason reason;
68 enum connman_service_state state;
70 struct connman_service *service;
73 GSList *pending_timeouts;
77 connman_bool_t online;
78 connman_bool_t priority;
79 GSList *allowed_bearers;
80 connman_bool_t avoid_handover;
81 connman_bool_t stay_connected;
82 unsigned int periodic_connect;
83 unsigned int idle_timeout;
85 enum connman_session_roaming_policy roaming_policy;
88 struct service_entry *entry;
89 enum connman_session_reason reason;
92 struct connman_session {
98 connman_bool_t append_all;
99 connman_bool_t info_dirty;
100 struct session_info *info;
101 struct session_info *info_last;
103 GSequence *service_list;
104 GHashTable *service_hash;
109 connman_bool_t match_all;
110 enum connman_service_type service_type;
113 static const char *trigger2string(enum connman_session_trigger trigger)
116 case CONNMAN_SESSION_TRIGGER_UNKNOWN:
118 case CONNMAN_SESSION_TRIGGER_SETTING:
120 case CONNMAN_SESSION_TRIGGER_CONNECT:
122 case CONNMAN_SESSION_TRIGGER_DISCONNECT:
124 case CONNMAN_SESSION_TRIGGER_PERIODIC:
126 case CONNMAN_SESSION_TRIGGER_SERVICE:
128 case CONNMAN_SESSION_TRIGGER_ECALL:
135 static const char *reason2string(enum connman_session_reason reason)
138 case CONNMAN_SESSION_REASON_UNKNOWN:
140 case CONNMAN_SESSION_REASON_CONNECT:
142 case CONNMAN_SESSION_REASON_DISCONNECT:
144 case CONNMAN_SESSION_REASON_FREE_RIDE:
146 case CONNMAN_SESSION_REASON_PERIODIC:
153 static const char *roamingpolicy2string(enum connman_session_roaming_policy policy)
156 case CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN:
158 case CONNMAN_SESSION_ROAMING_POLICY_DEFAULT:
160 case CONNMAN_SESSION_ROAMING_POLICY_ALWAYS:
162 case CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN:
164 case CONNMAN_SESSION_ROAMING_POLICY_NATIONAL:
166 case CONNMAN_SESSION_ROAMING_POLICY_INTERNATIONAL:
167 return "international";
173 static enum connman_session_roaming_policy string2roamingpolicy(const char *policy)
175 if (g_strcmp0(policy, "default") == 0)
176 return CONNMAN_SESSION_ROAMING_POLICY_DEFAULT;
177 else if (g_strcmp0(policy, "always") == 0)
178 return CONNMAN_SESSION_ROAMING_POLICY_ALWAYS;
179 else if (g_strcmp0(policy, "forbidden") == 0)
180 return CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN;
181 else if (g_strcmp0(policy, "national") == 0)
182 return CONNMAN_SESSION_ROAMING_POLICY_NATIONAL;
183 else if (g_strcmp0(policy, "international") == 0)
184 return CONNMAN_SESSION_ROAMING_POLICY_INTERNATIONAL;
186 return CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN;
189 static enum connman_service_type bearer2service(const char *bearer)
192 return CONNMAN_SERVICE_TYPE_UNKNOWN;
194 if (g_strcmp0(bearer, "ethernet") == 0)
195 return CONNMAN_SERVICE_TYPE_ETHERNET;
196 else if (g_strcmp0(bearer, "wifi") == 0)
197 return CONNMAN_SERVICE_TYPE_WIFI;
198 else if (g_strcmp0(bearer, "wimax") == 0)
199 return CONNMAN_SERVICE_TYPE_WIMAX;
200 else if (g_strcmp0(bearer, "bluetooth") == 0)
201 return CONNMAN_SERVICE_TYPE_BLUETOOTH;
202 else if (g_strcmp0(bearer, "cellular") == 0)
203 return CONNMAN_SERVICE_TYPE_CELLULAR;
204 else if (g_strcmp0(bearer, "vpn") == 0)
205 return CONNMAN_SERVICE_TYPE_VPN;
207 return CONNMAN_SERVICE_TYPE_UNKNOWN;
210 static char *service2bearer(enum connman_service_type type)
213 case CONNMAN_SERVICE_TYPE_ETHERNET:
215 case CONNMAN_SERVICE_TYPE_WIFI:
217 case CONNMAN_SERVICE_TYPE_WIMAX:
219 case CONNMAN_SERVICE_TYPE_BLUETOOTH:
221 case CONNMAN_SERVICE_TYPE_CELLULAR:
223 case CONNMAN_SERVICE_TYPE_VPN:
225 case CONNMAN_SERVICE_TYPE_UNKNOWN:
226 case CONNMAN_SERVICE_TYPE_SYSTEM:
227 case CONNMAN_SERVICE_TYPE_GPS:
228 case CONNMAN_SERVICE_TYPE_GADGET:
235 static void cleanup_bearer_info(gpointer data, gpointer user_data)
237 struct bearer_info *info = data;
243 static GSList *session_parse_allowed_bearers(DBusMessageIter *iter)
245 struct bearer_info *info;
246 DBusMessageIter array;
249 dbus_message_iter_recurse(iter, &array);
251 while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) {
254 dbus_message_iter_get_basic(&array, &bearer);
256 info = g_try_new0(struct bearer_info, 1);
258 g_slist_foreach(list, cleanup_bearer_info, NULL);
264 info->name = g_strdup(bearer);
265 info->service_type = bearer2service(info->name);
267 if (info->service_type == CONNMAN_SERVICE_TYPE_UNKNOWN &&
268 g_strcmp0(info->name, "*") == 0) {
269 info->match_all = TRUE;
271 info->match_all = FALSE;
274 list = g_slist_append(list, info);
276 dbus_message_iter_next(&array);
282 static GSList *session_allowed_bearers_any(void)
284 struct bearer_info *info;
287 info = g_try_new0(struct bearer_info, 1);
294 info->name = g_strdup("");
295 info->match_all = TRUE;
296 info->service_type = CONNMAN_SERVICE_TYPE_UNKNOWN;
298 list = g_slist_append(list, info);
303 static void append_allowed_bearers(DBusMessageIter *iter, void *user_data)
305 struct session_info *info = user_data;
308 for (list = info->allowed_bearers;
309 list != NULL; list = list->next) {
310 struct bearer_info *info = list->data;
312 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
317 static void append_ipconfig_ipv4(DBusMessageIter *iter, void *user_data)
319 struct connman_service *service = user_data;
320 struct connman_ipconfig *ipconfig_ipv4;
325 ipconfig_ipv4 = __connman_service_get_ip4config(service);
326 if (ipconfig_ipv4 == NULL)
329 __connman_ipconfig_append_ipv4(ipconfig_ipv4, iter);
332 static void append_ipconfig_ipv6(DBusMessageIter *iter, void *user_data)
334 struct connman_service *service = user_data;
335 struct connman_ipconfig *ipconfig_ipv4, *ipconfig_ipv6;
340 ipconfig_ipv4 = __connman_service_get_ip4config(service);
341 ipconfig_ipv6 = __connman_service_get_ip6config(service);
342 if (ipconfig_ipv6 == NULL)
345 __connman_ipconfig_append_ipv6(ipconfig_ipv6, iter, ipconfig_ipv4);
348 static void append_notify(DBusMessageIter *dict,
349 struct connman_session *session)
351 struct session_info *info = session->info;
352 struct session_info *info_last = session->info_last;
354 struct connman_service *service;
355 const char *name, *ifname, *bearer;
357 if (session->append_all == TRUE ||
358 info->online != info_last->online) {
359 connman_dbus_dict_append_basic(dict, "Online",
362 info_last->online = info->online;
365 if (session->append_all == TRUE ||
366 info->entry != info_last->entry) {
367 if (info->entry == NULL) {
373 name = info->entry->name;
374 ifname = info->entry->ifname;
375 service = info->entry->service;
376 bearer = info->entry->bearer;
379 connman_dbus_dict_append_basic(dict, "Name",
383 connman_dbus_dict_append_dict(dict, "IPv4",
384 append_ipconfig_ipv4,
387 connman_dbus_dict_append_dict(dict, "IPv6",
388 append_ipconfig_ipv6,
391 connman_dbus_dict_append_basic(dict, "Interface",
395 connman_dbus_dict_append_basic(dict, "Bearer",
399 info_last->entry = info->entry;
403 if (session->append_all == TRUE ||
404 info->priority != info_last->priority) {
405 connman_dbus_dict_append_basic(dict, "Priority",
408 info_last->priority = info->priority;
411 if (session->append_all == TRUE ||
412 info->allowed_bearers != info_last->allowed_bearers) {
413 connman_dbus_dict_append_array(dict, "AllowedBearers",
415 append_allowed_bearers,
417 info_last->allowed_bearers = info->allowed_bearers;
420 if (session->append_all == TRUE ||
421 info->avoid_handover != info_last->avoid_handover) {
422 connman_dbus_dict_append_basic(dict, "AvoidHandover",
424 &info->avoid_handover);
425 info_last->avoid_handover = info->avoid_handover;
428 if (session->append_all == TRUE ||
429 info->stay_connected != info_last->stay_connected) {
430 connman_dbus_dict_append_basic(dict, "StayConnected",
432 &info->stay_connected);
433 info_last->stay_connected = info->stay_connected;
436 if (session->append_all == TRUE ||
437 info->periodic_connect != info_last->periodic_connect) {
438 connman_dbus_dict_append_basic(dict, "PeriodicConnect",
440 &info->periodic_connect);
441 info_last->periodic_connect = info->periodic_connect;
444 if (session->append_all == TRUE ||
445 info->idle_timeout != info_last->idle_timeout) {
446 connman_dbus_dict_append_basic(dict, "IdleTimeout",
448 &info->idle_timeout);
449 info_last->idle_timeout = info->idle_timeout;
452 if (session->append_all == TRUE ||
453 info->ecall != info_last->ecall) {
454 connman_dbus_dict_append_basic(dict, "EmergencyCall",
457 info_last->ecall = info->ecall;
460 if (session->append_all == TRUE ||
461 info->roaming_policy != info_last->roaming_policy) {
462 policy = roamingpolicy2string(info->roaming_policy);
463 connman_dbus_dict_append_basic(dict, "RoamingPolicy",
466 info_last->roaming_policy = info->roaming_policy;
469 if (session->append_all == TRUE ||
470 info->marker != info_last->marker) {
471 connman_dbus_dict_append_basic(dict, "SessionMarker",
474 info_last->marker = info->marker;
477 session->append_all = FALSE;
478 session->info_dirty = FALSE;
481 static gboolean session_notify(gpointer user_data)
483 struct connman_session *session = user_data;
485 DBusMessageIter array, dict;
487 if (session->info_dirty == FALSE)
490 DBG("session %p owner %s notify_path %s", session,
491 session->owner, session->notify_path);
493 msg = dbus_message_new_method_call(session->owner, session->notify_path,
494 CONNMAN_NOTIFICATION_INTERFACE,
499 dbus_message_iter_init_append(msg, &array);
500 connman_dbus_dict_open(&array, &dict);
502 append_notify(&dict, session);
504 connman_dbus_dict_close(&array, &dict);
506 g_dbus_send_message(connection, msg);
511 static void ipconfig_ipv4_changed(struct connman_session *session)
513 struct session_info *info = session->info;
515 connman_dbus_setting_changed_dict(session->owner, session->notify_path,
516 "IPv4", append_ipconfig_ipv4,
517 info->entry->service);
520 static void ipconfig_ipv6_changed(struct connman_session *session)
522 struct session_info *info = session->info;
524 connman_dbus_setting_changed_dict(session->owner, session->notify_path,
525 "IPv6", append_ipconfig_ipv6,
526 info->entry->service);
529 static connman_bool_t service_type_match(struct connman_session *session,
530 struct connman_service *service)
532 struct session_info *info = session->info;
535 for (list = info->allowed_bearers;
536 list != NULL; list = list->next) {
537 struct bearer_info *info = list->data;
538 enum connman_service_type service_type;
540 if (info->match_all == TRUE)
543 service_type = connman_service_get_type(service);
544 if (info->service_type == service_type)
551 static connman_bool_t service_match(struct connman_session *session,
552 struct connman_service *service)
554 if (service_type_match(session, service) == FALSE)
560 static int service_type_weight(enum connman_service_type type)
563 * The session doesn't care which service
564 * to use. Nevertheless we have to sort them
565 * according their type. The ordering is
574 case CONNMAN_SERVICE_TYPE_ETHERNET:
576 case CONNMAN_SERVICE_TYPE_BLUETOOTH:
578 case CONNMAN_SERVICE_TYPE_WIFI:
579 case CONNMAN_SERVICE_TYPE_WIMAX:
581 case CONNMAN_SERVICE_TYPE_CELLULAR:
583 case CONNMAN_SERVICE_TYPE_UNKNOWN:
584 case CONNMAN_SERVICE_TYPE_SYSTEM:
585 case CONNMAN_SERVICE_TYPE_GPS:
586 case CONNMAN_SERVICE_TYPE_VPN:
587 case CONNMAN_SERVICE_TYPE_GADGET:
594 static gint sort_allowed_bearers(struct connman_service *service_a,
595 struct connman_service *service_b,
596 struct connman_session *session)
598 struct session_info *info = session->info;
600 enum connman_service_type type_a, type_b;
601 int weight_a, weight_b;
603 type_a = connman_service_get_type(service_a);
604 type_b = connman_service_get_type(service_b);
606 for (list = info->allowed_bearers;
607 list != NULL; list = list->next) {
608 struct bearer_info *info = list->data;
610 if (info->match_all == TRUE) {
611 if (type_a != type_b) {
612 weight_a = service_type_weight(type_a);
613 weight_b = service_type_weight(type_b);
615 if (weight_a > weight_b)
618 if (weight_a < weight_b)
625 if (type_a == info->service_type &&
626 type_b == info->service_type) {
630 if (type_a == info->service_type &&
631 type_b != info->service_type) {
635 if (type_a != info->service_type &&
636 type_b == info->service_type) {
644 static gint sort_services(gconstpointer a, gconstpointer b, gpointer user_data)
646 struct service_entry *entry_a = (void *)a;
647 struct service_entry *entry_b = (void *)b;
648 struct connman_session *session = user_data;
650 return sort_allowed_bearers(entry_a->service, entry_b->service,
654 static void cleanup_session(gpointer user_data)
656 struct connman_session *session = user_data;
657 struct session_info *info = session->info;
659 DBG("remove %s", session->session_path);
661 g_hash_table_destroy(session->service_hash);
662 g_sequence_free(session->service_list);
664 if (info->entry != NULL &&
665 info->entry->reason == CONNMAN_SESSION_REASON_CONNECT) {
666 __connman_service_disconnect(info->entry->service);
669 g_slist_foreach(info->allowed_bearers, cleanup_bearer_info, NULL);
670 g_slist_free(info->allowed_bearers);
672 g_free(session->owner);
673 g_free(session->session_path);
674 g_free(session->notify_path);
675 g_free(session->info);
676 g_free(session->info_last);
681 static connman_bool_t is_online(enum connman_service_state state)
684 case CONNMAN_SERVICE_STATE_UNKNOWN:
685 case CONNMAN_SERVICE_STATE_IDLE:
686 case CONNMAN_SERVICE_STATE_ASSOCIATION:
687 case CONNMAN_SERVICE_STATE_CONFIGURATION:
688 case CONNMAN_SERVICE_STATE_DISCONNECT:
689 case CONNMAN_SERVICE_STATE_FAILURE:
690 case CONNMAN_SERVICE_STATE_READY:
692 case CONNMAN_SERVICE_STATE_ONLINE:
699 static connman_bool_t is_connecting(enum connman_service_state state)
702 case CONNMAN_SERVICE_STATE_UNKNOWN:
703 case CONNMAN_SERVICE_STATE_IDLE:
705 case CONNMAN_SERVICE_STATE_ASSOCIATION:
706 case CONNMAN_SERVICE_STATE_CONFIGURATION:
707 case CONNMAN_SERVICE_STATE_READY:
709 case CONNMAN_SERVICE_STATE_DISCONNECT:
710 case CONNMAN_SERVICE_STATE_FAILURE:
711 case CONNMAN_SERVICE_STATE_ONLINE:
718 static connman_bool_t explicit_connect(enum connman_session_reason reason)
721 case CONNMAN_SESSION_REASON_UNKNOWN:
722 case CONNMAN_SESSION_REASON_FREE_RIDE:
723 case CONNMAN_SESSION_REASON_DISCONNECT:
725 case CONNMAN_SESSION_REASON_CONNECT:
726 case CONNMAN_SESSION_REASON_PERIODIC:
733 static connman_bool_t explicit_disconnect(struct session_info *info)
735 if (info->entry == NULL)
738 DBG("reason %s service %p state %d",
739 reason2string(info->entry->reason),
740 info->entry->service, info->entry->state);
742 if (info->entry->reason == CONNMAN_SESSION_REASON_UNKNOWN)
745 if (explicit_connect(info->entry->reason) == FALSE)
748 if (__connman_service_session_dec(info->entry->service) == FALSE)
751 if (ecall_info != NULL && ecall_info != info)
757 struct pending_data {
758 unsigned int timeout;
759 struct service_entry *entry;
760 gboolean (*cb)(gpointer);
763 static void pending_timeout_free(gpointer data, gpointer user_data)
765 struct pending_data *pending = data;
767 DBG("pending %p timeout %d", pending, pending->timeout);
768 g_source_remove(pending->timeout);
772 static void pending_timeout_remove_all(struct service_entry *entry)
776 g_slist_foreach(entry->pending_timeouts, pending_timeout_free, NULL);
777 g_slist_free(entry->pending_timeouts);
778 entry->pending_timeouts = NULL;
781 static gboolean pending_timeout_cb(gpointer data)
783 struct pending_data *pending = data;
784 struct service_entry *entry = pending->entry;
787 DBG("pending %p timeout %d", pending, pending->timeout);
789 ret = pending->cb(pending->entry);
791 entry->pending_timeouts =
792 g_slist_remove(entry->pending_timeouts,
799 static connman_bool_t pending_timeout_add(unsigned int seconds,
800 gboolean (*cb)(gpointer),
801 struct service_entry *entry)
803 struct pending_data *pending = g_try_new0(struct pending_data, 1);
805 if (pending == NULL || cb == NULL || entry == NULL) {
811 pending->entry = entry;
812 pending->timeout = g_timeout_add_seconds(seconds, pending_timeout_cb,
814 entry->pending_timeouts = g_slist_prepend(entry->pending_timeouts,
817 DBG("pending %p entry %p timeout id %d", pending, entry,
823 static gboolean call_disconnect(gpointer user_data)
825 struct service_entry *entry = user_data;
826 struct connman_service *service = entry->service;
829 * TODO: We should mark this entry as pending work. In case
830 * disconnect fails we just unassign this session from the
831 * service and can't do anything later on it
833 DBG("disconnect service %p", service);
834 __connman_service_disconnect(service);
839 static gboolean call_connect(gpointer user_data)
841 struct service_entry *entry = user_data;
842 struct connman_service *service = entry->service;
844 DBG("connect service %p", service);
845 __connman_service_connect(service);
850 static connman_bool_t deselect_service(struct session_info *info)
852 struct service_entry *entry;
853 connman_bool_t disconnect, online;
857 if (info->entry == NULL)
860 disconnect = explicit_disconnect(info);
862 online = is_connecting(info->entry->state) == TRUE ||
863 is_online(info->entry->state) == TRUE;
865 info->online = FALSE;
866 info->entry->reason = CONNMAN_SESSION_REASON_UNKNOWN;
871 DBG("disconnect %d online %d", disconnect, online);
873 if (disconnect == TRUE && online == TRUE)
874 pending_timeout_add(0, call_disconnect, entry);
879 static void deselect_and_disconnect(struct connman_session *session,
880 enum connman_session_reason reason)
882 struct session_info *info = session->info;
884 session->info_dirty |= deselect_service(info);
886 info->reason = reason;
889 static connman_bool_t select_online_service(struct session_info *info,
890 struct service_entry *entry)
895 info->entry->reason = info->reason;
897 if (explicit_connect(info->reason) == FALSE)
900 __connman_service_session_inc(info->entry->service);
905 static connman_bool_t select_offline_service(struct session_info *info,
906 struct service_entry *entry)
908 if (explicit_connect(info->reason) == FALSE)
911 info->online = FALSE;
914 info->entry->reason = info->reason;
916 __connman_service_session_inc(info->entry->service);
917 pending_timeout_add(0, call_connect, entry);
922 static connman_bool_t select_service(struct session_info *info,
923 struct service_entry *entry)
925 DBG("service %p", entry->service);
927 if (is_online(entry->state) == TRUE)
928 return select_online_service(info, entry);
930 return select_offline_service(info, entry);
933 static void select_and_connect(struct connman_session *session,
934 enum connman_session_reason reason)
936 struct session_info *info = session->info;
937 struct service_entry *entry = NULL;
940 DBG("session %p reason %s", session, reason2string(reason));
942 info->reason = reason;
944 iter = g_sequence_get_begin_iter(session->service_list);
946 while (g_sequence_iter_is_end(iter) == FALSE) {
947 entry = g_sequence_get(iter);
949 switch (entry->state) {
950 case CONNMAN_SERVICE_STATE_ASSOCIATION:
951 case CONNMAN_SERVICE_STATE_CONFIGURATION:
952 case CONNMAN_SERVICE_STATE_READY:
953 case CONNMAN_SERVICE_STATE_ONLINE:
954 case CONNMAN_SERVICE_STATE_IDLE:
955 case CONNMAN_SERVICE_STATE_DISCONNECT:
956 session->info_dirty |=
957 select_service(info, entry);
959 case CONNMAN_SERVICE_STATE_UNKNOWN:
960 case CONNMAN_SERVICE_STATE_FAILURE:
964 iter = g_sequence_iter_next(iter);
968 static struct service_entry *create_service_entry(struct connman_service *service,
970 enum connman_service_state state)
972 struct service_entry *entry;
973 enum connman_service_type type;
976 entry = g_try_new0(struct service_entry, 1);
980 entry->reason = CONNMAN_SESSION_REASON_UNKNOWN;
981 entry->state = state;
986 entry->service = service;
988 idx = __connman_service_get_index(entry->service);
989 entry->ifname = connman_inet_ifname(idx);
990 if (entry->ifname == NULL)
991 entry->ifname = g_strdup("");
993 type = connman_service_get_type(entry->service);
994 entry->bearer = service2bearer(type);
999 static void destroy_service_entry(gpointer data)
1001 struct service_entry *entry = data;
1003 pending_timeout_remove_all(entry);
1004 g_free(entry->ifname);
1009 static void populate_service_list(struct connman_session *session)
1011 struct service_entry *entry;
1012 GSequenceIter *iter;
1014 session->service_hash =
1015 g_hash_table_new_full(g_direct_hash, g_direct_equal,
1017 session->service_list = __connman_service_get_list(session,
1019 create_service_entry,
1020 destroy_service_entry);
1022 g_sequence_sort(session->service_list, sort_services, session);
1024 iter = g_sequence_get_begin_iter(session->service_list);
1026 while (g_sequence_iter_is_end(iter) == FALSE) {
1027 entry = g_sequence_get(iter);
1029 DBG("service %p type %s name %s", entry->service,
1030 service2bearer(connman_service_get_type(entry->service)),
1033 g_hash_table_replace(session->service_hash,
1034 entry->service, iter);
1036 iter = g_sequence_iter_next(iter);
1040 static void session_changed(struct connman_session *session,
1041 enum connman_session_trigger trigger)
1043 struct session_info *info = session->info;
1044 struct session_info *info_last = session->info_last;
1045 GSequenceIter *service_iter = NULL, *service_iter_last = NULL;
1046 GSequence *service_list_last;
1047 GHashTable *service_hash_last;
1050 * TODO: This only a placeholder for the 'real' algorithm to
1051 * play a bit around. So we are going to improve it step by step.
1054 DBG("session %p trigger %s reason %s", session, trigger2string(trigger),
1055 reason2string(info->reason));
1057 if (info->entry != NULL) {
1058 info->online = is_online(info->entry->state);
1059 if (info_last->online != info->online)
1060 session->info_dirty = TRUE;
1064 case CONNMAN_SESSION_TRIGGER_UNKNOWN:
1065 DBG("ignore session changed event");
1067 case CONNMAN_SESSION_TRIGGER_SETTING:
1068 if (info->allowed_bearers != info_last->allowed_bearers) {
1070 service_hash_last = session->service_hash;
1071 service_list_last = session->service_list;
1073 populate_service_list(session);
1075 if (info->entry != NULL) {
1076 service_iter_last = g_hash_table_lookup(
1078 info->entry->service);
1079 service_iter = g_hash_table_lookup(
1080 session->service_hash,
1081 info->entry->service);
1084 if (service_iter == NULL && service_iter_last != NULL) {
1086 * The currently selected service is
1087 * not part of this session anymore.
1089 deselect_and_disconnect(session, info->reason);
1092 g_hash_table_remove_all(service_hash_last);
1093 g_sequence_free(service_list_last);
1096 if (info->online == FALSE) {
1097 select_and_connect(session,
1098 CONNMAN_SESSION_REASON_FREE_RIDE);
1102 case CONNMAN_SESSION_TRIGGER_CONNECT:
1103 if (info->online == TRUE) {
1104 if (info->entry->reason == CONNMAN_SESSION_REASON_CONNECT)
1106 info->entry->reason = CONNMAN_SESSION_REASON_CONNECT;
1107 __connman_service_session_inc(info->entry->service);
1111 if (info->entry != NULL &&
1112 is_connecting(info->entry->state) == TRUE) {
1116 select_and_connect(session,
1117 CONNMAN_SESSION_REASON_CONNECT);
1120 case CONNMAN_SESSION_TRIGGER_DISCONNECT:
1121 deselect_and_disconnect(session,
1122 CONNMAN_SESSION_REASON_DISCONNECT);
1125 case CONNMAN_SESSION_TRIGGER_PERIODIC:
1126 if (info->online == TRUE) {
1127 info->entry->reason = CONNMAN_SESSION_REASON_PERIODIC;
1128 __connman_service_session_inc(info->entry->service);
1132 select_and_connect(session,
1133 CONNMAN_SESSION_REASON_PERIODIC);
1136 case CONNMAN_SESSION_TRIGGER_SERVICE:
1137 if (info->entry != NULL &&
1138 (is_connecting(info->entry->state) == TRUE ||
1139 is_online(info->entry->state) == TRUE)) {
1143 deselect_and_disconnect(session, info->reason);
1145 if (info->reason == CONNMAN_SESSION_REASON_FREE_RIDE ||
1146 info->stay_connected == TRUE) {
1147 select_and_connect(session, info->reason);
1151 case CONNMAN_SESSION_TRIGGER_ECALL:
1152 if (info->online == FALSE && info->entry != NULL &&
1153 info->entry->service != NULL) {
1154 deselect_and_disconnect(session, info->reason);
1160 session_notify(session);
1163 static DBusMessage *connect_session(DBusConnection *conn,
1164 DBusMessage *msg, void *user_data)
1166 struct connman_session *session = user_data;
1167 struct session_info *info = session->info;
1169 DBG("session %p", session);
1171 if (ecall_info != NULL && ecall_info != info)
1172 return __connman_error_failed(msg, EBUSY);
1174 session_changed(session, CONNMAN_SESSION_TRIGGER_CONNECT);
1176 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1179 static DBusMessage *disconnect_session(DBusConnection *conn,
1180 DBusMessage *msg, void *user_data)
1182 struct connman_session *session = user_data;
1183 struct session_info *info = session->info;
1185 DBG("session %p", session);
1187 if (ecall_info != NULL && ecall_info != info)
1188 return __connman_error_failed(msg, EBUSY);
1190 session_changed(session, CONNMAN_SESSION_TRIGGER_DISCONNECT);
1192 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1195 static void update_ecall_sessions(struct connman_session *session)
1197 struct session_info *info = session->info;
1198 struct connman_session *session_iter;
1199 GHashTableIter iter;
1200 gpointer key, value;
1202 g_hash_table_iter_init(&iter, session_hash);
1204 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1205 session_iter = value;
1207 if (session_iter == session)
1210 session_iter->info->ecall = info->ecall;
1211 session_iter->info_dirty = TRUE;
1213 session_changed(session_iter, CONNMAN_SESSION_TRIGGER_ECALL);
1217 static void update_ecall(struct connman_session *session)
1219 struct session_info *info = session->info;
1220 struct session_info *info_last = session->info_last;
1222 DBG("session %p ecall_info %p ecall %d -> %d", session,
1223 ecall_info, info_last->ecall, info->ecall);
1225 if (ecall_info == NULL) {
1226 if (!(info_last->ecall == FALSE && info->ecall == TRUE))
1230 } else if (ecall_info == info) {
1231 if (!(info_last->ecall == TRUE && info->ecall == FALSE))
1239 update_ecall_sessions(session);
1241 session->info_dirty = TRUE;
1245 /* not a valid transition */
1246 info->ecall = info_last->ecall;
1249 static DBusMessage *change_session(DBusConnection *conn,
1250 DBusMessage *msg, void *user_data)
1252 struct connman_session *session = user_data;
1253 struct session_info *info = session->info;
1254 struct session_info *info_last = session->info_last;
1255 DBusMessageIter iter, value;
1257 GSList *allowed_bearers;
1259 DBG("session %p", session);
1260 if (dbus_message_iter_init(msg, &iter) == FALSE)
1261 return __connman_error_invalid_arguments(msg);
1263 dbus_message_iter_get_basic(&iter, &name);
1264 dbus_message_iter_next(&iter);
1265 dbus_message_iter_recurse(&iter, &value);
1267 switch (dbus_message_iter_get_arg_type(&value)) {
1268 case DBUS_TYPE_ARRAY:
1269 if (g_str_equal(name, "AllowedBearers") == TRUE) {
1270 allowed_bearers = session_parse_allowed_bearers(&value);
1272 g_slist_foreach(info->allowed_bearers,
1273 cleanup_bearer_info, NULL);
1274 g_slist_free(info->allowed_bearers);
1276 if (allowed_bearers == NULL) {
1277 allowed_bearers = session_allowed_bearers_any();
1279 if (allowed_bearers == NULL)
1280 return __connman_error_failed(msg, ENOMEM);
1283 info->allowed_bearers = allowed_bearers;
1285 session->info_dirty = TRUE;
1290 case DBUS_TYPE_BOOLEAN:
1291 if (g_str_equal(name, "Priority") == TRUE) {
1292 dbus_message_iter_get_basic(&value,
1295 if (info_last->priority != info->priority)
1296 session->info_dirty = TRUE;
1297 } else if (g_str_equal(name, "AvoidHandover") == TRUE) {
1298 dbus_message_iter_get_basic(&value,
1299 &info->avoid_handover);
1301 if (info_last->avoid_handover != info->avoid_handover)
1302 session->info_dirty = TRUE;
1303 } else if (g_str_equal(name, "StayConnected") == TRUE) {
1304 dbus_message_iter_get_basic(&value,
1305 &info->stay_connected);
1307 if (info_last->stay_connected != info->stay_connected)
1308 session->info_dirty = TRUE;
1309 } else if (g_str_equal(name, "EmergencyCall") == TRUE) {
1310 dbus_message_iter_get_basic(&value,
1313 update_ecall(session);
1318 case DBUS_TYPE_UINT32:
1319 if (g_str_equal(name, "PeriodicConnect") == TRUE) {
1320 dbus_message_iter_get_basic(&value,
1321 &info->periodic_connect);
1323 if (info_last->periodic_connect != info->periodic_connect)
1324 session->info_dirty = TRUE;
1325 } else if (g_str_equal(name, "IdleTimeout") == TRUE) {
1326 dbus_message_iter_get_basic(&value,
1327 &info->idle_timeout);
1329 if (info_last->idle_timeout != info->idle_timeout)
1330 session->info_dirty = TRUE;
1335 case DBUS_TYPE_STRING:
1336 if (g_str_equal(name, "RoamingPolicy") == TRUE) {
1338 dbus_message_iter_get_basic(&value, &val);
1339 info->roaming_policy =
1340 string2roamingpolicy(val);
1342 if (info_last->roaming_policy != info->roaming_policy)
1343 session->info_dirty = TRUE;
1352 if (session->info_dirty == TRUE)
1353 session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING);
1355 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1358 return __connman_error_invalid_arguments(msg);
1361 static void release_session(gpointer key, gpointer value, gpointer user_data)
1363 struct connman_session *session = value;
1364 DBusMessage *message;
1366 DBG("owner %s path %s", session->owner, session->notify_path);
1368 if (session->notify_watch > 0)
1369 g_dbus_remove_watch(connection, session->notify_watch);
1371 g_dbus_unregister_interface(connection, session->session_path,
1372 CONNMAN_SESSION_INTERFACE);
1374 message = dbus_message_new_method_call(session->owner,
1375 session->notify_path,
1376 CONNMAN_NOTIFICATION_INTERFACE,
1378 if (message == NULL)
1381 dbus_message_set_no_reply(message, TRUE);
1383 g_dbus_send_message(connection, message);
1386 static int session_disconnect(struct connman_session *session)
1388 DBG("session %p, %s", session, session->owner);
1390 if (session->notify_watch > 0)
1391 g_dbus_remove_watch(connection, session->notify_watch);
1393 g_dbus_unregister_interface(connection, session->session_path,
1394 CONNMAN_SESSION_INTERFACE);
1396 deselect_and_disconnect(session,
1397 CONNMAN_SESSION_REASON_DISCONNECT);
1399 g_hash_table_remove(session_hash, session->session_path);
1404 static void owner_disconnect(DBusConnection *conn, void *user_data)
1406 struct connman_session *session = user_data;
1408 DBG("session %p, %s died", session, session->owner);
1410 session_disconnect(session);
1413 static DBusMessage *destroy_session(DBusConnection *conn,
1414 DBusMessage *msg, void *user_data)
1416 struct connman_session *session = user_data;
1417 struct session_info *info = session->info;
1419 DBG("session %p", session);
1421 if (ecall_info != NULL && ecall_info != info)
1422 return __connman_error_failed(msg, EBUSY);
1424 session_disconnect(session);
1426 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1429 static GDBusMethodTable session_methods[] = {
1430 { "Destroy", "", "", destroy_session },
1431 { "Connect", "", "", connect_session },
1432 { "Disconnect", "", "", disconnect_session },
1433 { "Change", "sv", "", change_session },
1437 int __connman_session_create(DBusMessage *msg)
1439 const char *owner, *notify_path;
1440 char *session_path = NULL;
1441 DBusMessageIter iter, array;
1442 struct connman_session *session = NULL;
1443 struct session_info *info, *info_last;
1445 connman_bool_t priority = FALSE, avoid_handover = FALSE;
1446 connman_bool_t stay_connected = FALSE, ecall = FALSE;
1447 enum connman_session_roaming_policy roaming_policy =
1448 CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN;
1449 GSList *allowed_bearers = NULL;
1450 unsigned int periodic_connect = 0;
1451 unsigned int idle_timeout = 0;
1455 owner = dbus_message_get_sender(msg);
1457 DBG("owner %s", owner);
1459 if (ecall_info != NULL) {
1461 * If there is an emergency call already going on,
1462 * ignore session creation attempt
1468 dbus_message_iter_init(msg, &iter);
1469 dbus_message_iter_recurse(&iter, &array);
1471 while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
1472 DBusMessageIter entry, value;
1473 const char *key, *val;
1475 dbus_message_iter_recurse(&array, &entry);
1476 dbus_message_iter_get_basic(&entry, &key);
1478 dbus_message_iter_next(&entry);
1479 dbus_message_iter_recurse(&entry, &value);
1481 switch (dbus_message_iter_get_arg_type(&value)) {
1482 case DBUS_TYPE_ARRAY:
1483 if (g_str_equal(key, "AllowedBearers") == TRUE) {
1485 session_parse_allowed_bearers(&value);
1490 case DBUS_TYPE_BOOLEAN:
1491 if (g_str_equal(key, "Priority") == TRUE) {
1492 dbus_message_iter_get_basic(&value,
1494 } else if (g_str_equal(key, "AvoidHandover") == TRUE) {
1495 dbus_message_iter_get_basic(&value,
1497 } else if (g_str_equal(key, "StayConnected") == TRUE) {
1498 dbus_message_iter_get_basic(&value,
1500 } else if (g_str_equal(key, "EmergencyCall") == TRUE) {
1501 dbus_message_iter_get_basic(&value,
1507 case DBUS_TYPE_UINT32:
1508 if (g_str_equal(key, "PeriodicConnect") == TRUE) {
1509 dbus_message_iter_get_basic(&value,
1511 } else if (g_str_equal(key, "IdleTimeout") == TRUE) {
1512 dbus_message_iter_get_basic(&value,
1518 case DBUS_TYPE_STRING:
1519 if (g_str_equal(key, "RoamingPolicy") == TRUE) {
1520 dbus_message_iter_get_basic(&value, &val);
1521 roaming_policy = string2roamingpolicy(val);
1526 dbus_message_iter_next(&array);
1529 dbus_message_iter_next(&iter);
1530 dbus_message_iter_get_basic(&iter, ¬ify_path);
1532 if (notify_path == NULL) {
1537 session_path = g_strdup_printf("/sessions%s", notify_path);
1538 if (session_path == NULL) {
1543 session = g_hash_table_lookup(session_hash, session_path);
1544 if (session != NULL) {
1550 session = g_try_new0(struct connman_session, 1);
1551 if (session == NULL) {
1556 session->info = g_try_new0(struct session_info, 1);
1557 if (session->info == NULL) {
1562 session->info_last = g_try_new0(struct session_info, 1);
1563 if (session->info_last == NULL) {
1568 info = session->info;
1569 info_last = session->info_last;
1571 session->owner = g_strdup(owner);
1572 session->session_path = session_path;
1573 session->notify_path = g_strdup(notify_path);
1574 session->notify_watch =
1575 g_dbus_add_disconnect_watch(connection, session->owner,
1576 owner_disconnect, session, NULL);
1578 info->online = FALSE;
1579 info->priority = priority;
1580 info->avoid_handover = avoid_handover;
1581 info->stay_connected = stay_connected;
1582 info->periodic_connect = periodic_connect;
1583 info->idle_timeout = idle_timeout;
1584 info->ecall = ecall;
1585 info->roaming_policy = roaming_policy;
1589 if (allowed_bearers == NULL) {
1590 info->allowed_bearers =
1591 session_allowed_bearers_any();
1593 if (info->allowed_bearers == NULL) {
1598 info->allowed_bearers = allowed_bearers;
1601 g_hash_table_replace(session_hash, session->session_path, session);
1603 DBG("add %s", session->session_path);
1605 if (g_dbus_register_interface(connection, session->session_path,
1606 CONNMAN_SESSION_INTERFACE,
1607 session_methods, NULL,
1608 NULL, session, NULL) == FALSE) {
1609 connman_error("Failed to register %s", session->session_path);
1610 g_hash_table_remove(session_hash, session->session_path);
1617 g_dbus_send_reply(connection, msg,
1618 DBUS_TYPE_OBJECT_PATH, &session->session_path,
1622 populate_service_list(session);
1623 if (info->ecall == TRUE) {
1625 update_ecall_sessions(session);
1628 info_last->online = info->online;
1629 info_last->priority = info->priority;
1630 info_last->avoid_handover = info->avoid_handover;
1631 info_last->stay_connected = info->stay_connected;
1632 info_last->periodic_connect = info->periodic_connect;
1633 info_last->idle_timeout = info->idle_timeout;
1634 info_last->ecall = info->ecall;
1635 info_last->roaming_policy = info->roaming_policy;
1636 info_last->entry = info->entry;
1637 info_last->marker = info->marker;
1638 info_last->allowed_bearers = info->allowed_bearers;
1640 session->info_dirty = TRUE;
1641 session->append_all = TRUE;
1643 session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING);
1648 connman_error("Failed to create session");
1650 if (session != NULL) {
1651 if (session->info_last != NULL)
1652 g_free(session->info_last);
1653 if (session->info != NULL)
1654 g_free(session->info);
1658 g_free(session_path);
1660 g_slist_foreach(allowed_bearers, cleanup_bearer_info, NULL);
1661 g_slist_free(allowed_bearers);
1666 int __connman_session_destroy(DBusMessage *msg)
1668 const char *owner, *session_path;
1669 struct connman_session *session;
1671 owner = dbus_message_get_sender(msg);
1673 DBG("owner %s", owner);
1675 dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &session_path,
1677 if (session_path == NULL)
1680 session = g_hash_table_lookup(session_hash, session_path);
1681 if (session == NULL)
1684 if (g_strcmp0(owner, session->owner) != 0)
1687 session_disconnect(session);
1692 connman_bool_t __connman_session_mode()
1697 void __connman_session_set_mode(connman_bool_t enable)
1699 DBG("enable %d", enable);
1701 sessionmode = enable;
1703 if (sessionmode == TRUE)
1704 __connman_service_disconnect_all();
1707 static void service_add(struct connman_service *service,
1710 GHashTableIter iter;
1711 GSequenceIter *iter_service_list;
1712 gpointer key, value;
1713 struct connman_session *session;
1714 struct service_entry *entry;
1716 DBG("service %p", service);
1718 g_hash_table_iter_init(&iter, session_hash);
1720 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1723 if (service_match(session, service) == FALSE)
1726 entry = create_service_entry(service, name,
1727 CONNMAN_SERVICE_STATE_IDLE);
1732 g_sequence_insert_sorted(session->service_list,
1733 entry, sort_services,
1736 g_hash_table_replace(session->service_hash, service,
1739 session_changed(session, CONNMAN_SESSION_TRIGGER_SERVICE);
1743 static void service_remove(struct connman_service *service)
1746 GHashTableIter iter;
1747 gpointer key, value;
1748 struct connman_session *session;
1749 struct session_info *info;
1751 DBG("service %p", service);
1753 g_hash_table_iter_init(&iter, session_hash);
1755 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1756 GSequenceIter *iter;
1758 info = session->info;
1760 iter = g_hash_table_lookup(session->service_hash, service);
1764 g_sequence_remove(iter);
1766 if (info->entry != NULL && info->entry->service == service)
1768 session_changed(session, CONNMAN_SESSION_TRIGGER_SERVICE);
1772 static void service_state_changed(struct connman_service *service,
1773 enum connman_service_state state)
1775 GHashTableIter iter;
1776 gpointer key, value;
1778 DBG("service %p state %d", service, state);
1780 g_hash_table_iter_init(&iter, session_hash);
1782 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1783 struct connman_session *session = value;
1784 GSequenceIter *service_iter;
1786 service_iter = g_hash_table_lookup(session->service_hash, service);
1787 if (service_iter != NULL) {
1788 struct service_entry *entry;
1790 entry = g_sequence_get(service_iter);
1791 entry->state = state;
1794 session_changed(session,
1795 CONNMAN_SESSION_TRIGGER_SERVICE);
1799 static void ipconfig_changed(struct connman_service *service,
1800 struct connman_ipconfig *ipconfig)
1802 GHashTableIter iter;
1803 gpointer key, value;
1804 struct connman_session *session;
1805 struct session_info *info;
1806 enum connman_ipconfig_type type;
1808 DBG("service %p ipconfig %p", service, ipconfig);
1810 type = __connman_ipconfig_get_config_type(ipconfig);
1812 g_hash_table_iter_init(&iter, session_hash);
1814 while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1816 info = session->info;
1818 if (info->entry != NULL && info->entry->service == service) {
1819 if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
1820 ipconfig_ipv4_changed(session);
1821 else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
1822 ipconfig_ipv6_changed(session);
1827 static struct connman_notifier session_notifier = {
1829 .service_add = service_add,
1830 .service_remove = service_remove,
1831 .service_state_changed = service_state_changed,
1832 .ipconfig_changed = ipconfig_changed,
1835 int __connman_session_init(void)
1841 connection = connman_dbus_get_connection();
1842 if (connection == NULL)
1845 err = connman_notifier_register(&session_notifier);
1847 dbus_connection_unref(connection);
1851 session_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1852 NULL, cleanup_session);
1854 sessionmode = FALSE;
1858 void __connman_session_cleanup(void)
1862 if (connection == NULL)
1865 connman_notifier_unregister(&session_notifier);
1867 g_hash_table_foreach(session_hash, release_session, NULL);
1868 g_hash_table_destroy(session_hash);
1869 session_hash = NULL;
1871 dbus_connection_unref(connection);