From: Daniel Wagner <[email protected]>
---
src/session.c | 217 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 214 insertions(+), 3 deletions(-)
diff --git a/src/session.c b/src/session.c
index 5010840..af2617b 100644
--- a/src/session.c
+++ b/src/session.c
@@ -506,13 +506,11 @@ static void update_service(struct connman_session
*session)
}
}
-static connman_bool_t service_match(struct connman_session *session,
+static connman_bool_t service_type_match(struct connman_session *session,
struct connman_service *service)
{
GSList *list;
- DBG("session %p service %p", session, service);
-
for (list = session->allowed_bearers; list != NULL; list = list->next) {
struct bearer_info *info = list->data;
enum connman_service_type service_type;
@@ -528,6 +526,43 @@ static connman_bool_t service_match(struct connman_session
*session,
return FALSE;
}
+static connman_bool_t service_roaming_match(struct connman_session *session,
+ struct connman_service *service)
+{
+ if (__connman_service_is_roaming(service) == FALSE)
+ return TRUE;
+
+ switch (session->roaming_policy) {
+ case CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN:
+ case CONNMAN_SESSION_ROAMING_POLICY_DEFAULT:
+ case CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN:
+ return FALSE;
+ case CONNMAN_SESSION_ROAMING_POLICY_ALWAYS:
+ case CONNMAN_SESSION_ROAMING_POLICY_NATIONAL:
+ case CONNMAN_SESSION_ROAMING_POLICY_INTERNATIONAL:
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static connman_bool_t service_match(struct connman_session *session,
+ struct connman_service *service)
+{
+ enum connman_service_type service_type;
+
+ DBG("session %p service %p", session, service);
+
+ if (service_type_match(session, service) == FALSE)
+ return FALSE;
+
+ service_type = connman_service_get_type(service);
+ if (service_type == CONNMAN_SERVICE_TYPE_CELLULAR)
+ return service_roaming_match(session, service);
+
+ return TRUE;
+}
+
static connman_bool_t session_select_service(struct connman_session *session)
{
struct connman_service *service;
@@ -1045,6 +1080,180 @@ void __connman_session_set_mode(connman_bool_t enable)
__connman_service_disconnect_all();
}
+static int service_type_weight(enum connman_service_type type)
+{
+ /*
+ * The session doesn't care which service
+ * to use. Nevertheless we have to sort them
+ * according their type. The ordering is
+ *
+ * 1. Ethernet
+ * 2. Bluetooth
+ * 3. WiFi/WiMAX
+ * 4. GSM/UTMS/3G
+ */
+
+ switch (type) {
+ case CONNMAN_SERVICE_TYPE_ETHERNET:
+ return 4;
+ case CONNMAN_SERVICE_TYPE_BLUETOOTH:
+ return 3;
+ case CONNMAN_SERVICE_TYPE_WIFI:
+ case CONNMAN_SERVICE_TYPE_WIMAX:
+ return 2;
+ case CONNMAN_SERVICE_TYPE_CELLULAR:
+ return 1;
+ case CONNMAN_SERVICE_TYPE_UNKNOWN:
+ case CONNMAN_SERVICE_TYPE_SYSTEM:
+ case CONNMAN_SERVICE_TYPE_GPS:
+ case CONNMAN_SERVICE_TYPE_VPN:
+ case CONNMAN_SERVICE_TYPE_GADGET:
+ return 0;
+ }
+
+ return 0;
+}
+
+static gint sort_allowed_bearers(struct connman_service *service_a,
+ struct connman_service *service_b,
+ struct connman_session *session)
+{
+ enum connman_service_type type_a, type_b;
+ GSList *list;
+
+ type_a = connman_service_get_type(service_a);
+ type_b = connman_service_get_type(service_b);
+
+ for (list = session->allowed_bearers; list != NULL; list = list->next) {
+ struct bearer_info *info = list->data;
+
+ if (info->match_all == TRUE) {
+ if (type_a != type_b) {
+ return service_type_weight(type_a) -
+ service_type_weight(type_b);
+ }
+ }
+
+ if (type_a == info->service_type &&
+ type_b == info->service_type) {
+ return 0;
+ }
+
+ if (type_a == info->service_type &&
+ type_b != info->service_type) {
+ return 1;
+ }
+
+ if (type_a != info->service_type &&
+ type_b == info->service_type) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static gint sort_roaming_policy(struct connman_service *service_a,
+ struct connman_service *service_b,
+ struct connman_session *session)
+{
+ connman_bool_t roaming_a;
+ connman_bool_t roaming_b;
+
+ roaming_a = __connman_service_is_roaming(service_a);
+ roaming_b = __connman_service_is_roaming(service_b);
+
+ /* Prefer non roaming over roaming */
+
+ if (roaming_a == TRUE && roaming_b == FALSE)
+ return -1;
+
+ if (roaming_a == FALSE && roaming_b == TRUE)
+ return 1;
+
+ return 0;
+}
+
+static gint sort_services(gconstpointer a, gconstpointer b, gpointer user_data)
+{
+ struct connman_service *service_a = (void *)a;
+ struct connman_service *service_b = (void *)b;
+ struct connman_session *session = user_data;
+ int res;
+
+ res = sort_allowed_bearers(service_a, service_b, session);
+ if (res != 0)
+ return res;
+
+ return sort_roaming_policy(service_a, service_b, session);
+}
+
+static void service_add(struct connman_service *service)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+ struct connman_session *session;
+
+ DBG("service %p", service);
+
+ g_hash_table_iter_init(&iter, session_hash);
+
+ while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+ session = value;
+
+ if (service_match(session, service) == FALSE)
+ continue;
+
+ g_sequence_insert_sorted(session->service_list, service,
+ sort_services, session);
+ }
+}
+
+static gint service_in_session(gconstpointer a, gconstpointer b,
+ gpointer user_data)
+{
+ if (a == b)
+ return 0;
+
+ return -1;
+}
+
+static void service_remove(struct connman_service *service)
+{
+
+ GHashTableIter iter;
+ gpointer key, value;
+ GSequenceIter *seq_iter;
+ struct connman_session *session;
+ struct connman_service *found_service;
+
+ DBG("service %p", service);
+
+ g_hash_table_iter_init(&iter, session_hash);
+
+ while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+ session = value;
+
+ if (session->service_list == NULL)
+ continue;
+
+ seq_iter = g_sequence_search(session->service_list, service,
+ service_in_session, NULL);
+ if (g_sequence_iter_is_end(seq_iter) == TRUE)
+ continue;
+
+ g_sequence_remove(seq_iter);
+
+ found_service = g_sequence_get(seq_iter);
+ if (found_service != session->service)
+ continue;
+
+ session->service = NULL;
+ update_service(session);
+ g_timeout_add_seconds(0, service_changed, session);
+ }
+}
+
static void service_state_changed(struct connman_service *service,
enum connman_service_state state)
{
@@ -1099,6 +1308,8 @@ static void ipconfig_changed(struct connman_service
*service,
static struct connman_notifier session_notifier = {
.name = "session",
+ .service_add = service_add,
+ .service_remove = service_remove,
.service_state_changed = service_state_changed,
.ipconfig_changed = ipconfig_changed,
};
--
1.7.4
_______________________________________________
connman mailing list
[email protected]
http://lists.connman.net/listinfo/connman