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

Reply via email to