session: Remove notify watch
[platform/upstream/connman.git] / src / session.c
index b67b2ea..7370a07 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
  *  Copyright (C) 2011  BWM CarIT GmbH. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
 
 #include <gdbus.h>
 
+#include <connman/session.h>
+
 #include "connman.h"
 
 static DBusConnection *connection;
 static GHashTable *session_hash;
 static connman_bool_t sessionmode;
-static struct session_info *ecall_info;
+static struct connman_session *ecall_session;
+static GSList *policy_list;
 
 enum connman_session_trigger {
        CONNMAN_SESSION_TRIGGER_UNKNOWN         = 0,
        CONNMAN_SESSION_TRIGGER_SETTING         = 1,
        CONNMAN_SESSION_TRIGGER_CONNECT         = 2,
        CONNMAN_SESSION_TRIGGER_DISCONNECT      = 3,
-       CONNMAN_SESSION_TRIGGER_PERIODIC        = 4,
-       CONNMAN_SESSION_TRIGGER_SERVICE         = 5,
-       CONNMAN_SESSION_TRIGGER_ECALL           = 6,
+       CONNMAN_SESSION_TRIGGER_SERVICE         = 4,
+       CONNMAN_SESSION_TRIGGER_ECALL           = 5,
 };
 
 enum connman_session_reason {
        CONNMAN_SESSION_REASON_UNKNOWN          = 0,
        CONNMAN_SESSION_REASON_CONNECT          = 1,
-       CONNMAN_SESSION_REASON_DISCONNECT       = 2,
-       CONNMAN_SESSION_REASON_FREE_RIDE        = 3,
-       CONNMAN_SESSION_REASON_PERIODIC         = 4,
+       CONNMAN_SESSION_REASON_FREE_RIDE        = 2,
 };
 
 enum connman_session_state {
@@ -59,21 +59,6 @@ enum connman_session_state {
        CONNMAN_SESSION_STATE_ONLINE         = 2,
 };
 
-enum connman_session_type {
-       CONNMAN_SESSION_TYPE_ANY      = 0,
-       CONNMAN_SESSION_TYPE_LOCAL    = 1,
-       CONNMAN_SESSION_TYPE_INTERNET = 2,
-};
-
-enum connman_session_roaming_policy {
-       CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN          = 0,
-       CONNMAN_SESSION_ROAMING_POLICY_DEFAULT          = 1,
-       CONNMAN_SESSION_ROAMING_POLICY_ALWAYS           = 2,
-       CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN        = 3,
-       CONNMAN_SESSION_ROAMING_POLICY_NATIONAL         = 4,
-       CONNMAN_SESSION_ROAMING_POLICY_INTERNATIONAL    = 5,
-};
-
 struct service_entry {
        /* track why this service was selected */
        enum connman_session_reason reason;
@@ -86,18 +71,8 @@ struct service_entry {
 };
 
 struct session_info {
+       struct connman_session_config config;
        enum connman_session_state state;
-       enum connman_session_type type;
-       connman_bool_t priority;
-       GSList *allowed_bearers;
-       connman_bool_t avoid_handover;
-       connman_bool_t stay_connected;
-       unsigned int periodic_connect;
-       unsigned int idle_timeout;
-       connman_bool_t ecall;
-       enum connman_session_roaming_policy roaming_policy;
-       unsigned int marker;
-
        struct service_entry *entry;
        enum connman_session_reason reason;
 };
@@ -108,20 +83,20 @@ struct connman_session {
        char *notify_path;
        guint notify_watch;
 
+       struct connman_session_policy *policy;
+
        connman_bool_t append_all;
        struct session_info *info;
        struct session_info *info_last;
+       struct connman_session_config *policy_config;
+       GSList *user_allowed_bearers;
+
+       connman_bool_t ecall;
 
        GSequence *service_list;
        GHashTable *service_hash;
 };
 
-struct bearer_info {
-       char *name;
-       connman_bool_t match_all;
-       enum connman_service_type service_type;
-};
-
 static const char *trigger2string(enum connman_session_trigger trigger)
 {
        switch (trigger) {
@@ -133,8 +108,6 @@ static const char *trigger2string(enum connman_session_trigger trigger)
                return "connect";
        case CONNMAN_SESSION_TRIGGER_DISCONNECT:
                return "disconnect";
-       case CONNMAN_SESSION_TRIGGER_PERIODIC:
-               return "periodic";
        case CONNMAN_SESSION_TRIGGER_SERVICE:
                return "service";
        case CONNMAN_SESSION_TRIGGER_ECALL:
@@ -151,12 +124,8 @@ static const char *reason2string(enum connman_session_reason reason)
                return "unknown";
        case CONNMAN_SESSION_REASON_CONNECT:
                return "connect";
-       case CONNMAN_SESSION_REASON_DISCONNECT:
-               return "disconnect";
        case CONNMAN_SESSION_REASON_FREE_RIDE:
                return "free-ride";
-       case CONNMAN_SESSION_REASON_PERIODIC:
-               return "periodic";
        }
 
        return NULL;
@@ -179,8 +148,10 @@ static const char *state2string(enum connman_session_state state)
 static const char *type2string(enum connman_session_type type)
 {
        switch (type) {
-       case CONNMAN_SESSION_TYPE_ANY:
+       case CONNMAN_SESSION_TYPE_UNKNOWN:
                return "";
+       case CONNMAN_SESSION_TYPE_ANY:
+               return "any";
        case CONNMAN_SESSION_TYPE_LOCAL:
                return "local";
        case CONNMAN_SESSION_TYPE_INTERNET:
@@ -190,37 +161,7 @@ static const char *type2string(enum connman_session_type type)
        return NULL;
 }
 
-static enum connman_session_type string2type(const char *type)
-{
-       if (g_strcmp0(type, "local") == 0)
-               return CONNMAN_SESSION_TYPE_LOCAL;
-       else if (g_strcmp0(type, "internet") == 0)
-               return CONNMAN_SESSION_TYPE_INTERNET;
-
-       return CONNMAN_SESSION_TYPE_ANY;
-}
-
-static const char *roamingpolicy2string(enum connman_session_roaming_policy policy)
-{
-       switch (policy) {
-       case CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN:
-               return "unknown";
-       case CONNMAN_SESSION_ROAMING_POLICY_DEFAULT:
-               return "default";
-       case CONNMAN_SESSION_ROAMING_POLICY_ALWAYS:
-               return "always";
-       case CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN:
-               return "forbidden";
-       case CONNMAN_SESSION_ROAMING_POLICY_NATIONAL:
-               return "national";
-       case CONNMAN_SESSION_ROAMING_POLICY_INTERNATIONAL:
-               return "international";
-       }
-
-       return NULL;
-}
-
-static enum connman_session_roaming_policy string2roamingpolicy(const char *policy)
+enum connman_session_roaming_policy connman_session_parse_roaming_policy(const char *policy)
 {
        if (g_strcmp0(policy, "default") == 0)
                return CONNMAN_SESSION_ROAMING_POLICY_DEFAULT;
@@ -236,25 +177,38 @@ static enum connman_session_roaming_policy string2roamingpolicy(const char *poli
                return CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN;
 }
 
-static enum connman_service_type bearer2service(const char *bearer)
+enum connman_session_type connman_session_parse_connection_type(const char *type)
 {
-       if (bearer == NULL)
-               return CONNMAN_SERVICE_TYPE_UNKNOWN;
+       if (g_strcmp0(type, "any") == 0)
+               return CONNMAN_SESSION_TYPE_ANY;
+       if (g_strcmp0(type, "local") == 0)
+               return CONNMAN_SESSION_TYPE_LOCAL;
+       else if (g_strcmp0(type, "internet") == 0)
+               return CONNMAN_SESSION_TYPE_INTERNET;
+
+       return CONNMAN_SESSION_TYPE_UNKNOWN;
+}
 
+static int bearer2service(const char *bearer, enum connman_service_type *type)
+{
        if (g_strcmp0(bearer, "ethernet") == 0)
-               return CONNMAN_SERVICE_TYPE_ETHERNET;
+               *type = CONNMAN_SERVICE_TYPE_ETHERNET;
        else if (g_strcmp0(bearer, "wifi") == 0)
-               return CONNMAN_SERVICE_TYPE_WIFI;
+               *type = CONNMAN_SERVICE_TYPE_WIFI;
        else if (g_strcmp0(bearer, "wimax") == 0)
-               return CONNMAN_SERVICE_TYPE_WIMAX;
+               *type = CONNMAN_SERVICE_TYPE_WIMAX;
        else if (g_strcmp0(bearer, "bluetooth") == 0)
-               return CONNMAN_SERVICE_TYPE_BLUETOOTH;
+               *type = CONNMAN_SERVICE_TYPE_BLUETOOTH;
        else if (g_strcmp0(bearer, "cellular") == 0)
-               return CONNMAN_SERVICE_TYPE_CELLULAR;
+               *type = CONNMAN_SERVICE_TYPE_CELLULAR;
        else if (g_strcmp0(bearer, "vpn") == 0)
-               return CONNMAN_SERVICE_TYPE_VPN;
+               *type = CONNMAN_SERVICE_TYPE_VPN;
+       else if (g_strcmp0(bearer, "*") == 0)
+               *type = CONNMAN_SERVICE_TYPE_UNKNOWN;
        else
-               return CONNMAN_SERVICE_TYPE_UNKNOWN;
+               return -EINVAL;
+
+       return 0;
 }
 
 static char *service2bearer(enum connman_service_type type)
@@ -282,72 +236,279 @@ static char *service2bearer(enum connman_service_type type)
        return "";
 }
 
-static void cleanup_bearer_info(gpointer data, gpointer user_data)
+static int assign_policy_plugin(struct connman_session *session)
 {
-       struct bearer_info *info = data;
+       if (session->policy != NULL)
+               return -EALREADY;
+
+       if (policy_list == NULL)
+               return 0;
+
+       session->policy = policy_list->data;
+
+       return 0;
+}
+
+static int create_policy_config(struct connman_session *session,
+                               connman_session_config_cb callback,
+                               void *user_data)
+{
+       struct connman_session_config *config;
+
+       if (session->policy == NULL) {
+               config = connman_session_create_default_config();
+               if (config == NULL)
+                       return -ENOMEM;
+
+               session->policy_config = config;
+
+               return 0;
+       }
+
+       return (*session->policy->create)(session, callback, user_data);
+}
+
+static void destroy_policy_config(struct connman_session *session)
+{
+       if (session->policy == NULL) {
+               g_free(session->policy_config);
+               return;
+       }
+
+       (*session->policy->destroy)(session);
+}
+
+static void probe_policy(struct connman_session_policy *policy)
+{
+
+       GHashTableIter iter;
+       gpointer key, value;
+       struct connman_session *session;
+
+       DBG("policy %p name %s", policy, policy->name);
+
+       g_hash_table_iter_init(&iter, session_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               session = value;
+
+               if (session->policy != NULL)
+                       continue;
+
+               assign_policy_plugin(session);
+       }
+}
+
+static void remove_policy(struct connman_session_policy *policy)
+{
+       GHashTableIter iter;
+       gpointer key, value;
+       struct connman_session *session;
+
+       DBG("policy %p name %s", policy, policy->name);
+
+       g_hash_table_iter_init(&iter, session_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               session = value;
+
+               if (session->policy != policy)
+                       continue;
+
+               session->policy = NULL;
+               assign_policy_plugin(session);
+       }
+}
+
+static gint compare_priority(gconstpointer a, gconstpointer b)
+{
+       const struct connman_session_policy *policy1 = a;
+       const struct connman_session_policy *policy2 = b;
+
+       return policy2->priority - policy1->priority;
+}
+
+
+int connman_session_policy_register(struct connman_session_policy *policy)
+{
+       DBG("name %s", policy->name);
+
+       if (policy->create == NULL || policy->destroy == NULL)
+               return -EINVAL;
+
+       policy_list = g_slist_insert_sorted(policy_list, policy,
+                                               compare_priority);
+
+       probe_policy(policy);
+
+       return 0;
+}
+
+void connman_session_policy_unregister(struct connman_session_policy *policy)
+{
+       DBG("name %s", policy->name);
+
+       policy_list = g_slist_remove(policy_list, policy);
 
-       g_free(info->name);
-       g_free(info);
+       remove_policy(policy);
 }
 
-static GSList *session_parse_allowed_bearers(DBusMessageIter *iter)
+int connman_session_set_default_config(struct connman_session_config *config)
+{
+       config->priority = FALSE;
+       config->roaming_policy = CONNMAN_SESSION_ROAMING_POLICY_DEFAULT;
+       config->type = CONNMAN_SESSION_TYPE_ANY;
+       config->ecall = FALSE;
+
+       g_slist_free(config->allowed_bearers);
+       config->allowed_bearers = g_slist_prepend(NULL,
+                               GINT_TO_POINTER(CONNMAN_SERVICE_TYPE_UNKNOWN));
+       if (config->allowed_bearers == NULL)
+               return -ENOMEM;
+
+       return 0;
+}
+
+struct connman_session_config *connman_session_create_default_config(void)
+{
+       struct connman_session_config *config;
+
+       config = g_try_new0(struct connman_session_config, 1);
+       if (config == NULL)
+               return NULL;
+
+       if (connman_session_set_default_config(config) < 0) {
+               g_free(config);
+               return NULL;
+       }
+
+       return config;
+}
+
+static enum connman_session_type apply_policy_on_type(
+                       enum connman_session_type policy,
+                       enum connman_session_type type)
+{
+       if (type == CONNMAN_SESSION_TYPE_UNKNOWN)
+               return CONNMAN_SESSION_TYPE_UNKNOWN;
+
+       if (policy == CONNMAN_SESSION_TYPE_ANY)
+               return type;
+
+       if (policy == CONNMAN_SESSION_TYPE_LOCAL)
+               return CONNMAN_SESSION_TYPE_LOCAL;
+
+       return CONNMAN_SESSION_TYPE_INTERNET;
+}
+
+int connman_session_parse_bearers(const char *token, GSList **list)
+{
+       enum connman_service_type bearer;
+       int err;
+
+       if (g_strcmp0(token, "") == 0)
+               return 0;
+
+       err = bearer2service(token, &bearer);
+       if (err < 0)
+               return err;
+
+       *list = g_slist_append(*list, GINT_TO_POINTER(bearer));
+
+       return 0;
+}
+
+static int parse_bearers(DBusMessageIter *iter, GSList **list)
 {
-       struct bearer_info *info;
        DBusMessageIter array;
-       GSList *list = NULL;
+       int type, err;
 
        dbus_message_iter_recurse(iter, &array);
 
-       while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) {
-               char *bearer = NULL;
+       *list = NULL;
 
-               dbus_message_iter_get_basic(&array, &bearer);
+       while ((type = dbus_message_iter_get_arg_type(&array)) !=
+                       DBUS_TYPE_INVALID) {
+               char *bearer_name = NULL;
 
-               info = g_try_new0(struct bearer_info, 1);
-               if (info == NULL) {
-                       g_slist_foreach(list, cleanup_bearer_info, NULL);
-                       g_slist_free(list);
-
-                       return NULL;
+               if (type != DBUS_TYPE_STRING) {
+                       g_slist_free(*list);
+                       *list = NULL;
+                       return -EINVAL;
                }
 
-               info->name = g_strdup(bearer);
-               info->service_type = bearer2service(info->name);
+               dbus_message_iter_get_basic(&array, &bearer_name);
 
-               if (info->service_type == CONNMAN_SERVICE_TYPE_UNKNOWN &&
-                               g_strcmp0(info->name, "*") == 0) {
-                       info->match_all = TRUE;
-               } else {
-                       info->match_all = FALSE;
+               err = connman_session_parse_bearers(bearer_name, list);
+               if (err < 0) {
+                       g_slist_free(*list);
+                       *list = NULL;
+                       return err;
                }
 
-               list = g_slist_append(list, info);
-
                dbus_message_iter_next(&array);
        }
 
-       return list;
+       return 0;
 }
 
-static GSList *session_allowed_bearers_any(void)
+static int filter_bearer(GSList *policy_bearers,
+                               enum connman_service_type bearer,
+                               GSList **list)
 {
-       struct bearer_info *info;
-       GSList *list = NULL;
+       enum connman_service_type policy;
+       GSList *it;
 
-       info = g_try_new0(struct bearer_info, 1);
-       if (info == NULL) {
-               g_slist_free(list);
+       if (policy_bearers == NULL)
+               goto clone;
 
-               return NULL;
+       for (it = policy_bearers; it != NULL; it = it->next) {
+               policy = GPOINTER_TO_INT(it->data);
+
+               if (bearer == CONNMAN_SERVICE_TYPE_UNKNOWN) {
+                       bearer = policy;
+                       goto clone;
+               }
+
+               if (policy != CONNMAN_SERVICE_TYPE_UNKNOWN && policy != bearer)
+                       continue;
+
+               goto clone;
        }
 
-       info->name = g_strdup("");
-       info->match_all = TRUE;
-       info->service_type = CONNMAN_SERVICE_TYPE_UNKNOWN;
+       *list = NULL;
+
+       return 0;
+
+clone:
+       *list = g_slist_append(*list, GINT_TO_POINTER(bearer));
+
+       return 0;
+}
+
+static int apply_policy_on_bearers(GSList *policy_bearers, GSList *bearers,
+                               GSList **list)
+{
+       enum connman_service_type bearer;
+       GSList *it;
+       int err;
+
+       *list = NULL;
+
+       for (it = bearers; it != NULL; it = it->next) {
+               bearer = GPOINTER_TO_INT(it->data);
+
+               err = filter_bearer(policy_bearers, bearer, list);
+               if (err < 0)
+                       return err;
+       }
 
-       list = g_slist_append(list, info);
+       return 0;
+}
 
-       return list;
+const char *connman_session_get_owner(struct connman_session *session)
+{
+       return session->owner;
 }
 
 static void append_allowed_bearers(DBusMessageIter *iter, void *user_data)
@@ -355,12 +516,16 @@ static void append_allowed_bearers(DBusMessageIter *iter, void *user_data)
        struct session_info *info = user_data;
        GSList *list;
 
-       for (list = info->allowed_bearers;
+       for (list = info->config.allowed_bearers;
                        list != NULL; list = list->next) {
-               struct bearer_info *info = list->data;
+               enum connman_service_type bearer = GPOINTER_TO_INT(list->data);
+               const char *name = __connman_service_type2string(bearer);
+
+               if (name == NULL)
+                       name = "*";
 
                dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
-                                               &info->name);
+                                               &name);
        }
 }
 
@@ -410,7 +575,6 @@ static void append_notify(DBusMessageIter *dict,
 {
        struct session_info *info = session->info;
        struct session_info *info_last = session->info_last;
-       const char *policy;
        struct connman_service *service;
        const char *name, *ifname, *bearer;
 
@@ -461,87 +625,23 @@ static void append_notify(DBusMessageIter *dict,
                info_last->entry = info->entry;
        }
 
-       if (session->append_all == TRUE || info->type != info_last->type) {
-               const char *type = type2string(info->type);
+       if (session->append_all == TRUE ||
+                       info->config.type != info_last->config.type) {
+               const char *type = type2string(info->config.type);
 
                connman_dbus_dict_append_basic(dict, "ConnectionType",
                                                DBUS_TYPE_STRING,
                                                &type);
-               info_last->type = info->type;
-       }
-
-       if (session->append_all == TRUE ||
-                       info->priority != info_last->priority) {
-               connman_dbus_dict_append_basic(dict, "Priority",
-                                               DBUS_TYPE_BOOLEAN,
-                                               &info->priority);
-               info_last->priority = info->priority;
+               info_last->config.type = info->config.type;
        }
 
        if (session->append_all == TRUE ||
-                       info->allowed_bearers != info_last->allowed_bearers) {
+                       info->config.allowed_bearers != info_last->config.allowed_bearers) {
                connman_dbus_dict_append_array(dict, "AllowedBearers",
                                                DBUS_TYPE_STRING,
                                                append_allowed_bearers,
                                                info);
-               info_last->allowed_bearers = info->allowed_bearers;
-       }
-
-       if (session->append_all == TRUE ||
-                       info->avoid_handover != info_last->avoid_handover) {
-               connman_dbus_dict_append_basic(dict, "AvoidHandover",
-                                               DBUS_TYPE_BOOLEAN,
-                                               &info->avoid_handover);
-               info_last->avoid_handover = info->avoid_handover;
-       }
-
-       if (session->append_all == TRUE ||
-                       info->stay_connected != info_last->stay_connected) {
-               connman_dbus_dict_append_basic(dict, "StayConnected",
-                                               DBUS_TYPE_BOOLEAN,
-                                               &info->stay_connected);
-               info_last->stay_connected = info->stay_connected;
-       }
-
-       if (session->append_all == TRUE ||
-                       info->periodic_connect != info_last->periodic_connect) {
-               connman_dbus_dict_append_basic(dict, "PeriodicConnect",
-                                               DBUS_TYPE_UINT32,
-                                               &info->periodic_connect);
-               info_last->periodic_connect = info->periodic_connect;
-       }
-
-       if (session->append_all == TRUE ||
-                       info->idle_timeout != info_last->idle_timeout) {
-               connman_dbus_dict_append_basic(dict, "IdleTimeout",
-                                               DBUS_TYPE_UINT32,
-                                               &info->idle_timeout);
-               info_last->idle_timeout = info->idle_timeout;
-       }
-
-       if (session->append_all == TRUE ||
-                       info->ecall != info_last->ecall) {
-               connman_dbus_dict_append_basic(dict, "EmergencyCall",
-                                               DBUS_TYPE_BOOLEAN,
-                                               &info->ecall);
-               info_last->ecall = info->ecall;
-       }
-
-       if (session->append_all == TRUE ||
-                       info->roaming_policy != info_last->roaming_policy) {
-               policy = roamingpolicy2string(info->roaming_policy);
-               connman_dbus_dict_append_basic(dict, "RoamingPolicy",
-                                               DBUS_TYPE_STRING,
-                                               &policy);
-               info_last->roaming_policy = info->roaming_policy;
-       }
-
-       if (session->append_all == TRUE ||
-                       info->marker != info_last->marker) {
-               connman_dbus_dict_append_basic(dict, "SessionMarker",
-                                               DBUS_TYPE_UINT32,
-                                               &info->marker);
-               info_last->marker = info->marker;
+               info_last->config.allowed_bearers = info->config.allowed_bearers;
        }
 
        session->append_all = FALSE;
@@ -551,6 +651,8 @@ static connman_bool_t is_type_matching_state(enum connman_session_state *state,
                                                enum connman_session_type type)
 {
        switch (type) {
+       case CONNMAN_SESSION_TYPE_UNKNOWN:
+               return FALSE;
        case CONNMAN_SESSION_TYPE_ANY:
                return TRUE;
        case CONNMAN_SESSION_TYPE_LOCAL:
@@ -584,16 +686,8 @@ static connman_bool_t compute_notifiable_changes(struct connman_session *session
                        info->state >= CONNMAN_SESSION_STATE_CONNECTED)
                return TRUE;
 
-       if (info->periodic_connect != info_last->periodic_connect ||
-                       info->allowed_bearers != info_last->allowed_bearers ||
-                       info->avoid_handover != info_last->avoid_handover ||
-                       info->stay_connected != info_last->stay_connected ||
-                       info->roaming_policy != info_last->roaming_policy ||
-                       info->idle_timeout != info_last->idle_timeout ||
-                       info->priority != info_last->priority ||
-                       info->marker != info_last->marker ||
-                       info->ecall != info_last->ecall ||
-                       info->type != info_last->type)
+       if (info->config.allowed_bearers != info_last->config.allowed_bearers ||
+                       info->config.type != info_last->config.type)
                return TRUE;
 
        return FALSE;
@@ -653,16 +747,16 @@ static connman_bool_t service_type_match(struct connman_session *session,
        struct session_info *info = session->info;
        GSList *list;
 
-       for (list = info->allowed_bearers;
+       for (list = info->config.allowed_bearers;
                        list != NULL; list = list->next) {
-               struct bearer_info *info = list->data;
+               enum connman_service_type bearer = GPOINTER_TO_INT(list->data);
                enum connman_service_type service_type;
 
-               if (info->match_all == TRUE)
+               if (bearer == CONNMAN_SERVICE_TYPE_UNKNOWN)
                        return TRUE;
 
                service_type = connman_service_get_type(service);
-               if (info->service_type == service_type)
+               if (bearer == service_type)
                        return TRUE;
        }
 
@@ -724,11 +818,11 @@ static gint sort_allowed_bearers(struct connman_service *service_a,
        type_a = connman_service_get_type(service_a);
        type_b = connman_service_get_type(service_b);
 
-       for (list = info->allowed_bearers;
+       for (list = info->config.allowed_bearers;
                        list != NULL; list = list->next) {
-               struct bearer_info *info = list->data;
+               enum connman_service_type bearer = GPOINTER_TO_INT(list->data);
 
-               if (info->match_all == TRUE) {
+               if (bearer == CONNMAN_SERVICE_TYPE_UNKNOWN) {
                        if (type_a != type_b) {
                                weight_a = service_type_weight(type_a);
                                weight_b = service_type_weight(type_b);
@@ -743,20 +837,14 @@ static gint sort_allowed_bearers(struct connman_service *service_a,
                        }
                }
 
-               if (type_a == info->service_type &&
-                               type_b == info->service_type) {
+               if (type_a == bearer && type_b == bearer)
                        return 0;
-               }
 
-               if (type_a == info->service_type &&
-                               type_b != info->service_type) {
+               if (type_a == bearer && type_b != bearer)
                        return -1;
-               }
 
-               if (type_a != info->service_type &&
-                               type_b == info->service_type) {
+               if (type_a != bearer && type_b == bearer)
                        return 1;
-               }
        }
 
        return 0;
@@ -772,6 +860,25 @@ static gint sort_services(gconstpointer a, gconstpointer b, gpointer user_data)
                                session);
 }
 
+static void free_session(struct connman_session *session)
+{
+       if (session == NULL)
+               return;
+
+       if (session->notify_watch > 0)
+               g_dbus_remove_watch(connection, session->notify_watch);
+
+       destroy_policy_config(session);
+       g_slist_free(session->info->config.allowed_bearers);
+       g_free(session->owner);
+       g_free(session->session_path);
+       g_free(session->notify_path);
+       g_free(session->info);
+       g_free(session->info_last);
+
+       g_free(session);
+}
+
 static void cleanup_session(gpointer user_data)
 {
        struct connman_session *session = user_data;
@@ -779,6 +886,7 @@ static void cleanup_session(gpointer user_data)
 
        DBG("remove %s", session->session_path);
 
+       g_slist_free(session->user_allowed_bearers);
        g_hash_table_destroy(session->service_hash);
        g_sequence_free(session->service_list);
 
@@ -787,16 +895,7 @@ static void cleanup_session(gpointer user_data)
                __connman_service_disconnect(info->entry->service);
        }
 
-       g_slist_foreach(info->allowed_bearers, cleanup_bearer_info, NULL);
-       g_slist_free(info->allowed_bearers);
-
-       g_free(session->owner);
-       g_free(session->session_path);
-       g_free(session->notify_path);
-       g_free(session->info);
-       g_free(session->info_last);
-
-       g_free(session);
+       free_session(session);
 }
 
 static enum connman_session_state service_to_session_state(enum connman_service_state state)
@@ -860,10 +959,8 @@ static connman_bool_t explicit_connect(enum connman_session_reason reason)
        switch (reason) {
        case CONNMAN_SESSION_REASON_UNKNOWN:
        case CONNMAN_SESSION_REASON_FREE_RIDE:
-       case CONNMAN_SESSION_REASON_DISCONNECT:
                break;
        case CONNMAN_SESSION_REASON_CONNECT:
-       case CONNMAN_SESSION_REASON_PERIODIC:
                return TRUE;
        }
 
@@ -888,9 +985,6 @@ static connman_bool_t explicit_disconnect(struct session_info *info)
        if (__connman_service_session_dec(info->entry->service) == FALSE)
                return FALSE;
 
-       if (ecall_info != NULL && ecall_info != info)
-               return FALSE;
-
        return TRUE;
 }
 
@@ -1014,14 +1108,13 @@ static void deselect_service(struct session_info *info)
                pending_timeout_add(0, call_disconnect, entry);
 }
 
-static void deselect_and_disconnect(struct connman_session *session,
-                                       enum connman_session_reason reason)
+static void deselect_and_disconnect(struct connman_session *session)
 {
        struct session_info *info = session->info;
 
        deselect_service(info);
 
-       info->reason = reason;
+       info->reason = CONNMAN_SESSION_REASON_FREE_RIDE;
 }
 
 static void select_connected_service(struct session_info *info,
@@ -1030,7 +1123,7 @@ static void select_connected_service(struct session_info *info,
        enum connman_session_state state;
 
        state = service_to_session_state(entry->state);
-       if (is_type_matching_state(&state, info->type) == FALSE)
+       if (is_type_matching_state(&state, info->config.type) == FALSE)
                return;
 
        info->state = state;
@@ -1198,7 +1291,7 @@ static void session_changed(struct connman_session *session,
 
                state = service_to_session_state(info->entry->state);
 
-               if (is_type_matching_state(&state, info->type) == TRUE)
+               if (is_type_matching_state(&state, info->config.type) == TRUE)
                        info->state = state;
        }
 
@@ -1207,7 +1300,7 @@ static void session_changed(struct connman_session *session,
                DBG("ignore session changed event");
                return;
        case CONNMAN_SESSION_TRIGGER_SETTING:
-               if (info->allowed_bearers != info_last->allowed_bearers) {
+               if (info->config.allowed_bearers != info_last->config.allowed_bearers) {
 
                        service_hash_last = session->service_hash;
                        service_list_last = session->service_list;
@@ -1228,18 +1321,18 @@ static void session_changed(struct connman_session *session,
                                 * The currently selected service is
                                 * not part of this session anymore.
                                 */
-                               deselect_and_disconnect(session, info->reason);
+                               deselect_and_disconnect(session);
                        }
 
                        g_hash_table_remove_all(service_hash_last);
                        g_sequence_free(service_list_last);
                }
 
-               if (info->type != info_last->type) {
+               if (info->config.type != info_last->config.type) {
                        if (info->state >= CONNMAN_SESSION_STATE_CONNECTED &&
                                        is_type_matching_state(&info->state,
-                                                       info->type) == FALSE)
-                               deselect_and_disconnect(session, info->reason);
+                                                       info->config.type) == FALSE)
+                               deselect_and_disconnect(session);
                }
 
                if (info->state == CONNMAN_SESSION_STATE_DISCONNECTED) {
@@ -1248,6 +1341,11 @@ static void session_changed(struct connman_session *session,
                }
 
                break;
+       case CONNMAN_SESSION_TRIGGER_ECALL:
+               /*
+                * For the time beeing we fallback to normal connect
+                * strategy.
+                */
        case CONNMAN_SESSION_TRIGGER_CONNECT:
                if (info->state >= CONNMAN_SESSION_STATE_CONNECTED) {
                        if (info->entry->reason == CONNMAN_SESSION_REASON_CONNECT)
@@ -1267,19 +1365,7 @@ static void session_changed(struct connman_session *session,
 
                break;
        case CONNMAN_SESSION_TRIGGER_DISCONNECT:
-               deselect_and_disconnect(session,
-                                       CONNMAN_SESSION_REASON_DISCONNECT);
-
-               break;
-       case CONNMAN_SESSION_TRIGGER_PERIODIC:
-               if (info->state >= CONNMAN_SESSION_STATE_CONNECTED) {
-                       info->entry->reason = CONNMAN_SESSION_REASON_PERIODIC;
-                       __connman_service_session_inc(info->entry->service);
-                       break;
-               }
-
-               select_and_connect(session,
-                               CONNMAN_SESSION_REASON_PERIODIC);
+               deselect_and_disconnect(session);
 
                break;
        case CONNMAN_SESSION_TRIGGER_SERVICE:
@@ -1289,109 +1375,94 @@ static void session_changed(struct connman_session *session,
                        break;
                }
 
-               deselect_and_disconnect(session, info->reason);
+               deselect_and_disconnect(session);
 
-               if (info->reason == CONNMAN_SESSION_REASON_FREE_RIDE ||
-                               info->stay_connected == TRUE) {
+               if (info->reason == CONNMAN_SESSION_REASON_FREE_RIDE) {
                        select_and_connect(session, info->reason);
                }
 
                break;
-       case CONNMAN_SESSION_TRIGGER_ECALL:
-               if (info->state == CONNMAN_SESSION_STATE_DISCONNECTED &&
-                               info->entry != NULL &&
-                               info->entry->service != NULL) {
-                       deselect_and_disconnect(session, info->reason);
-               }
-
-               break;
        }
 
        session_notify(session);
 }
 
-static DBusMessage *connect_session(DBusConnection *conn,
-                                       DBusMessage *msg, void *user_data)
+int connman_session_config_update(struct connman_session *session)
 {
-       struct connman_session *session = user_data;
        struct session_info *info = session->info;
+       GSList *allowed_bearers;
+       int err;
 
        DBG("session %p", session);
 
-       if (ecall_info != NULL && ecall_info != info)
-               return __connman_error_failed(msg, EBUSY);
+       /*
+        * We update all configuration even though only one entry
+        * might have changed. We can still optimize this later.
+        */
 
-       session_changed(session, CONNMAN_SESSION_TRIGGER_CONNECT);
+       err = apply_policy_on_bearers(
+               session->policy_config->allowed_bearers,
+               session->user_allowed_bearers,
+               &allowed_bearers);
+       if (err < 0)
+               return err;
 
-       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
-}
+       g_slist_free(info->config.allowed_bearers);
+       info->config.allowed_bearers = allowed_bearers;
 
-static DBusMessage *disconnect_session(DBusConnection *conn,
-                                       DBusMessage *msg, void *user_data)
-{
-       struct connman_session *session = user_data;
-       struct session_info *info = session->info;
+       info->config.type = apply_policy_on_type(
+                               session->policy_config->type,
+                               info->config.type);
 
-       DBG("session %p", session);
+       info->config.roaming_policy = session->policy_config->roaming_policy;
 
-       if (ecall_info != NULL && ecall_info != info)
-               return __connman_error_failed(msg, EBUSY);
+       info->config.ecall = session->policy_config->ecall;
+       if (info->config.ecall == TRUE)
+               ecall_session = session;
 
-       session_changed(session, CONNMAN_SESSION_TRIGGER_DISCONNECT);
+       info->config.priority = session->policy_config->priority;
 
-       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+       session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING);
+
+       return 0;
 }
 
-static void update_ecall_sessions(struct connman_session *session)
+static DBusMessage *connect_session(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
 {
-       struct session_info *info = session->info;
-       struct connman_session *session_iter;
-       GHashTableIter iter;
-       gpointer key, value;
-
-       g_hash_table_iter_init(&iter, session_hash);
+       struct connman_session *session = user_data;
 
-       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
-               session_iter = value;
+       DBG("session %p", session);
 
-               if (session_iter == session)
-                       continue;
+       if (ecall_session != NULL) {
+               if (ecall_session->ecall == TRUE && ecall_session != session)
+                       return __connman_error_failed(msg, EBUSY);
 
-               session_iter->info->ecall = info->ecall;
+               session->ecall = TRUE;
+               session_changed(session, CONNMAN_SESSION_TRIGGER_ECALL);
+       } else
+               session_changed(session, CONNMAN_SESSION_TRIGGER_CONNECT);
 
-               session_changed(session_iter, CONNMAN_SESSION_TRIGGER_ECALL);
-       }
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
 }
 
-static void update_ecall(struct connman_session *session)
+static DBusMessage *disconnect_session(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
 {
-       struct session_info *info = session->info;
-       struct session_info *info_last = session->info_last;
+       struct connman_session *session = user_data;
 
-       DBG("session %p ecall_info %p ecall %d -> %d", session,
-               ecall_info, info_last->ecall, info->ecall);
+       DBG("session %p", session);
 
-       if (ecall_info == NULL) {
-               if (!(info_last->ecall == FALSE && info->ecall == TRUE))
-                       goto err;
+       if (ecall_session != NULL) {
+               if (ecall_session->ecall == TRUE && ecall_session != session)
+                       return __connman_error_failed(msg, EBUSY);
 
-               ecall_info = info;
-       } else if (ecall_info == info) {
-               if (!(info_last->ecall == TRUE && info->ecall == FALSE))
-                       goto err;
-
-               ecall_info = NULL;
-       } else {
-               goto err;
+               session->ecall = FALSE;
        }
 
-       update_ecall_sessions(session);
-
-       return;
+       session_changed(session, CONNMAN_SESSION_TRIGGER_DISCONNECT);
 
-err:
-       /* not a valid transition */
-       info->ecall = info_last->ecall;
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
 }
 
 static DBusMessage *change_session(DBusConnection *conn,
@@ -1403,62 +1474,40 @@ static DBusMessage *change_session(DBusConnection *conn,
        const char *name;
        const char *val;
        GSList *allowed_bearers;
+       int err;
 
        DBG("session %p", session);
        if (dbus_message_iter_init(msg, &iter) == FALSE)
                return __connman_error_invalid_arguments(msg);
 
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return __connman_error_invalid_arguments(msg);
+
        dbus_message_iter_get_basic(&iter, &name);
        dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return __connman_error_invalid_arguments(msg);
+
        dbus_message_iter_recurse(&iter, &value);
 
        switch (dbus_message_iter_get_arg_type(&value)) {
        case DBUS_TYPE_ARRAY:
                if (g_str_equal(name, "AllowedBearers") == TRUE) {
-                       allowed_bearers = session_parse_allowed_bearers(&value);
+                       err = parse_bearers(&value, &allowed_bearers);
+                       if (err < 0)
+                               return __connman_error_failed(msg, err);
 
-                       g_slist_foreach(info->allowed_bearers,
-                                       cleanup_bearer_info, NULL);
-                       g_slist_free(info->allowed_bearers);
+                       g_slist_free(info->config.allowed_bearers);
+                       session->user_allowed_bearers = allowed_bearers;
 
-                       if (allowed_bearers == NULL) {
-                               allowed_bearers = session_allowed_bearers_any();
-
-                               if (allowed_bearers == NULL)
-                                       return __connman_error_failed(msg, ENOMEM);
-                       }
+                       err = apply_policy_on_bearers(
+                                       session->policy_config->allowed_bearers,
+                                       session->user_allowed_bearers,
+                                       &info->config.allowed_bearers);
 
-                       info->allowed_bearers = allowed_bearers;
-               } else {
-                       goto err;
-               }
-               break;
-       case DBUS_TYPE_BOOLEAN:
-               if (g_str_equal(name, "Priority") == TRUE) {
-                       dbus_message_iter_get_basic(&value,
-                                       &info->priority);
-               } else if (g_str_equal(name, "AvoidHandover") == TRUE) {
-                       dbus_message_iter_get_basic(&value,
-                                       &info->avoid_handover);
-               } else if (g_str_equal(name, "StayConnected") == TRUE) {
-                       dbus_message_iter_get_basic(&value,
-                                       &info->stay_connected);
-               } else if (g_str_equal(name, "EmergencyCall") == TRUE) {
-                       dbus_message_iter_get_basic(&value,
-                                       &info->ecall);
-
-                       update_ecall(session);
-               } else {
-                       goto err;
-               }
-               break;
-       case DBUS_TYPE_UINT32:
-               if (g_str_equal(name, "PeriodicConnect") == TRUE) {
-                       dbus_message_iter_get_basic(&value,
-                                       &info->periodic_connect);
-               } else if (g_str_equal(name, "IdleTimeout") == TRUE) {
-                       dbus_message_iter_get_basic(&value,
-                                       &info->idle_timeout);
+                       if (err < 0)
+                               return __connman_error_failed(msg, err);
                } else {
                        goto err;
                }
@@ -1466,11 +1515,9 @@ static DBusMessage *change_session(DBusConnection *conn,
        case DBUS_TYPE_STRING:
                if (g_str_equal(name, "ConnectionType") == TRUE) {
                        dbus_message_iter_get_basic(&value, &val);
-                       info->type = string2type(val);
-               } else if (g_str_equal(name, "RoamingPolicy") == TRUE) {
-                       dbus_message_iter_get_basic(&value, &val);
-                       info->roaming_policy =
-                                       string2roamingpolicy(val);
+                       info->config.type = apply_policy_on_type(
+                               session->policy_config->type,
+                               connman_session_parse_connection_type(val));
                } else {
                        goto err;
                }
@@ -1522,8 +1569,7 @@ static int session_disconnect(struct connman_session *session)
        g_dbus_unregister_interface(connection, session->session_path,
                                                CONNMAN_SESSION_INTERFACE);
 
-       deselect_and_disconnect(session,
-                               CONNMAN_SESSION_REASON_DISCONNECT);
+       deselect_and_disconnect(session);
 
        g_hash_table_remove(session_hash, session->session_path);
 
@@ -1543,11 +1589,10 @@ static DBusMessage *destroy_session(DBusConnection *conn,
                                        DBusMessage *msg, void *user_data)
 {
        struct connman_session *session = user_data;
-       struct session_info *info = session->info;
 
        DBG("session %p", session);
 
-       if (ecall_info != NULL && ecall_info != info)
+       if (ecall_session != NULL && ecall_session != session)
                return __connman_error_failed(msg, EBUSY);
 
        session_disconnect(session);
@@ -1555,38 +1600,119 @@ static DBusMessage *destroy_session(DBusConnection *conn,
        return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
 }
 
-static GDBusMethodTable session_methods[] = {
-       { "Destroy",    "",   "", destroy_session    },
-       { "Connect",    "",   "", connect_session    },
-       { "Disconnect", "",   "", disconnect_session },
-       { "Change",     "sv", "", change_session     },
+static const GDBusMethodTable session_methods[] = {
+       { GDBUS_METHOD("Destroy", NULL, NULL, destroy_session) },
+       { GDBUS_METHOD("Connect", NULL, NULL, connect_session) },
+       { GDBUS_METHOD("Disconnect", NULL, NULL,
+                       disconnect_session ) },
+       { GDBUS_METHOD("Change",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" }),
+                       NULL, change_session) },
        { },
 };
 
+struct user_config {
+       DBusMessage *pending;
+
+       enum connman_session_type type;
+       GSList *allowed_bearers;
+};
+
+static void session_create_cb(struct connman_session *session,
+                               struct connman_session_config *config,
+                               void *user_data, int err)
+{
+       DBusMessage *reply;
+       struct user_config *user_config = user_data;
+       struct session_info *info, *info_last;
+
+       DBG("session %p config %p", session, config);
+
+       if (err != 0)
+               goto out;
+
+       session->policy_config = config;
+
+       info = session->info;
+       info_last = session->info_last;
+
+       if (session->policy_config->ecall == TRUE)
+               ecall_session = session;
+
+       info->state = CONNMAN_SESSION_STATE_DISCONNECTED;
+       info->config.type = apply_policy_on_type(
+                               session->policy_config->type,
+                               user_config->type);
+       info->config.priority = session->policy_config->priority;
+       info->config.roaming_policy = session->policy_config->roaming_policy;
+       info->entry = NULL;
+
+       session->user_allowed_bearers = user_config->allowed_bearers;
+
+       err = apply_policy_on_bearers(
+                       session->policy_config->allowed_bearers,
+                       session->user_allowed_bearers,
+                       &info->config.allowed_bearers);
+       if (err < 0)
+               goto out;
+
+       g_hash_table_replace(session_hash, session->session_path, session);
+
+       DBG("add %s", session->session_path);
+
+       if (g_dbus_register_interface(connection, session->session_path,
+                                       CONNMAN_SESSION_INTERFACE,
+                                       session_methods, NULL,
+                                       NULL, session, NULL) == FALSE) {
+               connman_error("Failed to register %s", session->session_path);
+               g_hash_table_remove(session_hash, session->session_path);
+               err = -EINVAL;
+               goto out;
+       }
+
+       reply = g_dbus_create_reply(user_config->pending,
+                               DBUS_TYPE_OBJECT_PATH, &session->session_path,
+                               DBUS_TYPE_INVALID);
+       g_dbus_send_message(connection, reply);
+
+       populate_service_list(session);
+
+       info_last->state = info->state;
+       info_last->config.priority = info->config.priority;
+       info_last->config.roaming_policy = info->config.roaming_policy;
+       info_last->entry = info->entry;
+       info_last->config.allowed_bearers = info->config.allowed_bearers;
+
+       session->append_all = TRUE;
+
+       session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING);
+
+out:
+       if (err < 0) {
+               __connman_error_failed(user_config->pending, err);
+               free_session(session);
+       }
+
+       dbus_message_unref(user_config->pending);
+       g_free(user_config);
+}
+
 int __connman_session_create(DBusMessage *msg)
 {
        const char *owner, *notify_path;
        char *session_path = NULL;
        DBusMessageIter iter, array;
        struct connman_session *session = NULL;
-       struct session_info *info, *info_last;
-
-       enum connman_session_type type = CONNMAN_SESSION_TYPE_ANY;
-       connman_bool_t priority = FALSE, avoid_handover = FALSE;
-       connman_bool_t stay_connected = FALSE, ecall = FALSE;
-       enum connman_session_roaming_policy roaming_policy =
-                               CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN;
-       GSList *allowed_bearers = NULL;
-       unsigned int periodic_connect = 0;
-       unsigned int idle_timeout = 0;
-
+       struct user_config *user_config = NULL;
+       connman_bool_t user_allowed_bearers = FALSE;
+       connman_bool_t user_connection_type = FALSE;
        int err;
 
        owner = dbus_message_get_sender(msg);
 
        DBG("owner %s", owner);
 
-       if (ecall_info != NULL) {
+       if (ecall_session != NULL && ecall_session->ecall == TRUE) {
                /*
                 * If there is an emergency call already going on,
                 * ignore session creation attempt
@@ -1595,6 +1721,14 @@ int __connman_session_create(DBusMessage *msg)
                goto err;
        }
 
+       user_config = g_try_new0(struct user_config, 1);
+       if (user_config == NULL) {
+               err = -ENOMEM;
+               goto err;
+       }
+
+       user_config->pending = dbus_message_ref(msg);
+
        dbus_message_iter_init(msg, &iter);
        dbus_message_iter_recurse(&iter, &array);
 
@@ -1611,54 +1745,52 @@ int __connman_session_create(DBusMessage *msg)
                switch (dbus_message_iter_get_arg_type(&value)) {
                case DBUS_TYPE_ARRAY:
                        if (g_str_equal(key, "AllowedBearers") == TRUE) {
-                               allowed_bearers =
-                                       session_parse_allowed_bearers(&value);
-                       } else {
-                               return -EINVAL;
-                       }
-                       break;
-               case DBUS_TYPE_BOOLEAN:
-                       if (g_str_equal(key, "Priority") == TRUE) {
-                               dbus_message_iter_get_basic(&value,
-                                                       &priority);
-                       } else if (g_str_equal(key, "AvoidHandover") == TRUE) {
-                               dbus_message_iter_get_basic(&value,
-                                                       &avoid_handover);
-                       } else if (g_str_equal(key, "StayConnected") == TRUE) {
-                               dbus_message_iter_get_basic(&value,
-                                                       &stay_connected);
-                       } else if (g_str_equal(key, "EmergencyCall") == TRUE) {
-                               dbus_message_iter_get_basic(&value,
-                                                       &ecall);
-                       } else {
-                               return -EINVAL;
-                       }
-                       break;
-               case DBUS_TYPE_UINT32:
-                       if (g_str_equal(key, "PeriodicConnect") == TRUE) {
-                               dbus_message_iter_get_basic(&value,
-                                                       &periodic_connect);
-                       } else if (g_str_equal(key, "IdleTimeout") == TRUE) {
-                               dbus_message_iter_get_basic(&value,
-                                                       &idle_timeout);
+                               err = parse_bearers(&value,
+                                               &user_config->allowed_bearers);
+                               if (err < 0)
+                                       goto err;
+
+                               user_allowed_bearers = TRUE;
                        } else {
-                               return -EINVAL;
+                               err = -EINVAL;
+                               goto err;
                        }
                        break;
                case DBUS_TYPE_STRING:
                        if (g_str_equal(key, "ConnectionType") == TRUE) {
                                dbus_message_iter_get_basic(&value, &val);
-                               type = string2type(val);
-                       } else if (g_str_equal(key, "RoamingPolicy") == TRUE) {
-                               dbus_message_iter_get_basic(&value, &val);
-                               roaming_policy = string2roamingpolicy(val);
+                               user_config->type =
+                                       connman_session_parse_connection_type(val);
+
+                               user_connection_type = TRUE;
                        } else {
-                               return -EINVAL;
+                               err = -EINVAL;
+                               goto err;
                        }
                }
                dbus_message_iter_next(&array);
        }
 
+       /*
+        * If the user hasn't provided a configuration, we set
+        * the default configuration.
+        *
+        * For AllowedBearers this is '*', ...
+        */
+       if (user_allowed_bearers == FALSE) {
+               user_config->allowed_bearers =
+                       g_slist_append(NULL,
+                               GINT_TO_POINTER(CONNMAN_SERVICE_TYPE_UNKNOWN));
+               if (user_config->allowed_bearers == NULL) {
+                       err = -ENOMEM;
+                       goto err;
+               }
+       }
+
+       /* ... and for ConnectionType it is 'any'. */
+       if (user_connection_type == FALSE)
+               user_config->type = CONNMAN_SESSION_TYPE_ANY;
+
        dbus_message_iter_next(&iter);
        dbus_message_iter_get_basic(&iter, &notify_path);
 
@@ -1675,6 +1807,7 @@ int __connman_session_create(DBusMessage *msg)
 
        session = g_hash_table_lookup(session_hash, session_path);
        if (session != NULL) {
+               g_free(session_path);
                session = NULL;
                err = -EEXIST;
                goto err;
@@ -1682,10 +1815,13 @@ int __connman_session_create(DBusMessage *msg)
 
        session = g_try_new0(struct connman_session, 1);
        if (session == NULL) {
+               g_free(session_path);
                err = -ENOMEM;
                goto err;
        }
 
+       session->session_path = session_path;
+
        session->info = g_try_new0(struct session_info, 1);
        if (session->info == NULL) {
                err = -ENOMEM;
@@ -1698,102 +1834,40 @@ int __connman_session_create(DBusMessage *msg)
                goto err;
        }
 
-       info = session->info;
-       info_last = session->info_last;
-
        session->owner = g_strdup(owner);
-       session->session_path = session_path;
        session->notify_path = g_strdup(notify_path);
        session->notify_watch =
                g_dbus_add_disconnect_watch(connection, session->owner,
                                        owner_disconnect, session, NULL);
 
-       info->state = CONNMAN_SESSION_STATE_DISCONNECTED;
-       info->type = type;
-       info->priority = priority;
-       info->avoid_handover = avoid_handover;
-       info->stay_connected = stay_connected;
-       info->periodic_connect = periodic_connect;
-       info->idle_timeout = idle_timeout;
-       info->ecall = ecall;
-       info->roaming_policy = roaming_policy;
-       info->entry = NULL;
-       info->marker = 0;
-
-       if (allowed_bearers == NULL) {
-               info->allowed_bearers =
-                               session_allowed_bearers_any();
-
-               if (info->allowed_bearers == NULL) {
-                       err = -ENOMEM;
-                       goto err;
-               }
-       } else {
-               info->allowed_bearers = allowed_bearers;
-       }
-
-       g_hash_table_replace(session_hash, session->session_path, session);
-
-       DBG("add %s", session->session_path);
-
-       if (g_dbus_register_interface(connection, session->session_path,
-                                       CONNMAN_SESSION_INTERFACE,
-                                       session_methods, NULL,
-                                       NULL, session, NULL) == FALSE) {
-               connman_error("Failed to register %s", session->session_path);
-               g_hash_table_remove(session_hash, session->session_path);
-               session = NULL;
-
-               err = -EINVAL;
+       err = assign_policy_plugin(session);
+       if (err < 0)
                goto err;
-       }
-
-       g_dbus_send_reply(connection, msg,
-                               DBUS_TYPE_OBJECT_PATH, &session->session_path,
-                               DBUS_TYPE_INVALID);
-
-
-       populate_service_list(session);
-       if (info->ecall == TRUE) {
-               ecall_info = info;
-               update_ecall_sessions(session);
-       }
-
-       info_last->state = info->state;
-       info_last->priority = info->priority;
-       info_last->avoid_handover = info->avoid_handover;
-       info_last->stay_connected = info->stay_connected;
-       info_last->periodic_connect = info->periodic_connect;
-       info_last->idle_timeout = info->idle_timeout;
-       info_last->ecall = info->ecall;
-       info_last->roaming_policy = info->roaming_policy;
-       info_last->entry = info->entry;
-       info_last->marker = info->marker;
-       info_last->allowed_bearers = info->allowed_bearers;
-
-       session->append_all = TRUE;
 
-       session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING);
+       err = create_policy_config(session, session_create_cb, user_config);
+       if (err < 0)
+               goto err;
 
-       return 0;
+       return -EINPROGRESS;
 
 err:
        connman_error("Failed to create session");
 
-       if (session != NULL) {
-               if (session->info_last != NULL)
-                       g_free(session->info_last);
-               if (session->info != NULL)
-                       g_free(session->info);
-               g_free(session);
-       }
+       free_session(session);
 
-       g_free(session_path);
+       if (user_config != NULL) {
+               dbus_message_unref(user_config->pending);
+               g_slist_free(user_config->allowed_bearers);
+               g_free(user_config);
+       }
+       return err;
+}
 
-       g_slist_foreach(allowed_bearers, cleanup_bearer_info, NULL);
-       g_slist_free(allowed_bearers);
+void connman_session_destroy(struct connman_session *session)
+{
+       DBG("session %p", session);
 
-       return err;
+       session_disconnect(session);
 }
 
 int __connman_session_destroy(DBusMessage *msg)
@@ -1817,7 +1891,7 @@ int __connman_session_destroy(DBusMessage *msg)
        if (g_strcmp0(owner, session->owner) != 0)
                return -EACCES;
 
-       session_disconnect(session);
+       connman_session_destroy(session);
 
        return 0;
 }
@@ -1892,15 +1966,15 @@ static void service_remove(struct connman_service *service)
        g_hash_table_iter_init(&iter, session_hash);
 
        while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
-               GSequenceIter *iter;
+               GSequenceIter *seq_iter;
                session = value;
                info = session->info;
 
-               iter = g_hash_table_lookup(session->service_hash, service);
-               if (iter == NULL)
+               seq_iter = g_hash_table_lookup(session->service_hash, service);
+               if (seq_iter == NULL)
                        continue;
 
-               g_sequence_remove(iter);
+               g_sequence_remove(seq_iter);
 
                if (info->entry != NULL && info->entry->service == service)
                        info->entry = NULL;