service: Return 6to4 tunnel status to dbus caller.
[platform/upstream/connman.git] / src / service.c
index 893ccba..ae1f6fd 100644 (file)
@@ -35,31 +35,22 @@ static DBusConnection *connection = NULL;
 
 static GSequence *service_list = NULL;
 static GHashTable *service_hash = NULL;
+static GSList *counter_list = NULL;
 
 struct connman_stats {
        connman_bool_t valid;
        connman_bool_t enabled;
-       unsigned int rx_packets_last;
-       unsigned int tx_packets_last;
-       unsigned int rx_packets;
-       unsigned int tx_packets;
-       unsigned int rx_bytes_last;
-       unsigned int tx_bytes_last;
-       unsigned int rx_bytes;
-       unsigned int tx_bytes;
-       unsigned int rx_errors_last;
-       unsigned int tx_errors_last;
-       unsigned int rx_errors;
-       unsigned int tx_errors;
-       unsigned int rx_dropped_last;
-       unsigned int tx_dropped_last;
-       unsigned int rx_dropped;
-       unsigned int tx_dropped;
-       unsigned int time_start;
-       unsigned int time;
+       struct connman_stats_data data_last;
+       struct connman_stats_data data;
        GTimer *timer;
 };
 
+struct connman_stats_counter {
+       connman_bool_t append_all;
+       struct connman_stats stats;
+       struct connman_stats stats_roaming;
+};
+
 struct connman_service {
        gint refcount;
        char *identifier;
@@ -67,7 +58,8 @@ struct connman_service {
        enum connman_service_type type;
        enum connman_service_mode mode;
        enum connman_service_security security;
-       enum connman_service_state state;
+       enum connman_service_state state_ipv4;
+       enum connman_service_state state_ipv6;
        enum connman_service_error error;
        connman_uint8_t strength;
        connman_bool_t favorite;
@@ -81,20 +73,17 @@ struct connman_service {
        char *name;
        char *passphrase;
        char *profile;
-       char *apn;
-       char *username;
-       char *password;
-       char *mcc;
-       char *mnc;
        connman_bool_t roaming;
        connman_bool_t login_required;
-       struct connman_ipconfig *ipconfig;
+       struct connman_ipconfig *ipconfig_ipv4;
+       struct connman_ipconfig *ipconfig_ipv6;
        struct connman_network *network;
        struct connman_provider *provider;
        char **nameservers;
-       char *nameserver;
+       char **nameservers_config;
        char **domains;
        char *domainname;
+       char **timeservers;
        /* 802.1x settings from the config files */
        char *eap;
        char *identity;
@@ -107,6 +96,14 @@ struct connman_service {
        guint timeout;
        struct connman_location *location;
        struct connman_stats stats;
+       struct connman_stats stats_roaming;
+       GHashTable *counter_table;
+       enum connman_service_proxy_method proxy;
+       enum connman_service_proxy_method proxy_config;
+       char **proxies;
+       char **excludes;
+       char *pac;
+       connman_bool_t wps;
 };
 
 static void append_path(gpointer value, gpointer user_data)
@@ -175,6 +172,8 @@ const char *__connman_service_type2string(enum connman_service_type type)
                return "gps";
        case CONNMAN_SERVICE_TYPE_VPN:
                return "vpn";
+       case CONNMAN_SERVICE_TYPE_GADGET:
+               return "gadget";
        }
 
        return NULL;
@@ -259,6 +258,12 @@ static const char *error2string(enum connman_service_error error)
                return "dhcp-failed";
        case CONNMAN_SERVICE_ERROR_CONNECT_FAILED:
                return "connect-failed";
+       case CONNMAN_SERVICE_ERROR_LOGIN_FAILED:
+               return "login-failed";
+       case CONNMAN_SERVICE_ERROR_AUTH_FAILED:
+               return "auth-failed";
+       case CONNMAN_SERVICE_ERROR_INVALID_KEY:
+               return "invalid-key";
        }
 
        return NULL;
@@ -270,16 +275,160 @@ static enum connman_service_error string2error(const char *error)
                return CONNMAN_SERVICE_ERROR_DHCP_FAILED;
        else if (g_strcmp0(error, "pin-missing") == 0)
                return CONNMAN_SERVICE_ERROR_PIN_MISSING;
+       else if (g_strcmp0(error, "invalid-key") == 0)
+               return CONNMAN_SERVICE_ERROR_INVALID_KEY;
 
        return CONNMAN_SERVICE_ERROR_UNKNOWN;
 }
 
-static connman_bool_t is_connecting(struct connman_service *service)
+static const char *proxymethod2string(enum connman_service_proxy_method method)
+{
+       switch (method) {
+       case CONNMAN_SERVICE_PROXY_METHOD_DIRECT:
+               return "direct";
+       case CONNMAN_SERVICE_PROXY_METHOD_MANUAL:
+               return "manual";
+       case CONNMAN_SERVICE_PROXY_METHOD_AUTO:
+               return "auto";
+       case CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN:
+               break;
+       }
+
+       return NULL;
+}
+
+static enum connman_service_proxy_method string2proxymethod(const char *method)
+{
+       if (g_strcmp0(method, "direct") == 0)
+               return CONNMAN_SERVICE_PROXY_METHOD_DIRECT;
+       else if (g_strcmp0(method, "auto") == 0)
+               return CONNMAN_SERVICE_PROXY_METHOD_AUTO;
+       else if (g_strcmp0(method, "manual") == 0)
+               return CONNMAN_SERVICE_PROXY_METHOD_MANUAL;
+       else
+               return CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN;
+}
+
+static enum connman_service_state combine_state(
+                                       enum connman_service_state state_a,
+                                       enum connman_service_state state_b)
+{
+       enum connman_service_state result;
+
+       if (state_a == state_b) {
+               result = state_a;
+               goto done;
+       }
+
+       if (state_a == CONNMAN_SERVICE_STATE_UNKNOWN) {
+               result = state_b;
+               goto done;
+       }
+
+       if (state_b == CONNMAN_SERVICE_STATE_UNKNOWN) {
+               result = state_a;
+               goto done;
+       }
+
+       if (state_a == CONNMAN_SERVICE_STATE_IDLE) {
+               result = state_b;
+               goto done;
+       }
+
+       if (state_b == CONNMAN_SERVICE_STATE_IDLE) {
+               result = state_a;
+               goto done;
+       }
+
+       if (state_a == CONNMAN_SERVICE_STATE_ASSOCIATION) {
+               if (state_b == CONNMAN_SERVICE_STATE_CONFIGURATION ||
+                               state_b == CONNMAN_SERVICE_STATE_ONLINE ||
+                               state_b == CONNMAN_SERVICE_STATE_READY)
+                       result = state_b;
+               else
+                       result = state_a;
+               goto done;
+       }
+
+       if (state_b == CONNMAN_SERVICE_STATE_ASSOCIATION) {
+               if (state_a == CONNMAN_SERVICE_STATE_CONFIGURATION ||
+                               state_a == CONNMAN_SERVICE_STATE_ONLINE ||
+                               state_a == CONNMAN_SERVICE_STATE_READY)
+                       result = state_a;
+               else
+                       result = state_b;
+               goto done;
+       }
+
+       if (state_a == CONNMAN_SERVICE_STATE_CONFIGURATION) {
+               if (state_b == CONNMAN_SERVICE_STATE_ONLINE ||
+                               state_b == CONNMAN_SERVICE_STATE_READY)
+                       result = state_b;
+               else
+                       result = state_a;
+               goto done;
+       }
+
+       if (state_b == CONNMAN_SERVICE_STATE_CONFIGURATION) {
+               if (state_a == CONNMAN_SERVICE_STATE_ONLINE ||
+                               state_a == CONNMAN_SERVICE_STATE_READY)
+                       result = state_a;
+               else
+                       result = state_b;
+               goto done;
+       }
+
+       if (state_a == CONNMAN_SERVICE_STATE_READY) {
+               if (state_b == CONNMAN_SERVICE_STATE_ONLINE)
+                       result = state_b;
+               else
+                       result = state_a;
+               goto done;
+       }
+
+       if (state_b == CONNMAN_SERVICE_STATE_READY) {
+               if (state_a == CONNMAN_SERVICE_STATE_ONLINE)
+                       result = state_a;
+               else
+                       result = state_b;
+               goto done;
+       }
+
+       if (state_a == CONNMAN_SERVICE_STATE_ONLINE) {
+               result = state_a;
+               goto done;
+       }
+
+       if (state_b == CONNMAN_SERVICE_STATE_ONLINE) {
+               result = state_b;
+               goto done;
+       }
+
+       if (state_a == CONNMAN_SERVICE_STATE_DISCONNECT) {
+               result = state_b;
+               goto done;
+       }
+
+       if (state_b == CONNMAN_SERVICE_STATE_DISCONNECT) {
+               result = state_a;
+               goto done;
+       }
+
+       result = CONNMAN_SERVICE_STATE_FAILURE;
+
+done:
+       return result;
+}
+
+static connman_bool_t is_connecting_state(struct connman_service *service,
+                                       enum connman_service_state state)
 {
-       switch (service->state) {
+       switch (state) {
        case CONNMAN_SERVICE_STATE_UNKNOWN:
        case CONNMAN_SERVICE_STATE_IDLE:
        case CONNMAN_SERVICE_STATE_FAILURE:
+               if (service->network != NULL)
+                       return connman_network_get_connecting(service->network);
        case CONNMAN_SERVICE_STATE_DISCONNECT:
        case CONNMAN_SERVICE_STATE_READY:
        case CONNMAN_SERVICE_STATE_ONLINE:
@@ -292,9 +441,10 @@ static connman_bool_t is_connecting(struct connman_service *service)
        return FALSE;
 }
 
-static connman_bool_t is_connected(const struct connman_service *service)
+static connman_bool_t is_connected_state(const struct connman_service *service,
+                                       enum connman_service_state state)
 {
-       switch (service->state) {
+       switch (state) {
        case CONNMAN_SERVICE_STATE_UNKNOWN:
        case CONNMAN_SERVICE_STATE_IDLE:
        case CONNMAN_SERVICE_STATE_ASSOCIATION:
@@ -310,14 +460,39 @@ static connman_bool_t is_connected(const struct connman_service *service)
        return FALSE;
 }
 
+static connman_bool_t is_connecting(struct connman_service *service)
+{
+       enum connman_service_state state;
+
+       state = combine_state(service->state_ipv4, service->state_ipv6);
+
+       return is_connecting_state(service, state);
+}
+
+static connman_bool_t is_connected(struct connman_service *service)
+{
+       enum connman_service_state state;
+
+       state = combine_state(service->state_ipv4, service->state_ipv6);
+
+       return is_connected_state(service, state);
+}
+
 static void update_nameservers(struct connman_service *service)
 {
-       const char *ifname = connman_ipconfig_get_ifname(service->ipconfig);
+       const char *ifname;
+
+       if (service->ipconfig_ipv4)
+               ifname = connman_ipconfig_get_ifname(service->ipconfig_ipv4);
+       else if (service->ipconfig_ipv6)
+               ifname = connman_ipconfig_get_ifname(service->ipconfig_ipv6);
+       else
+               ifname = NULL;
 
        if (ifname == NULL)
                return;
 
-       switch (service->state) {
+       switch (combine_state(service->state_ipv4, service->state_ipv6)) {
        case CONNMAN_SERVICE_STATE_UNKNOWN:
        case CONNMAN_SERVICE_STATE_IDLE:
        case CONNMAN_SERVICE_STATE_ASSOCIATION:
@@ -334,59 +509,164 @@ static void update_nameservers(struct connman_service *service)
 
        connman_resolver_remove_all(ifname);
 
-       if (service->nameservers != NULL) {
+       if (service->nameservers_config != NULL) {
+               int i;
+
+               for (i = 0; service->nameservers_config[i] != NULL; i++) {
+                       connman_resolver_append(ifname, NULL,
+                                               service->nameservers_config[i]);
+               }
+       } else if (service->nameservers != NULL) {
                int i;
 
-               for (i = 0; service->nameservers[i]; i++)
+               for (i = 0; service->nameservers[i] != NULL; i++) {
                        connman_resolver_append(ifname, NULL,
                                                service->nameservers[i]);
-       } else if (service->nameserver != NULL)
-               connman_resolver_append(ifname, NULL, service->nameserver);
+               }
+       }
+
+       if (service->domains != NULL) {
+               int i;
+
+               for (i = 0; service->domains[i]; i++)
+                       connman_resolver_append(ifname, service->domains[i],
+                                               NULL);
+       } else if (service->domainname != NULL)
+               connman_resolver_append(ifname, service->domainname, NULL);
 
        connman_resolver_flush();
 }
 
-void __connman_service_append_nameserver(struct connman_service *service,
+int __connman_service_nameserver_append(struct connman_service *service,
                                                const char *nameserver)
 {
-       DBG("service %p nameserver %s", service, nameserver);
+       int len;
+
+       DBG("service %p nameserver %s", service, nameserver);
 
        if (nameserver == NULL)
-               return;
+               return -EINVAL;
+
+       if (service->nameservers != NULL) {
+               int i;
+
+               for (i = 0; service->nameservers[i] != NULL; i++)
+                       if (g_strcmp0(service->nameservers[i], nameserver) == 0)
+                               return -EEXIST;
 
-       g_free(service->nameserver);
-       service->nameserver = g_strdup(nameserver);
+               len = g_strv_length(service->nameservers);
+               service->nameservers = g_try_renew(char *, service->nameservers,
+                                                       len + 2);
+       } else {
+               len = 0;
+               service->nameservers = g_try_new0(char *, len + 2);
+       }
+
+       if (service->nameservers == NULL)
+               return -ENOMEM;
+
+       service->nameservers[len] = g_strdup(nameserver);
+       service->nameservers[len + 1] = NULL;
 
        update_nameservers(service);
+
+       return 0;
 }
 
-void __connman_service_remove_nameserver(struct connman_service *service,
+int __connman_service_nameserver_remove(struct connman_service *service,
                                                const char *nameserver)
 {
+       char **servers;
+       int len, i, j;
+
        DBG("service %p nameserver %s", service, nameserver);
 
        if (nameserver == NULL)
-               return;
+               return -EINVAL;
+
+       if (service->nameservers == NULL)
+               return 0;
+
+       len = g_strv_length(service->nameservers);
+       if (len == 1) {
+               if (g_strcmp0(service->nameservers[0], nameserver) != 0)
+                       return 0;
 
-       g_free(service->nameserver);
-       service->nameserver = NULL;
+               g_strfreev(service->nameservers);
+               service->nameservers = NULL;
+
+               return 0;
+       }
+
+       servers = g_try_new0(char *, len - 1);
+       if (servers == NULL)
+               return -ENOMEM;
+
+       for (i = 0, j = 0; i < len; i++) {
+               if (g_strcmp0(service->nameservers[i], nameserver) != 0) {
+                       servers[j] = g_strdup(service->nameservers[i]);
+                       j++;
+               }
+       }
+       servers[len - 2] = NULL;
+
+       g_strfreev(service->nameservers);
+       service->nameservers = servers;
+
+       update_nameservers(service);
+
+       return 0;
+}
+
+void __connman_service_nameserver_clear(struct connman_service *service)
+{
+       g_strfreev(service->nameservers);
+       service->nameservers = NULL;
 
        update_nameservers(service);
 }
 
+static void nameserver_add_routes(int index, char **nameservers,
+                                       const char *gw)
+{
+       int i;
+
+       for (i = 0; nameservers[i] != NULL; i++) {
+               if (connman_inet_compare_subnet(index, nameservers[i]))
+                       continue;
+
+               connman_inet_add_host_route(index, nameservers[i], gw);
+       }
+}
+
+static void nameserver_del_routes(int index, char **nameservers)
+{
+       int i;
+
+       for (i = 0; nameservers[i] != NULL; i++)
+               connman_inet_del_host_route(index, nameservers[i]);
+}
+
 void __connman_service_nameserver_add_routes(struct connman_service *service,
                                                const char *gw)
 {
-       int index;
+       int index = -1;
 
        if (service == NULL)
                return;
 
-       index = connman_network_get_index(service->network);
-
-       if (service->nameservers != NULL) {
-               int i;
+       if (service->network != NULL)
+               index = connman_network_get_index(service->network);
+       else if (service->provider != NULL)
+               index = connman_provider_get_index(service->provider);
 
+       if (service->nameservers_config != NULL) {
+               /*
+                * Configured nameserver takes preference over the
+                * discoverd nameserver gathered from DHCP, VPN, etc.
+                */
+               nameserver_add_routes(index, service->nameservers_config, gw);
+       } else if (service->nameservers != NULL) {
                /*
                 * We add nameservers host routes for nameservers that
                 * are not on our subnet. For those who are, the subnet
@@ -394,192 +674,114 @@ void __connman_service_nameserver_add_routes(struct connman_service *service,
                 * tries to reach them. The subnet route is installed
                 * when setting the interface IP address.
                 */
-               for (i = 0; service->nameservers[i]; i++) {
-                       if (connman_inet_compare_subnet(index,
-                                                       service->nameservers[i]))
-                               continue;
-
-                       connman_inet_add_host_route(index,
-                                               service->nameservers[i], gw);
-               }
-       } else if (service->nameserver != NULL) {
-               if (connman_inet_compare_subnet(index, service->nameserver))
-                       return;
-
-               connman_inet_add_host_route(index, service->nameserver, gw);
+               nameserver_add_routes(index, service->nameservers, gw);
        }
 }
 
 void __connman_service_nameserver_del_routes(struct connman_service *service)
 {
-       int index;
+       int index = -1;
 
        if (service == NULL)
                return;
 
-       index = connman_network_get_index(service->network);
+       if (service->network != NULL)
+               index = connman_network_get_index(service->network);
+       else if (service->provider != NULL)
+               index = connman_provider_get_index(service->provider);
 
-       if (service->nameservers != NULL) {
-               int i;
+       if (service->nameservers_config != NULL)
+               nameserver_del_routes(index, service->nameservers_config);
+       else if (service->nameservers != NULL)
+               nameserver_del_routes(index, service->nameservers);
+}
 
-               for (i = 0; service->nameservers[i]; i++)
-                       connman_inet_del_host_route(index,
-                                               service->nameservers[i]);
-       } else if (service->nameserver != NULL) {
-               connman_inet_del_host_route(index, service->nameserver);
-       }
+static struct connman_stats *stats_get(struct connman_service *service)
+{
+       if (service->roaming == TRUE)
+               return &service->stats_roaming;
+       else
+               return &service->stats;
+}
+
+static connman_bool_t stats_enabled(struct connman_service *service)
+{
+       struct connman_stats *stats = stats_get(service);
+
+       return stats->enabled;
 }
 
 static void stats_start(struct connman_service *service)
 {
+       struct connman_stats *stats = stats_get(service);
+
        DBG("service %p", service);
 
-       if (service->stats.timer == NULL)
+       if (stats->timer == NULL)
                return;
 
-       service->stats.enabled = TRUE;
-
-       service->stats.time_start = service->stats.time;
-
-       g_timer_start(service->stats.timer);
+       stats->enabled = TRUE;
+       stats->data_last.time = stats->data.time;
 
-       __connman_counter_add_service(service);
+       g_timer_start(stats->timer);
 }
 
 static void stats_stop(struct connman_service *service)
 {
+       struct connman_stats *stats = stats_get(service);
        unsigned int seconds;
 
        DBG("service %p", service);
 
-       if (service->stats.timer == NULL)
+       if (stats->timer == NULL)
                return;
 
-       if (service->stats.enabled == FALSE)
+       if (stats->enabled == FALSE)
                return;
 
-       __connman_counter_remove_service(service);
+       g_timer_stop(stats->timer);
 
-       g_timer_stop(service->stats.timer);
-
-       seconds = g_timer_elapsed(service->stats.timer, NULL);
-       service->stats.time = service->stats.time_start + seconds;
-
-       service->stats.enabled = FALSE;
-}
-
-static int stats_load(struct connman_service *service,
-               GKeyFile *keyfile, const char *identifier)
-{
-       service->stats.rx_packets = g_key_file_get_integer(keyfile,
-                               identifier, "rx_packets", NULL);
-       service->stats.tx_packets = g_key_file_get_integer(keyfile,
-                               identifier, "tx_packets", NULL);
-       service->stats.rx_bytes = g_key_file_get_integer(keyfile,
-                               identifier, "rx_bytes", NULL);
-       service->stats.tx_bytes = g_key_file_get_integer(keyfile,
-                               identifier, "tx_bytes", NULL);
-       service->stats.rx_errors = g_key_file_get_integer(keyfile,
-                               identifier, "rx_errors", NULL);
-       service->stats.tx_errors = g_key_file_get_integer(keyfile,
-                               identifier, "tx_errors", NULL);
-       service->stats.rx_dropped = g_key_file_get_integer(keyfile,
-                               identifier, "rx_dropped", NULL);
-       service->stats.tx_dropped = g_key_file_get_integer(keyfile,
-                               identifier, "tx_dropped", NULL);
-       service->stats.time = g_key_file_get_integer(keyfile,
-                               identifier, "time", NULL);
-
-       return 0;
-}
-
-static int stats_save(struct connman_service *service,
-               GKeyFile *keyfile, const char *identifier)
-{
-       g_key_file_set_integer(keyfile, identifier, "rx_packets",
-                       service->stats.rx_packets);
-       g_key_file_set_integer(keyfile, identifier, "tx_packets",
-                       service->stats.tx_packets);
-       g_key_file_set_integer(keyfile, identifier, "rx_bytes",
-                       service->stats.rx_bytes);
-       g_key_file_set_integer(keyfile, identifier, "tx_bytes",
-                       service->stats.tx_bytes);
-       g_key_file_set_integer(keyfile, identifier, "rx_errors",
-                       service->stats.rx_errors);
-       g_key_file_set_integer(keyfile, identifier, "tx_errors",
-                       service->stats.tx_errors);
-       g_key_file_set_integer(keyfile, identifier, "rx_dropped",
-                       service->stats.rx_dropped);
-       g_key_file_set_integer(keyfile, identifier, "tx_dropped",
-                       service->stats.tx_dropped);
-       g_key_file_set_integer(keyfile, identifier, "time",
-                       service->stats.time);
+       seconds = g_timer_elapsed(stats->timer, NULL);
+       stats->data.time = stats->data_last.time + seconds;
 
-       return 0;
+       stats->enabled = FALSE;
 }
 
 static void reset_stats(struct connman_service *service)
 {
        DBG("service %p", service);
 
+       /* home */
        service->stats.valid = FALSE;
-       service->stats.rx_packets = 0;
-       service->stats.tx_packets = 0;
-       service->stats.rx_bytes = 0;
-       service->stats.tx_bytes = 0;
-       service->stats.rx_errors = 0;
-       service->stats.tx_errors = 0;
-       service->stats.rx_dropped = 0;
-       service->stats.tx_dropped = 0;
-       service->stats.time = 0;
-       service->stats.time_start = 0;
-       g_timer_reset(service->stats.timer);
-
-}
-
-unsigned long __connman_service_stats_get_rx_packets(struct connman_service *service)
-{
-       return service->stats.rx_packets;
-}
-
-unsigned long __connman_service_stats_get_tx_packets(struct connman_service *service)
-{
-       return service->stats.tx_packets;
-}
-
-unsigned long __connman_service_stats_get_rx_bytes(struct connman_service *service)
-{
-       return service->stats.rx_bytes;
-}
-
-unsigned long __connman_service_stats_get_tx_bytes(struct connman_service *service)
-{
-       return service->stats.tx_bytes;
-}
 
-unsigned long __connman_service_stats_get_rx_errors(struct connman_service *service)
-{
-       return service->stats.rx_errors;
-}
+       service->stats.data.rx_packets = 0;
+       service->stats.data.tx_packets = 0;
+       service->stats.data.rx_bytes = 0;
+       service->stats.data.tx_bytes = 0;
+       service->stats.data.rx_errors = 0;
+       service->stats.data.tx_errors = 0;
+       service->stats.data.rx_dropped = 0;
+       service->stats.data.tx_dropped = 0;
+       service->stats.data.time = 0;
+       service->stats.data_last.time = 0;
 
-unsigned long __connman_service_stats_get_tx_errors(struct connman_service *service)
-{
-       return service->stats.tx_errors;
-}
+       g_timer_reset(service->stats.timer);
 
-unsigned long __connman_service_stats_get_rx_dropped(struct connman_service *service)
-{
-       return service->stats.rx_dropped;
-}
+       /* roaming */
+       service->stats_roaming.valid = FALSE;
 
-unsigned long __connman_service_stats_get_tx_dropped(struct connman_service *service)
-{
-       return service->stats.tx_dropped;
-}
+       service->stats_roaming.data.rx_packets = 0;
+       service->stats_roaming.data.tx_packets = 0;
+       service->stats_roaming.data.rx_bytes = 0;
+       service->stats_roaming.data.tx_bytes = 0;
+       service->stats_roaming.data.rx_errors = 0;
+       service->stats_roaming.data.tx_errors = 0;
+       service->stats_roaming.data.rx_dropped = 0;
+       service->stats_roaming.data.tx_dropped = 0;
+       service->stats_roaming.data.time = 0;
+       service->stats_roaming.data_last.time = 0;
 
-unsigned long __connman_service_stats_get_time(struct connman_service *service)
-{
-       return service->stats.time;
+       g_timer_reset(service->stats_roaming.timer);
 }
 
 static struct connman_service *get_default(void)
@@ -635,7 +837,8 @@ static void state_changed(struct connman_service *service)
 {
        const char *str;
 
-       str = state2string(service->state);
+       str = state2string(combine_state(service->state_ipv4,
+                                                       service->state_ipv6));
        if (str == NULL)
                return;
 
@@ -707,6 +910,7 @@ static void passphrase_changed(struct connman_service *service)
        case CONNMAN_SERVICE_TYPE_CELLULAR:
        case CONNMAN_SERVICE_TYPE_GPS:
        case CONNMAN_SERVICE_TYPE_VPN:
+       case CONNMAN_SERVICE_TYPE_GADGET:
                return;
        case CONNMAN_SERVICE_TYPE_WIFI:
                required = FALSE;
@@ -745,93 +949,92 @@ static void login_changed(struct connman_service *service)
                                                DBUS_TYPE_BOOLEAN, &required);
 }
 
-static void apn_changed(struct connman_service *service)
+static void append_security(DBusMessageIter *iter, void *user_data)
 {
-       dbus_bool_t required;
-
-       switch (service->type) {
-       case CONNMAN_SERVICE_TYPE_UNKNOWN:
-       case CONNMAN_SERVICE_TYPE_SYSTEM:
-       case CONNMAN_SERVICE_TYPE_ETHERNET:
-       case CONNMAN_SERVICE_TYPE_WIMAX:
-       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
-       case CONNMAN_SERVICE_TYPE_WIFI:
-       case CONNMAN_SERVICE_TYPE_GPS:
-       case CONNMAN_SERVICE_TYPE_VPN:
-               return;
-       case CONNMAN_SERVICE_TYPE_CELLULAR:
-               break;
-       }
+       struct connman_service *service = user_data;
+       const char *str;
 
-       required = (service->apn == NULL) ? TRUE : FALSE;
+       str = security2string(service->security);
+       if (str != NULL)
+               dbus_message_iter_append_basic(iter,
+                               DBUS_TYPE_STRING, &str);
 
-       connman_dbus_property_changed_basic(service->path,
-                               CONNMAN_SERVICE_INTERFACE, "SetupRequired",
-                                               DBUS_TYPE_BOOLEAN, &required);
+       str = "wps";
+       if (service->wps == TRUE)
+               dbus_message_iter_append_basic(iter,
+                               DBUS_TYPE_STRING, &str);
 }
 
 static void append_ethernet(DBusMessageIter *iter, void *user_data)
 {
        struct connman_service *service = user_data;
 
-       if (is_connecting(service) == FALSE &&
-                       is_connected(service) == FALSE)
-               return;
-
-       if (service->ipconfig != NULL)
-               __connman_ipconfig_append_ethernet(service->ipconfig, iter);
+       if (service->ipconfig_ipv4 != NULL)
+               __connman_ipconfig_append_ethernet(service->ipconfig_ipv4,
+                                                                       iter);
+       else if (service->ipconfig_ipv6 != NULL)
+               __connman_ipconfig_append_ethernet(service->ipconfig_ipv6,
+                                                                       iter);
 }
 
 static void append_ipv4(DBusMessageIter *iter, void *user_data)
 {
        struct connman_service *service = user_data;
 
-       if (is_connected(service) == FALSE)
+       DBG("ipv4 %p state %s", service->ipconfig_ipv4,
+                               state2string(service->state_ipv4));
+
+       if (is_connected_state(service, service->state_ipv4) == FALSE)
                return;
 
-       if (service->ipconfig != NULL)
-               __connman_ipconfig_append_ipv4(service->ipconfig, iter);
+       if (service->ipconfig_ipv4 != NULL)
+               __connman_ipconfig_append_ipv4(service->ipconfig_ipv4, iter);
 }
 
 static void append_ipv6(DBusMessageIter *iter, void *user_data)
 {
        struct connman_service *service = user_data;
-       struct connman_ipconfig *ipv6config;
 
-       if (is_connected(service) == FALSE)
-               return;
-
-       if (service->ipconfig == NULL)
-               return;
+       DBG("ipv6 %p state %s", service->ipconfig_ipv6,
+                               state2string(service->state_ipv6));
 
-       ipv6config = connman_ipconfig_get_ipv6config(service->ipconfig);
-       if (ipv6config == NULL)
+       if (is_connected_state(service, service->state_ipv6) == FALSE)
                return;
 
-       __connman_ipconfig_append_ipv6(ipv6config, iter);
+       if (service->ipconfig_ipv6 != NULL)
+               __connman_ipconfig_append_ipv6(service->ipconfig_ipv6, iter,
+                                               service->ipconfig_ipv4);
 }
 
 static void append_ipv4config(DBusMessageIter *iter, void *user_data)
 {
        struct connman_service *service = user_data;
 
-       if (service->ipconfig != NULL)
-               __connman_ipconfig_append_ipv4config(service->ipconfig, iter);
+       if (service->ipconfig_ipv4 != NULL)
+               __connman_ipconfig_append_ipv4config(service->ipconfig_ipv4,
+                                                       iter);
 }
 
 static void append_ipv6config(DBusMessageIter *iter, void *user_data)
 {
        struct connman_service *service = user_data;
-       struct connman_ipconfig *ipv6config;
 
-       if (service->ipconfig == NULL)
-               return;
+       if (service->ipconfig_ipv6 != NULL)
+               __connman_ipconfig_append_ipv6config(service->ipconfig_ipv6,
+                                                       iter);
+}
 
-       ipv6config = connman_ipconfig_get_ipv6config(service->ipconfig);
-       if (ipv6config == NULL)
-               return;
+static void append_nameserver(DBusMessageIter *iter, char ***nameservers)
+{
+       char **servers;
+       int i;
+
+       servers = *nameservers;
 
-       __connman_ipconfig_append_ipv6config(ipv6config, iter);
+       for (i = 0; servers[i] != NULL; i++) {
+               dbus_message_iter_append_basic(iter,
+                                       DBUS_TYPE_STRING, &servers[i]);
+       }
 }
 
 static void append_dns(DBusMessageIter *iter, void *user_data)
@@ -841,21 +1044,13 @@ static void append_dns(DBusMessageIter *iter, void *user_data)
        if (is_connected(service) == FALSE)
                return;
 
-       if (service->nameservers != NULL) {
-               int i;
-
-               for (i = 0; service->nameservers[i]; i++)
-                       dbus_message_iter_append_basic(iter,
-                               DBUS_TYPE_STRING, &service->nameservers[i]);
-
+       if (service->nameservers_config != NULL) {
+               append_nameserver(iter, &service->nameservers_config);
                return;
-       }
-
-       if (service->nameserver == NULL)
+       } else if (service->nameservers != NULL) {
+               append_nameserver(iter, &service->nameservers);
                return;
-
-       dbus_message_iter_append_basic(iter,
-                               DBUS_TYPE_STRING, &service->nameserver);
+       }
 }
 
 static void append_dnsconfig(DBusMessageIter *iter, void *user_data)
@@ -863,19 +1058,22 @@ static void append_dnsconfig(DBusMessageIter *iter, void *user_data)
        struct connman_service *service = user_data;
        int i;
 
-       if (service->nameservers == NULL)
+       if (service->nameservers_config == NULL)
                return;
 
-       for (i = 0; service->nameservers[i]; i++)
+       for (i = 0; service->nameservers_config[i]; i++) {
                dbus_message_iter_append_basic(iter,
-                               DBUS_TYPE_STRING, &service->nameservers[i]);
+                               DBUS_TYPE_STRING,
+                               &service->nameservers_config[i]);
+       }
 }
 
 static void append_domain(DBusMessageIter *iter, void *user_data)
 {
        struct connman_service *service = user_data;
 
-       if (is_connected(service) == FALSE && is_connecting(service) == FALSE)
+       if (is_connected(service) == FALSE &&
+                               is_connecting(service) == FALSE)
                return;
 
        if (service->domainname == NULL)
@@ -898,15 +1096,123 @@ static void append_domainconfig(DBusMessageIter *iter, void *user_data)
                                DBUS_TYPE_STRING, &service->domains[i]);
 }
 
+static void append_proxies(DBusMessageIter *iter, void *user_data)
+{
+       struct connman_service *service = user_data;
+       int i;
+
+       if (service->proxies == NULL)
+               return;
+
+       for (i = 0; service->proxies[i]; i++)
+               dbus_message_iter_append_basic(iter,
+                               DBUS_TYPE_STRING, &service->proxies[i]);
+}
+
+static void append_excludes(DBusMessageIter *iter, void *user_data)
+{
+       struct connman_service *service = user_data;
+       int i;
+
+       if (service->excludes == NULL)
+               return;
+
+       for (i = 0; service->excludes[i]; i++)
+               dbus_message_iter_append_basic(iter,
+                               DBUS_TYPE_STRING, &service->excludes[i]);
+}
+
 static void append_proxy(DBusMessageIter *iter, void *user_data)
 {
        struct connman_service *service = user_data;
+       enum connman_service_proxy_method proxy;
+       const char *pac = NULL;
+       const char *method = proxymethod2string(
+               CONNMAN_SERVICE_PROXY_METHOD_DIRECT);
+
+       DBG("");
 
        if (is_connected(service) == FALSE)
                return;
 
-       if (service->ipconfig != NULL)
-               __connman_ipconfig_append_proxy(service->ipconfig, iter);
+       proxy = connman_service_get_proxy_method(service);
+
+       switch (proxy) {
+       case CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN:
+               return;
+       case CONNMAN_SERVICE_PROXY_METHOD_DIRECT:
+               goto done;
+       case CONNMAN_SERVICE_PROXY_METHOD_MANUAL:
+               connman_dbus_dict_append_array(iter, "Servers",
+                                       DBUS_TYPE_STRING, append_proxies,
+                                       service);
+
+               connman_dbus_dict_append_array(iter, "Excludes",
+                                       DBUS_TYPE_STRING, append_excludes,
+                                       service);
+               break;
+       case CONNMAN_SERVICE_PROXY_METHOD_AUTO:
+               /* Maybe DHCP, or WPAD,  has provided an url for a pac file */
+               if (service->ipconfig_ipv4 != NULL)
+                       pac = __connman_ipconfig_get_proxy_autoconfig(
+                               service->ipconfig_ipv4);
+               else if (service->ipconfig_ipv6 != NULL)
+                       pac = __connman_ipconfig_get_proxy_autoconfig(
+                               service->ipconfig_ipv6);
+
+               if (service->pac == NULL && pac == NULL)
+                       goto done;
+
+               if (service->pac != NULL)
+                       pac = service->pac;
+
+               connman_dbus_dict_append_basic(iter, "URL",
+                                       DBUS_TYPE_STRING, &pac);
+               break;
+       }
+
+       method = proxymethod2string(proxy);
+
+done:
+       connman_dbus_dict_append_basic(iter, "Method",
+                                       DBUS_TYPE_STRING, &method);
+}
+
+static void append_proxyconfig(DBusMessageIter *iter, void *user_data)
+{
+       struct connman_service *service = user_data;
+       const char *method;
+
+       if (service->proxy_config == CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN)
+               return;
+
+       switch (service->proxy_config) {
+       case CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN:
+               return;
+       case CONNMAN_SERVICE_PROXY_METHOD_DIRECT:
+               break;
+       case CONNMAN_SERVICE_PROXY_METHOD_MANUAL:
+               if (service->proxies != NULL)
+                       connman_dbus_dict_append_array(iter, "Servers",
+                                               DBUS_TYPE_STRING,
+                                               append_proxies, service);
+
+               if (service->excludes != NULL)
+                       connman_dbus_dict_append_array(iter, "Excludes",
+                                               DBUS_TYPE_STRING,
+                                               append_excludes, service);
+               break;
+       case CONNMAN_SERVICE_PROXY_METHOD_AUTO:
+               if (service->pac != NULL)
+                       connman_dbus_dict_append_basic(iter, "URL",
+                                       DBUS_TYPE_STRING, &service->pac);
+               break;
+       }
+
+       method = proxymethod2string(service->proxy_config);
+
+       connman_dbus_dict_append_basic(iter, "Method",
+                               DBUS_TYPE_STRING, &method);
 }
 
 static void append_provider(DBusMessageIter *iter, void *user_data)
@@ -991,6 +1297,258 @@ static void proxy_changed(struct connman_service *service)
                                                        append_proxy, service);
 }
 
+static void proxy_configuration_changed(struct connman_service *service)
+{
+       connman_dbus_property_changed_dict(service->path,
+                       CONNMAN_SERVICE_INTERFACE, "Proxy.Configuration",
+                                               append_proxyconfig, service);
+
+       proxy_changed(service);
+}
+
+static void link_changed(struct connman_service *service)
+{
+       connman_dbus_property_changed_dict(service->path,
+                                       CONNMAN_SERVICE_INTERFACE, "Ethernet",
+                                               append_ethernet, service);
+}
+
+static void stats_append_counters(DBusMessageIter *dict,
+                       struct connman_stats_data *stats,
+                       struct connman_stats_data *counters,
+                       connman_bool_t append_all)
+{
+       if (counters->rx_packets != stats->rx_packets || append_all) {
+               counters->rx_packets = stats->rx_packets;
+               connman_dbus_dict_append_basic(dict, "RX.Packets",
+                                       DBUS_TYPE_UINT32, &stats->rx_packets);
+       }
+
+       if (counters->tx_packets != stats->tx_packets || append_all) {
+               counters->tx_packets = stats->tx_packets;
+               connman_dbus_dict_append_basic(dict, "TX.Packets",
+                                       DBUS_TYPE_UINT32, &stats->tx_packets);
+       }
+
+       if (counters->rx_bytes != stats->rx_bytes || append_all) {
+               counters->rx_bytes = stats->rx_bytes;
+               connman_dbus_dict_append_basic(dict, "RX.Bytes",
+                                       DBUS_TYPE_UINT32, &stats->rx_bytes);
+       }
+
+       if (counters->tx_bytes != stats->tx_bytes || append_all) {
+               counters->tx_bytes = stats->tx_bytes;
+               connman_dbus_dict_append_basic(dict, "TX.Bytes",
+                                       DBUS_TYPE_UINT32, &stats->tx_bytes);
+       }
+
+       if (counters->rx_errors != stats->rx_errors || append_all) {
+               counters->rx_errors = stats->rx_errors;
+               connman_dbus_dict_append_basic(dict, "RX.Errors",
+                                       DBUS_TYPE_UINT32, &stats->rx_errors);
+       }
+
+       if (counters->tx_errors != stats->tx_errors || append_all) {
+               counters->tx_errors = stats->tx_errors;
+               connman_dbus_dict_append_basic(dict, "TX.Errors",
+                                       DBUS_TYPE_UINT32, &stats->tx_errors);
+       }
+
+       if (counters->rx_dropped != stats->rx_dropped || append_all) {
+               counters->rx_dropped = stats->rx_dropped;
+               connman_dbus_dict_append_basic(dict, "RX.Dropped",
+                                       DBUS_TYPE_UINT32, &stats->rx_dropped);
+       }
+
+       if (counters->tx_dropped != stats->tx_dropped || append_all) {
+               counters->tx_dropped = stats->tx_dropped;
+               connman_dbus_dict_append_basic(dict, "TX.Dropped",
+                                       DBUS_TYPE_UINT32, &stats->tx_dropped);
+       }
+
+       if (counters->time != stats->time || append_all) {
+               counters->time = stats->time;
+               connman_dbus_dict_append_basic(dict, "Time",
+                                       DBUS_TYPE_UINT32, &stats->time);
+       }
+}
+
+static void stats_append(struct connman_service *service,
+                               const char *counter,
+                               struct connman_stats_counter *counters,
+                               connman_bool_t append_all)
+{
+       DBusMessageIter array, dict;
+       DBusMessage *msg;
+
+       DBG("service %p counter %s", service, counter);
+
+       msg = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL);
+       if (msg == NULL)
+               return;
+
+       dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH,
+                               &service->path, DBUS_TYPE_INVALID);
+
+       dbus_message_iter_init_append(msg, &array);
+
+       /* home counter */
+       connman_dbus_dict_open(&array, &dict);
+
+       stats_append_counters(&dict, &service->stats.data,
+                               &counters->stats.data, append_all);
+
+       connman_dbus_dict_close(&array, &dict);
+
+       /* roaming counter */
+       connman_dbus_dict_open(&array, &dict);
+
+       stats_append_counters(&dict, &service->stats_roaming.data,
+                               &counters->stats_roaming.data, append_all);
+
+       connman_dbus_dict_close(&array, &dict);
+
+       __connman_counter_send_usage(counter, msg);
+}
+
+static void stats_update(struct connman_service *service,
+                               unsigned int rx_packets, unsigned int tx_packets,
+                               unsigned int rx_bytes, unsigned int tx_bytes,
+                               unsigned int rx_errors, unsigned int tx_errors,
+                               unsigned int rx_dropped, unsigned int tx_dropped)
+{
+       struct connman_stats *stats = stats_get(service);
+       struct connman_stats_data *data_last = &stats->data_last;
+       struct connman_stats_data *data = &stats->data;
+       unsigned int seconds;
+
+       DBG("service %p", service);
+
+       if (stats->valid == TRUE) {
+               data->rx_packets +=
+                       rx_packets - data_last->rx_packets;
+               data->tx_packets +=
+                       tx_packets - data_last->tx_packets;
+               data->rx_bytes +=
+                       rx_bytes - data_last->rx_bytes;
+               data->tx_bytes +=
+                       tx_bytes - data_last->tx_bytes;
+               data->rx_errors +=
+                       rx_errors - data_last->rx_errors;
+               data->tx_errors +=
+                       tx_errors - data_last->tx_errors;
+               data->rx_dropped +=
+                       rx_dropped - data_last->rx_dropped;
+               data->tx_dropped +=
+                       tx_dropped - data_last->tx_dropped;
+       } else {
+               stats->valid = TRUE;
+       }
+
+       data_last->rx_packets = rx_packets;
+       data_last->tx_packets = tx_packets;
+       data_last->rx_bytes = rx_bytes;
+       data_last->tx_bytes = tx_bytes;
+       data_last->rx_errors = rx_errors;
+       data_last->tx_errors = tx_errors;
+       data_last->rx_dropped = rx_dropped;
+       data_last->tx_dropped = tx_dropped;
+
+       seconds = g_timer_elapsed(stats->timer, NULL);
+       stats->data.time = stats->data_last.time + seconds;
+}
+
+void __connman_service_notify(struct connman_service *service,
+                       unsigned int rx_packets, unsigned int tx_packets,
+                       unsigned int rx_bytes, unsigned int tx_bytes,
+                       unsigned int rx_errors, unsigned int tx_errors,
+                       unsigned int rx_dropped, unsigned int tx_dropped)
+{
+       GHashTableIter iter;
+       gpointer key, value;
+       const char *counter;
+       struct connman_stats_counter *counters;
+       struct connman_stats_data *data;
+       int err;
+
+       if (service == NULL)
+               return;
+
+       if (is_connected(service) == FALSE)
+               return;
+
+       stats_update(service,
+               rx_packets, tx_packets,
+               rx_bytes, tx_bytes,
+               rx_errors, tx_errors,
+               rx_dropped, tx_dropped);
+
+       data = &stats_get(service)->data;
+       err = __connman_stats_update(service, service->roaming, data);
+       if (err < 0)
+               connman_error("Failed to store statistics for %s",
+                               service->identifier);
+
+       g_hash_table_iter_init(&iter, service->counter_table);
+       while (g_hash_table_iter_next(&iter, &key, &value)) {
+               counter = key;
+               counters = value;
+
+               stats_append(service, counter, counters, counters->append_all);
+               counters->append_all = FALSE;
+       }
+}
+
+int __connman_service_counter_register(const char *counter)
+{
+       struct connman_service *service;
+       GSequenceIter *iter;
+       struct connman_stats_counter *counters;
+
+       DBG("counter %s", counter);
+
+       counter_list = g_slist_append(counter_list, (gpointer)counter);
+
+       iter = g_sequence_get_begin_iter(service_list);
+
+       while (g_sequence_iter_is_end(iter) == FALSE) {
+               service = g_sequence_get(iter);
+
+               counters = g_try_new0(struct connman_stats_counter, 1);
+               if (counters == NULL)
+                       return -ENOMEM;
+
+               counters->append_all = TRUE;
+
+               g_hash_table_replace(service->counter_table, (gpointer)counter,
+                                       counters);
+
+               iter = g_sequence_iter_next(iter);
+       }
+
+       return 0;
+}
+
+void __connman_service_counter_unregister(const char *counter)
+{
+       struct connman_service *service;
+       GSequenceIter *iter;
+
+       DBG("counter %s", counter);
+
+       iter = g_sequence_get_begin_iter(service_list);
+
+       while (g_sequence_iter_is_end(iter) == FALSE) {
+               service = g_sequence_get(iter);
+
+               g_hash_table_remove(service->counter_table, counter);
+
+               iter = g_sequence_iter_next(iter);
+       }
+
+       counter_list = g_slist_remove(counter_list, counter);
+}
+
 static void append_properties(DBusMessageIter *dict, dbus_bool_t limited,
                                        struct connman_service *service)
 {
@@ -1007,12 +1565,11 @@ static void append_properties(DBusMessageIter *dict, dbus_bool_t limited,
                connman_dbus_dict_append_basic(dict, "Mode",
                                                DBUS_TYPE_STRING, &str);
 
-       str = security2string(service->security);
-       if (str != NULL)
-               connman_dbus_dict_append_basic(dict, "Security",
-                                               DBUS_TYPE_STRING, &str);
+       connman_dbus_dict_append_array(dict, "Security",
+                               DBUS_TYPE_STRING, append_security, service);
 
-       str = state2string(service->state);
+       str = state2string(combine_state(service->state_ipv4,
+                                                       service->state_ipv6));
        if (str != NULL)
                connman_dbus_dict_append_basic(dict, "State",
                                                DBUS_TYPE_STRING, &str);
@@ -1051,36 +1608,13 @@ static void append_properties(DBusMessageIter *dict, dbus_bool_t limited,
        case CONNMAN_SERVICE_TYPE_SYSTEM:
        case CONNMAN_SERVICE_TYPE_GPS:
        case CONNMAN_SERVICE_TYPE_VPN:
+       case CONNMAN_SERVICE_TYPE_GADGET:
                break;
        case CONNMAN_SERVICE_TYPE_CELLULAR:
                connman_dbus_dict_append_basic(dict, "Roaming",
                                        DBUS_TYPE_BOOLEAN, &service->roaming);
 
-               if (service->mcc != NULL && service->mnc != NULL) {
-                       connman_dbus_dict_append_basic(dict, "MCC",
-                                       DBUS_TYPE_STRING, &service->mcc);
-                       connman_dbus_dict_append_basic(dict, "MNC",
-                                       DBUS_TYPE_STRING, &service->mnc);
-               }
-
-               if (service->apn != NULL) {
-                       connman_dbus_dict_append_basic(dict, "APN",
-                                       DBUS_TYPE_STRING, &service->apn);
-
-                       if (service->username != NULL)
-                               connman_dbus_dict_append_basic(dict,
-                                       "Username", DBUS_TYPE_STRING,
-                                                       &service->username);
-
-                       if (service->password != NULL)
-                               connman_dbus_dict_append_basic(dict,
-                                       "Password", DBUS_TYPE_STRING,
-                                                       &service->password);
-
-                       required = FALSE;
-               } else
-                       required = TRUE;
-
+               required = FALSE;
                connman_dbus_dict_append_basic(dict, "SetupRequired",
                                                DBUS_TYPE_BOOLEAN, &required);
                connman_dbus_dict_append_dict(dict, "Ethernet",
@@ -1110,6 +1644,7 @@ static void append_properties(DBusMessageIter *dict, dbus_bool_t limited,
 
                connman_dbus_dict_append_basic(dict, "PassphraseRequired",
                                                DBUS_TYPE_BOOLEAN, &required);
+
                /* fall through */
        case CONNMAN_SERVICE_TYPE_ETHERNET:
        case CONNMAN_SERVICE_TYPE_WIMAX:
@@ -1143,7 +1678,11 @@ static void append_properties(DBusMessageIter *dict, dbus_bool_t limited,
 
        connman_dbus_dict_append_dict(dict, "Proxy", append_proxy, service);
 
-       connman_dbus_dict_append_dict(dict, "Provider", append_provider, service);
+       connman_dbus_dict_append_dict(dict, "Proxy.Configuration",
+                                               append_proxyconfig, service);
+
+       connman_dbus_dict_append_dict(dict, "Provider",
+                                               append_provider, service);
 }
 
 static void append_struct(gpointer value, gpointer user_data)
@@ -1167,88 +1706,520 @@ static void append_struct(gpointer value, gpointer user_data)
        dbus_message_iter_close_container(iter, &entry);
 }
 
-void __connman_service_list_struct(DBusMessageIter *iter)
-{
-       g_sequence_foreach(service_list, append_struct, iter);
-}
+void __connman_service_list_struct(DBusMessageIter *iter)
+{
+       g_sequence_foreach(service_list, append_struct, iter);
+}
+
+int __connman_service_get_index(struct connman_service *service)
+{
+       if (service == NULL)
+               return -1;
+
+       if (service->network != NULL)
+               return connman_network_get_index(service->network);
+       else if (service->provider != NULL)
+               return connman_provider_get_index(service->provider);
+
+       return -1;
+}
+
+void __connman_service_set_domainname(struct connman_service *service,
+                                               const char *domainname)
+{
+       if (service == NULL)
+               return;
+
+       g_free(service->domainname);
+       service->domainname = g_strdup(domainname);
+
+       domain_changed(service);
+}
+
+const char *connman_service_get_domainname(struct connman_service *service)
+{
+       if (service == NULL)
+               return NULL;
+
+       if (service->domains != NULL)
+               return service->domains[0];
+       else
+               return service->domainname;
+}
+
+char **connman_service_get_nameservers(struct connman_service *service)
+{
+       if (service == NULL)
+               return NULL;
+
+       if (service->nameservers_config != NULL)
+               return service->nameservers_config;
+       else if (service->nameservers != NULL)
+               return service->nameservers;
+
+       return NULL;
+}
+
+void connman_service_set_proxy_method(struct connman_service *service,
+                                       enum connman_service_proxy_method method)
+{
+       if (service == NULL)
+               return;
+
+       service->proxy = method;
+
+       proxy_changed(service);
+
+       if (method != CONNMAN_SERVICE_PROXY_METHOD_AUTO)
+               __connman_notifier_proxy_changed(service);
+}
+
+enum connman_service_proxy_method connman_service_get_proxy_method(
+                                       struct connman_service *service)
+{
+       if (service == NULL)
+               return CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN;
+
+       if (service->proxy_config != CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN) {
+               if (service->proxy_config == CONNMAN_SERVICE_PROXY_METHOD_AUTO &&
+                               service->pac == NULL)
+                       return service->proxy;
+
+               return service->proxy_config;
+       }
+
+       return service->proxy;
+}
+
+char **connman_service_get_proxy_servers(struct connman_service *service)
+{
+       return g_strdupv(service->proxies);
+}
+
+char **connman_service_get_proxy_excludes(struct connman_service *service)
+{
+       return g_strdupv(service->excludes);
+}
+
+const char *connman_service_get_proxy_url(struct connman_service *service)
+{
+       if (service == NULL)
+               return NULL;
+
+       return service->pac;
+}
+
+void __connman_service_set_proxy_autoconfig(struct connman_service *service,
+                                                       const char *url)
+{
+       if (service == NULL)
+               return;
+
+       service->proxy = CONNMAN_SERVICE_PROXY_METHOD_AUTO;
+
+       if (service->ipconfig_ipv4) {
+               if (__connman_ipconfig_set_proxy_autoconfig(
+                           service->ipconfig_ipv4, url) < 0)
+                       return;
+       } else if (service->ipconfig_ipv6) {
+               if (__connman_ipconfig_set_proxy_autoconfig(
+                           service->ipconfig_ipv6, url) < 0)
+                       return;
+       } else
+               return;
+
+       proxy_changed(service);
+
+       __connman_notifier_proxy_changed(service);
+}
+
+const char *connman_service_get_proxy_autoconfig(struct connman_service *service)
+{
+       if (service == NULL)
+               return NULL;
+
+       if (service->ipconfig_ipv4)
+               return __connman_ipconfig_get_proxy_autoconfig(
+                                               service->ipconfig_ipv4);
+       else if (service->ipconfig_ipv6)
+               return __connman_ipconfig_get_proxy_autoconfig(
+                                               service->ipconfig_ipv6);
+       return NULL;
+}
+
+static void update_timeservers(struct connman_service *service)
+{
+       int i;
+
+       if (service->timeservers == NULL)
+               return;
+
+       switch (combine_state(service->state_ipv4, service->state_ipv6)) {
+       case CONNMAN_SERVICE_STATE_UNKNOWN:
+       case CONNMAN_SERVICE_STATE_IDLE:
+       case CONNMAN_SERVICE_STATE_ASSOCIATION:
+       case CONNMAN_SERVICE_STATE_CONFIGURATION:
+               return;
+       case CONNMAN_SERVICE_STATE_FAILURE:
+       case CONNMAN_SERVICE_STATE_DISCONNECT:
+               for (i = 0; service->timeservers[i] != NULL; i++)
+                       connman_timeserver_remove(service->timeservers[i]);
+               return;
+       case CONNMAN_SERVICE_STATE_READY:
+       case CONNMAN_SERVICE_STATE_ONLINE:
+               break;
+       }
+
+       for (i = 0; service->timeservers[i] != NULL; i++)
+               connman_timeserver_append(service->timeservers[i]);
+}
+
+int __connman_service_timeserver_append(struct connman_service *service,
+                                               const char *timeserver)
+{
+       int len;
+
+       DBG("service %p timeserver %s", service, timeserver);
+
+       if (timeserver == NULL)
+               return -EINVAL;
+
+       if (service->timeservers != NULL) {
+               int i;
+
+               for (i = 0; service->timeservers[i] != NULL; i++)
+                       if (g_strcmp0(service->timeservers[i], timeserver) == 0)
+                               return -EEXIST;
+
+               len = g_strv_length(service->timeservers);
+               service->timeservers = g_try_renew(char *, service->timeservers,
+                                                       len + 2);
+       } else {
+               len = 0;
+               service->timeservers = g_try_new0(char *, len + 2);
+       }
+
+       if (service->timeservers == NULL)
+               return -ENOMEM;
+
+       service->timeservers[len] = g_strdup(timeserver);
+       service->timeservers[len + 1] = NULL;
+
+       update_timeservers(service);
+
+       return 0;
+}
+
+int __connman_service_timeserver_remove(struct connman_service *service,
+                                               const char *timeserver)
+{
+       char **servers;
+       int len, i, j;
+
+       DBG("service %p timeserver %s", service, timeserver);
+
+       if (timeserver == NULL)
+               return -EINVAL;
+
+       if (service->timeservers == NULL)
+               return 0;
+
+       len = g_strv_length(service->timeservers);
+       if (len == 1) {
+               if (g_strcmp0(service->timeservers[0], timeserver) != 0)
+                       return 0;
+
+               g_strfreev(service->timeservers);
+               service->timeservers = NULL;
+
+               return 0;
+       }
+
+       servers = g_try_new0(char *, len - 1);
+       if (servers == NULL)
+               return -ENOMEM;
+
+       for (i = 0, j = 0; i < len; i++) {
+               if (g_strcmp0(service->timeservers[i], timeserver) != 0) {
+                       servers[j] = g_strdup(service->timeservers[i]);
+                       j++;
+               }
+       }
+       servers[len - 2] = NULL;
+
+       g_strfreev(service->timeservers);
+       service->timeservers = servers;
+
+       update_timeservers(service);
+
+       return 0;
+}
+
+void __connman_service_set_pac(struct connman_service *service,
+                                       const char *pac)
+{
+       if (pac == NULL)
+               return;
+
+       g_free(service->pac);
+       service->pac = g_strdup(pac);
+}
+
+void __connman_service_set_passphrase(struct connman_service *service,
+                                       const char* passphrase)
+{
+       if (service->immutable == TRUE)
+               return;
+
+       g_free(service->passphrase);
+       service->passphrase = g_strdup(passphrase);
+
+       passphrase_changed(service);
+
+       if (service->network != NULL)
+               connman_network_set_string(service->network,
+                                       "WiFi.Passphrase",
+                                       service->passphrase);
+
+       __connman_storage_save_service(service);
+}
+
+static DBusMessage *get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct connman_service *service = user_data;
+       DBusMessage *reply;
+       DBusMessageIter array, dict;
+
+       DBG("service %p", service);
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &array);
+
+       connman_dbus_dict_open(&array, &dict);
+       append_properties(&dict, FALSE, service);
+       connman_dbus_dict_close(&array, &dict);
+
+       return reply;
+}
+
+static int update_proxy_configuration(struct connman_service *service,
+                               DBusMessageIter *array)
+{
+       DBusMessageIter dict;
+       enum connman_service_proxy_method method;
+       GString *servers_str = NULL;
+       GString *excludes_str = NULL;
+       const char *url = NULL;
+
+       method = CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN;
+
+       dbus_message_iter_recurse(array, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, variant;
+               const char *key;
+               int type;
+
+               dbus_message_iter_recurse(&dict, &entry);
+
+               if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+                       goto error;
+
+               dbus_message_iter_get_basic(&entry, &key);
+               dbus_message_iter_next(&entry);
+
+               if (dbus_message_iter_get_arg_type(&entry) !=
+                                                       DBUS_TYPE_VARIANT)
+                       goto error;
+
+               dbus_message_iter_recurse(&entry, &variant);
+               type = dbus_message_iter_get_arg_type(&variant);
+
+               if (g_str_equal(key, "Method") == TRUE) {
+                       const char *val;
+
+                       if (type != DBUS_TYPE_STRING)
+                               goto error;
+
+                       dbus_message_iter_get_basic(&variant, &val);
+                       method = string2proxymethod(val);
+               } else if (g_str_equal(key, "URL") == TRUE) {
+                       if (type != DBUS_TYPE_STRING)
+                               goto error;
+
+                       dbus_message_iter_get_basic(&variant, &url);
+               } else if (g_str_equal(key, "Servers") == TRUE) {
+                       DBusMessageIter str_array;
+
+                       if (type != DBUS_TYPE_ARRAY)
+                               goto error;
+
+                       servers_str = g_string_new(NULL);
+                       if (servers_str == NULL)
+                               goto error;
+
+                       dbus_message_iter_recurse(&variant, &str_array);
+
+                       while (dbus_message_iter_get_arg_type(&str_array) ==
+                                                       DBUS_TYPE_STRING) {
+                               char *val = NULL;
+
+                               dbus_message_iter_get_basic(&str_array, &val);
+
+                               if (servers_str->len > 0)
+                                       g_string_append_printf(servers_str,
+                                                       " %s", val);
+                               else
+                                       g_string_append(servers_str, val);
+
+                               dbus_message_iter_next(&str_array);
+                       }
+               } else if (g_str_equal(key, "Excludes") == TRUE) {
+                       DBusMessageIter str_array;
+
+                       if (type != DBUS_TYPE_ARRAY)
+                               goto error;
+
+                       excludes_str = g_string_new(NULL);
+                       if (excludes_str == NULL)
+                               goto error;
+
+                       dbus_message_iter_recurse(&variant, &str_array);
+
+                       while (dbus_message_iter_get_arg_type(&str_array) ==
+                                                       DBUS_TYPE_STRING) {
+                               char *val = NULL;
+
+                               dbus_message_iter_get_basic(&str_array, &val);
+
+                               if (excludes_str->len > 0)
+                                       g_string_append_printf(excludes_str,
+                                                       " %s", val);
+                               else
+                                       g_string_append(excludes_str, val);
+
+                               dbus_message_iter_next(&str_array);
+                       }
+               }
+
+               dbus_message_iter_next(&dict);
+       }
+
+       switch (method) {
+       case CONNMAN_SERVICE_PROXY_METHOD_DIRECT:
+               break;
+       case CONNMAN_SERVICE_PROXY_METHOD_MANUAL:
+               if (servers_str == NULL && service->proxies == NULL)
+                       goto error;
+
+               if (servers_str != NULL) {
+                       g_strfreev(service->proxies);
+
+                       if (servers_str->len > 0)
+                               service->proxies = g_strsplit_set(
+                                       servers_str->str, " ", 0);
+                       else
+                               service->proxies = NULL;
+               }
+
+               if (excludes_str != NULL) {
+                       g_strfreev(service->excludes);
 
-int __connman_service_get_index(struct connman_service *service)
-{
-       if (service == NULL)
-               return -1;
+                       if (excludes_str->len > 0)
+                               service->excludes = g_strsplit_set(
+                                       excludes_str->str, " ", 0);
+                       else
+                               service->excludes = NULL;
+               }
 
-       if (service->network == NULL)
-               return -1;
+               if (service->proxies == NULL)
+                       method = CONNMAN_SERVICE_PROXY_METHOD_DIRECT;
 
-       return connman_network_get_index(service->network);
-}
+               break;
+       case CONNMAN_SERVICE_PROXY_METHOD_AUTO:
+               g_free(service->pac);
 
-void __connman_service_set_domainname(struct connman_service *service,
-                                               const char *domainname)
-{
-       if (service == NULL)
-               return;
+               if (url != NULL && strlen(url) > 0)
+                       service->pac = g_strdup(url);
+               else
+                       service->pac = NULL;
 
-       g_free(service->domainname);
-       service->domainname = g_strdup(domainname);
+               /* if we are connected:
+                  - if service->pac == NULL
+                  - if __connman_ipconfig_get_proxy_autoconfig(
+                  service->ipconfig) == NULL
+                  --> We should start WPAD */
 
-       domain_changed(service);
-}
+               break;
+       case CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN:
+               goto error;
+       }
 
-const char *__connman_service_get_domainname(struct connman_service *service)
-{
-       if (service == NULL)
-               return NULL;
+       if (servers_str != NULL)
+               g_string_free(servers_str, TRUE);
 
-       return service->domainname;
-}
+       if (excludes_str != NULL)
+               g_string_free(excludes_str, TRUE);
 
-const char *__connman_service_get_nameserver(struct connman_service *service)
-{
-       if (service == NULL)
-               return NULL;
+       service->proxy_config = method;
 
-       return service->nameserver;
-}
+       return 0;
 
-void __connman_service_set_proxy_autoconfig(struct connman_service *service,
-                                                        const char *url)
-{
-       if (service == NULL)
-               return;
+error:
+       if (servers_str != NULL)
+               g_string_free(servers_str, TRUE);
 
-       if (__connman_ipconfig_set_proxy_autoconfig(service->ipconfig,
-                                                               url) < 0)
-               return;
+       if (excludes_str != NULL)
+               g_string_free(excludes_str, TRUE);
 
-       proxy_changed(service);
+       return -EINVAL;
 }
 
-static DBusMessage *get_properties(DBusConnection *conn,
-                                       DBusMessage *msg, void *user_data)
+static int set_ipconfig(struct connman_service *service,
+                       struct connman_ipconfig *ipconfig,
+                       DBusMessageIter *array,
+                       enum connman_service_state state,
+                       enum connman_service_state *new_state)
 {
-       struct connman_service *service = user_data;
-       DBusMessage *reply;
-       DBusMessageIter array, dict;
-       dbus_bool_t limited = TRUE;
+       enum connman_ipconfig_method old_method;
+       enum connman_ipconfig_method method = CONNMAN_IPCONFIG_METHOD_UNKNOWN;
+       enum connman_ipconfig_type type;
+       int err;
 
-       DBG("service %p", service);
+       old_method = __connman_ipconfig_get_method(ipconfig);
 
-       if (__connman_security_check_privilege(msg,
-                               CONNMAN_SECURITY_PRIVILEGE_SECRET) == 0)
-               limited = FALSE;
+       if (is_connecting_state(service, state) ||
+                                       is_connected_state(service, state))
+               __connman_network_clear_ipconfig(service->network, ipconfig);
 
-       reply = dbus_message_new_method_return(msg);
-       if (reply == NULL)
-               return NULL;
+       err = __connman_ipconfig_set_config(ipconfig, array);
+       method = __connman_ipconfig_get_method(ipconfig);
+       type = __connman_ipconfig_get_config_type(ipconfig);
 
-       dbus_message_iter_init_append(reply, &array);
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
+               if (err == 0 && old_method == CONNMAN_IPCONFIG_METHOD_OFF &&
+                               method == CONNMAN_IPCONFIG_METHOD_DHCP) {
+                       *new_state = service->state_ipv4 =
+                               CONNMAN_SERVICE_STATE_CONFIGURATION;
+                       __connman_ipconfig_enable(ipconfig);
+               }
 
-       connman_dbus_dict_open(&array, &dict);
-       append_properties(&dict, limited, service);
-       connman_dbus_dict_close(&array, &dict);
+       } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) {
+               if (err == 0 && old_method == CONNMAN_IPCONFIG_METHOD_OFF &&
+                               method == CONNMAN_IPCONFIG_METHOD_AUTO) {
+                       *new_state = service->state_ipv6;
+                       __connman_ipconfig_enable(ipconfig);
+               }
+       }
 
-       return reply;
+       DBG("err %d ipconfig %p type %d method %d state %s", err, ipconfig,
+               type, method, state2string(*new_state));
+
+       return err;
 }
 
 static DBusMessage *set_property(DBusConnection *conn,
@@ -1268,13 +2239,9 @@ static DBusMessage *set_property(DBusConnection *conn,
        dbus_message_iter_next(&iter);
        dbus_message_iter_recurse(&iter, &value);
 
-       if (__connman_security_check_privilege(msg,
-                                       CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0)
-               return __connman_error_permission_denied(msg);
-
        type = dbus_message_iter_get_arg_type(&value);
 
-       if (g_str_has_prefix(name, "AutoConnect") == TRUE) {
+       if (g_str_equal(name, "AutoConnect") == TRUE) {
                connman_bool_t autoconnect;
 
                if (type != DBUS_TYPE_BOOLEAN)
@@ -1302,90 +2269,9 @@ static DBusMessage *set_property(DBusConnection *conn,
                if (service->immutable == TRUE)
                        return __connman_error_not_supported(msg);
 
-               if (__connman_security_check_privilege(msg,
-                                       CONNMAN_SECURITY_PRIVILEGE_SECRET) < 0)
-                       return __connman_error_permission_denied(msg);
-
                dbus_message_iter_get_basic(&value, &passphrase);
 
-               g_free(service->passphrase);
-               service->passphrase = g_strdup(passphrase);
-
-               passphrase_changed(service);
-
-               if (service->network != NULL)
-                       connman_network_set_string(service->network,
-                               "WiFi.Passphrase", service->passphrase);
-
-               __connman_storage_save_service(service);
-       } else if (g_str_equal(name, "APN") == TRUE) {
-               const char *apn;
-
-               if (type != DBUS_TYPE_STRING)
-                       return __connman_error_invalid_arguments(msg);
-
-               if (service->immutable == TRUE)
-                       return __connman_error_not_supported(msg);
-
-               if (service->type != CONNMAN_SERVICE_TYPE_CELLULAR)
-                       return __connman_error_invalid_service(msg);
-
-               dbus_message_iter_get_basic(&value, &apn);
-
-               g_free(service->apn);
-               service->apn = g_strdup(apn);
-
-               apn_changed(service);
-
-               if (service->network != NULL)
-                       connman_network_set_string(service->network,
-                                               "Cellular.APN", service->apn);
-
-               __connman_storage_save_service(service);
-       } else if (g_str_equal(name, "Username") == TRUE) {
-               const char *username;
-
-               if (type != DBUS_TYPE_STRING)
-                       return __connman_error_invalid_arguments(msg);
-
-               if (service->immutable == TRUE)
-                       return __connman_error_not_supported(msg);
-
-               if (service->type != CONNMAN_SERVICE_TYPE_CELLULAR)
-                       return __connman_error_invalid_service(msg);
-
-               dbus_message_iter_get_basic(&value, &username);
-
-               g_free(service->username);
-               service->username = g_strdup(username);
-
-               if (service->network != NULL)
-                       connman_network_set_string(service->network,
-                                       "Cellular.Username", service->username);
-
-               __connman_storage_save_service(service);
-       } else if (g_str_equal(name, "Password") == TRUE) {
-               const char *password;
-
-               if (type != DBUS_TYPE_STRING)
-                       return __connman_error_invalid_arguments(msg);
-
-               if (service->immutable == TRUE)
-                       return __connman_error_not_supported(msg);
-
-               if (service->type != CONNMAN_SERVICE_TYPE_CELLULAR)
-                       return __connman_error_invalid_service(msg);
-
-               dbus_message_iter_get_basic(&value, &password);
-
-               g_free(service->password);
-               service->password = g_strdup(password);
-
-               if (service->network != NULL)
-                       connman_network_set_string(service->network,
-                                       "Cellular.Password", service->password);
-
-               __connman_storage_save_service(service);
+               __connman_service_set_passphrase(service, passphrase);
        } else if (g_str_equal(name, "Nameservers.Configuration") == TRUE) {
                DBusMessageIter entry;
                GString *str;
@@ -1400,7 +2286,7 @@ static DBusMessage *set_property(DBusConnection *conn,
                        return __connman_error_invalid_arguments(msg);
 
                index = connman_network_get_index(service->network);
-               gw = __connman_ipconfig_get_gateway(index);
+               gw = __connman_ipconfig_get_gateway_from_index(index);
 
                if (gw && strlen(gw))
                        __connman_service_nameserver_del_routes(service);
@@ -1417,12 +2303,14 @@ static DBusMessage *set_property(DBusConnection *conn,
                                g_string_append(str, val);
                }
 
-               g_strfreev(service->nameservers);
+               g_strfreev(service->nameservers_config);
 
-               if (str->len > 0)
-                       service->nameservers = g_strsplit_set(str->str, " ", 0);
-               else
-                       service->nameservers = NULL;
+               if (str->len > 0) {
+                       service->nameservers_config =
+                               g_strsplit_set(str->str, " ", 0);
+               } else {
+                       service->nameservers_config = NULL;
+               }
 
                g_string_free(str, TRUE);
 
@@ -1465,60 +2353,65 @@ static DBusMessage *set_property(DBusConnection *conn,
 
                g_string_free(str, TRUE);
 
-               //update_domains(service);
+               update_nameservers(service);
                domain_configuration_changed(service);
 
                __connman_storage_save_service(service);
+       } else if (g_str_equal(name, "Proxy.Configuration") == TRUE) {
+               int err;
+
+               if (type != DBUS_TYPE_ARRAY)
+                       return __connman_error_invalid_arguments(msg);
+
+               err = update_proxy_configuration(service, &value);
+
+               if (err < 0)
+                       return __connman_error_failed(msg, -err);
+
+               proxy_configuration_changed(service);
+
+               __connman_storage_save_service(service);
        } else if (g_str_equal(name, "IPv4.Configuration") == TRUE ||
                        g_str_equal(name, "IPv6.Configuration")) {
 
-               enum connman_ipconfig_type type = CONNMAN_IPCONFIG_TYPE_UNKNOWN;
+               struct connman_ipconfig *ipv4 = NULL, *ipv6 = NULL;
+               enum connman_service_state state =
+                                               CONNMAN_SERVICE_STATE_UNKNOWN;
                int err = 0;
-               struct connman_ipconfig *ipv6config;
 
                DBG("%s", name);
 
-               ipv6config = connman_ipconfig_get_ipv6config(
-                                               service->ipconfig);
-               if (service->ipconfig == NULL)
+               if (service->ipconfig_ipv4 == NULL &&
+                                       service->ipconfig_ipv6 == NULL)
                        return __connman_error_invalid_property(msg);
 
-               if (is_connecting(service) ||
-                               is_connected(service)) {
-                       __connman_network_clear_ipconfig(service->network,
-                                                       service->ipconfig);
-                       __connman_network_clear_ipconfig(service->network,
-                                                               ipv6config);
-               }
-
                if (g_str_equal(name, "IPv4.Configuration") == TRUE) {
-                       type = CONNMAN_IPCONFIG_TYPE_IPV4;
-                       err = __connman_ipconfig_set_config(
-                                       service->ipconfig, type, &value);
+                       ipv4 = service->ipconfig_ipv4;
+                       err = set_ipconfig(service, ipv4, &value,
+                                       service->state_ipv4, &state);
+
                } else if (g_str_equal(name, "IPv6.Configuration") == TRUE) {
-                       type = CONNMAN_IPCONFIG_TYPE_IPV6;
-                       err = __connman_ipconfig_set_config(
-                                               ipv6config, type, &value);
+                       ipv6 = service->ipconfig_ipv6;
+                       err = set_ipconfig(service, ipv6, &value,
+                                       service->state_ipv6, &state);
                }
 
                if (err < 0) {
-                       if (is_connected(service) ||
-                                       is_connecting(service))
-                               __connman_network_set_ipconfig(
-                                                       service->network,
-                                                       service->ipconfig);
+                       if (is_connected_state(service, state) ||
+                                       is_connecting_state(service, state))
+                               __connman_network_set_ipconfig(service->network,
+                                                               ipv4, ipv6);
                        return __connman_error_failed(msg, -err);
                }
 
-               if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               if (ipv4)
                        ipv4_configuration_changed(service);
-               else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
+               else if (ipv6)
                        ipv6_configuration_changed(service);
 
-               if (is_connecting(service) ||
-                               is_connected(service))
+               if (is_connecting(service) || is_connected(service))
                        __connman_network_set_ipconfig(service->network,
-                                                       service->ipconfig);
+                                                       ipv4, ipv6);
 
                __connman_storage_save_service(service);
        } else
@@ -1529,7 +2422,7 @@ static DBusMessage *set_property(DBusConnection *conn,
 
 static void set_idle(struct connman_service *service)
 {
-       service->state = CONNMAN_SERVICE_STATE_IDLE;
+       service->state_ipv4 = service->state_ipv6 = CONNMAN_SERVICE_STATE_IDLE;
        service->error = CONNMAN_SERVICE_ERROR_UNKNOWN;
        state_changed(service);
 }
@@ -1545,10 +2438,6 @@ static DBusMessage *clear_property(DBusConnection *conn,
        dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &name,
                                                        DBUS_TYPE_INVALID);
 
-       if (__connman_security_check_privilege(msg,
-                                       CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0)
-               return __connman_error_permission_denied(msg);
-
        if (g_str_equal(name, "Error") == TRUE) {
                set_idle(service);
 
@@ -1581,7 +2470,8 @@ static connman_bool_t is_ignore(struct connman_service *service)
        if (service->ignore == TRUE)
                return TRUE;
 
-       if (service->state == CONNMAN_SERVICE_STATE_FAILURE)
+       if (combine_state(service->state_ipv4, service->state_ipv6) ==
+                                               CONNMAN_SERVICE_STATE_FAILURE)
                return TRUE;
 
        return FALSE;
@@ -1612,7 +2502,9 @@ void __connman_service_auto_connect(void)
                        return;
 
                if (is_ignore(service) == FALSE &&
-                               service->state == CONNMAN_SERVICE_STATE_IDLE)
+                       combine_state(service->state_ipv4,
+                                       service->state_ipv6) ==
+                                               CONNMAN_SERVICE_STATE_IDLE)
                        break;
 
                service = NULL;
@@ -1646,9 +2538,21 @@ static void reply_pending(struct connman_service *service, int error)
                                                                error);
                        if (reply != NULL)
                                g_dbus_send_message(connection, reply);
-               } else
-                       g_dbus_send_reply(connection, service->pending,
+               } else {
+                       const char *sender;
+
+                       sender = dbus_message_get_interface(service->pending);
+
+                       DBG("sender %s", sender);
+
+                       if (g_strcmp0(sender, CONNMAN_MANAGER_INTERFACE) == 0)
+                               g_dbus_send_reply(connection, service->pending,
+                                       DBUS_TYPE_OBJECT_PATH, &service->path,
+                                                       DBUS_TYPE_INVALID);
+                       else
+                               g_dbus_send_reply(connection, service->pending,
                                                        DBUS_TYPE_INVALID);
+               }
 
                dbus_message_unref(service->pending);
                service->pending = NULL;
@@ -1667,7 +2571,10 @@ static gboolean connect_timeout(gpointer user_data)
        if (service->network != NULL)
                __connman_network_disconnect(service->network);
 
-       __connman_ipconfig_disable(service->ipconfig);
+       __connman_ipconfig_disable(service->ipconfig_ipv4);
+       __connman_ipconfig_disable(service->ipconfig_ipv6);
+
+       __connman_stats_service_unregister(service);
 
        if (service->pending != NULL) {
                DBusMessage *reply;
@@ -1682,7 +2589,11 @@ static gboolean connect_timeout(gpointer user_data)
                autoconnect = TRUE;
 
        __connman_service_indicate_state(service,
-                                       CONNMAN_SERVICE_STATE_FAILURE);
+                                       CONNMAN_SERVICE_STATE_FAILURE,
+                                       CONNMAN_IPCONFIG_TYPE_IPV4);
+       __connman_service_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_FAILURE,
+                                       CONNMAN_IPCONFIG_TYPE_IPV6);
 
        if (autoconnect == TRUE && service->userconnect == FALSE)
                __connman_service_auto_connect();
@@ -1719,6 +2630,20 @@ static connman_bool_t get_reconnect_state(struct connman_service *service)
        return __connman_device_get_reconnect(device);
 }
 
+static void request_input_cb (struct connman_service *service,
+                       const char *passphrase, void *user_data)
+{
+       DBG ("RequestInput return, %p", service);
+
+       if (passphrase == NULL && service->wps == FALSE)
+               return;
+
+       if (passphrase != NULL)
+               __connman_service_set_passphrase(service, passphrase);
+
+       __connman_service_connect(service);
+}
+
 struct connman_service *
 __connman_service_connect_type(enum connman_service_type type)
 {
@@ -1768,8 +2693,9 @@ __connman_service_connect_type(enum connman_service_type type)
        err = __connman_service_connect(service);
        if (err < 0) {
                if (err == -ENOKEY)
-                       if (__connman_agent_request_passphrase(service,
-                                                               NULL, NULL))
+                       if ( __connman_agent_request_input(service,
+                                                       request_input_cb,
+                                                       NULL))
                                return service;
 
                if (err != -EINPROGRESS)
@@ -1796,8 +2722,7 @@ static DBusMessage *connect_service(DBusConnection *conn,
        while (g_sequence_iter_is_end(iter) == FALSE) {
                struct connman_service *temp = g_sequence_get(iter);
 
-               if (service->type == temp->type &&
-                                       is_connecting(temp) == TRUE)
+               if (service->type == temp->type && is_connecting(temp) == TRUE)
                        return __connman_error_in_progress(msg);
 
                iter = g_sequence_iter_next(iter);
@@ -1814,11 +2739,15 @@ static DBusMessage *connect_service(DBusConnection *conn,
        err = __connman_service_connect(service);
        if (err < 0) {
                if (err == -ENOKEY) {
-                       if (__connman_agent_request_passphrase(service,
-                                                       NULL, NULL) == 0)
+                       if (__connman_agent_request_input(service,
+                                                       request_input_cb,
+                                                       NULL) == 0)
                                return NULL;
                }
 
+               if (service->pending == NULL)
+                       return NULL;
+
                if (err != -EINPROGRESS) {
                        dbus_message_unref(service->pending);
                        service->pending = NULL;
@@ -1871,7 +2800,8 @@ static DBusMessage *remove_service(DBusConnection *conn,
                return __connman_error_not_supported(msg);
 
        if (service->favorite == FALSE &&
-                       service->state != CONNMAN_SERVICE_STATE_FAILURE)
+               combine_state(service->state_ipv4, service->state_ipv6) !=
+                                               CONNMAN_SERVICE_STATE_FAILURE)
                return __connman_error_not_supported(msg);
 
        if (service->network != NULL) {
@@ -1885,17 +2815,6 @@ static DBusMessage *remove_service(DBusConnection *conn,
 
        passphrase_changed(service);
 
-       g_free(service->apn);
-       service->apn = NULL;
-
-       g_free(service->username);
-       service->username = NULL;
-
-       g_free(service->password);
-       service->password = NULL;
-
-       apn_changed(service);
-
        set_idle(service);
 
        __connman_service_set_favorite(service, FALSE);
@@ -1927,7 +2846,8 @@ static DBusMessage *move_service(DBusConnection *conn,
 
        DBG("target %s", target->identifier);
 
-       if (target->state != service->state)
+       if (target->state_ipv4 != service->state_ipv4 &&
+                               target->state_ipv6 != service->state_ipv6)
                return __connman_error_invalid_service(msg);
 
        g_get_current_time(&service->modified);
@@ -2008,30 +2928,35 @@ static void service_free(gpointer user_data)
                g_free(path);
        }
 
+       g_hash_table_destroy(service->counter_table);
+
        if (service->network != NULL)
                connman_network_unref(service->network);
 
        if (service->provider != NULL)
                connman_provider_unref(service->provider);
 
-       if (service->ipconfig != NULL) {
-               connman_ipconfig_unref(service->ipconfig);
-               service->ipconfig = NULL;
+       if (service->ipconfig_ipv4 != NULL) {
+               connman_ipconfig_unref(service->ipconfig_ipv4);
+               service->ipconfig_ipv4 = NULL;
+       }
+
+       if (service->ipconfig_ipv6 != NULL) {
+               connman_ipconfig_unref(service->ipconfig_ipv6);
+               service->ipconfig_ipv6 = NULL;
        }
 
        if (service->location != NULL)
                connman_location_unref(service->location);
 
        g_strfreev(service->nameservers);
+       g_strfreev(service->nameservers_config);
        g_strfreev(service->domains);
+       g_strfreev(service->proxies);
+       g_strfreev(service->excludes);
 
-       g_free(service->nameserver);
        g_free(service->domainname);
-       g_free(service->mcc);
-       g_free(service->mnc);
-       g_free(service->apn);
-       g_free(service->username);
-       g_free(service->password);
+       g_free(service->pac);
        g_free(service->profile);
        g_free(service->name);
        g_free(service->passphrase);
@@ -2046,6 +2971,8 @@ static void service_free(gpointer user_data)
 
        if (service->stats.timer != NULL)
                g_timer_destroy(service->stats.timer);
+       if (service->stats_roaming.timer != NULL)
+               g_timer_destroy(service->stats_roaming.timer);
 
        g_free(service);
 }
@@ -2075,6 +3002,19 @@ void __connman_service_put(struct connman_service *service)
        }
 }
 
+static void stats_init(struct connman_service *service)
+{
+       /* home */
+       service->stats.valid = FALSE;
+       service->stats.enabled = FALSE;
+       service->stats.timer = g_timer_new();
+
+       /* roaming */
+       service->stats_roaming.valid = FALSE;
+       service->stats_roaming.enabled = FALSE;
+       service->stats_roaming.timer = g_timer_new();
+}
+
 static void service_initialize(struct connman_service *service)
 {
        DBG("service %p", service);
@@ -2084,7 +3024,9 @@ static void service_initialize(struct connman_service *service)
        service->type     = CONNMAN_SERVICE_TYPE_UNKNOWN;
        service->mode     = CONNMAN_SERVICE_MODE_UNKNOWN;
        service->security = CONNMAN_SERVICE_SECURITY_UNKNOWN;
-       service->state    = CONNMAN_SERVICE_STATE_UNKNOWN;
+
+       service->state_ipv4 = CONNMAN_SERVICE_STATE_UNKNOWN;
+       service->state_ipv6 = CONNMAN_SERVICE_STATE_UNKNOWN;
 
        service->favorite  = FALSE;
        service->immutable = FALSE;
@@ -2096,11 +3038,11 @@ static void service_initialize(struct connman_service *service)
 
        service->order = 0;
 
-       service->stats.valid = FALSE;
-       service->stats.enabled = FALSE;
-       service->stats.timer = g_timer_new();
+       stats_init(service);
 
        service->provider = NULL;
+
+       service->wps = FALSE;
 }
 
 /**
@@ -2112,6 +3054,10 @@ static void service_initialize(struct connman_service *service)
  */
 struct connman_service *connman_service_create(void)
 {
+       GSList *list;
+       struct connman_stats_counter *counters;
+       const char *counter;
+
        struct connman_service *service;
 
        service = g_try_new0(struct connman_service, 1);
@@ -2120,6 +3066,25 @@ struct connman_service *connman_service_create(void)
 
        DBG("service %p", service);
 
+       service->counter_table = g_hash_table_new_full(g_str_hash,
+                                               g_str_equal, NULL, g_free);
+
+       for (list = counter_list; list; list = list->next) {
+               counter = list->data;
+
+               counters = g_try_new0(struct connman_stats_counter, 1);
+               if (counters == NULL) {
+                       g_hash_table_destroy(service->counter_table);
+                       g_free(service);
+                       return NULL;
+               }
+
+               counters->append_all = TRUE;
+
+               g_hash_table_replace(service->counter_table, (gpointer)counter,
+                               counters);
+       }
+
        service_initialize(service);
 
        service->location = __connman_location_create(service);
@@ -2140,6 +3105,8 @@ struct connman_location *__connman_service_get_location(struct connman_service *
  */
 struct connman_service *connman_service_ref(struct connman_service *service)
 {
+       DBG("%p", service);
+
        g_atomic_int_inc(&service->refcount);
 
        return service;
@@ -2161,12 +3128,21 @@ static gint service_compare(gconstpointer a, gconstpointer b,
 {
        struct connman_service *service_a = (void *) a;
        struct connman_service *service_b = (void *) b;
+       enum connman_service_state state_a, state_b;
 
-       if (service_a->state != service_b->state) {
+       state_a = combine_state(service_a->state_ipv4, service_a->state_ipv6);
+       state_b = combine_state(service_b->state_ipv4, service_b->state_ipv6);
+
+       if (state_a != state_b) {
                if (is_connected(service_a) == TRUE)
                        return -1;
                if (is_connected(service_b) == TRUE)
                        return 1;
+
+               if (is_connecting(service_a) == TRUE)
+                       return -1;
+               if (is_connecting(service_b) == TRUE)
+                       return 1;
        }
 
        if (service_a->order > service_b->order)
@@ -2188,6 +3164,7 @@ static gint service_compare(gconstpointer a, gconstpointer b,
                case CONNMAN_SERVICE_TYPE_ETHERNET:
                case CONNMAN_SERVICE_TYPE_GPS:
                case CONNMAN_SERVICE_TYPE_VPN:
+               case CONNMAN_SERVICE_TYPE_GADGET:
                        break;
                case CONNMAN_SERVICE_TYPE_WIFI:
                        return 1;
@@ -2228,6 +3205,19 @@ char *connman_service_get_interface(struct connman_service *service)
        if (service == NULL)
                return NULL;
 
+       if (service->type == CONNMAN_SERVICE_TYPE_VPN) {
+               if (service->ipconfig_ipv4)
+                       index = connman_ipconfig_get_index(
+                                               service->ipconfig_ipv4);
+               else if (service->ipconfig_ipv6)
+                       index = connman_ipconfig_get_index(
+                                               service->ipconfig_ipv6);
+               else
+                       return NULL;
+
+               return connman_inet_ifname(index);
+       }
+
        if (service->network == NULL)
                return NULL;
 
@@ -2251,12 +3241,50 @@ __connman_service_get_network(struct connman_service *service)
        return service->network;
 }
 
-struct connman_ipconfig *__connman_service_get_ipconfig(struct connman_service *service)
+struct connman_ipconfig *
+__connman_service_get_ip4config(struct connman_service *service)
+{
+       if (service == NULL)
+               return NULL;
+
+       return service->ipconfig_ipv4;
+}
+
+struct connman_ipconfig *
+__connman_service_get_ip6config(struct connman_service *service)
 {
        if (service == NULL)
                return NULL;
 
-       return service->ipconfig;
+       return service->ipconfig_ipv6;
+}
+
+struct connman_ipconfig *
+__connman_service_get_ipconfig(struct connman_service *service, int family)
+{
+       if (family == AF_INET)
+               return __connman_service_get_ip4config(service);
+       else if (family == AF_INET6)
+               return __connman_service_get_ip6config(service);
+       else
+               return NULL;
+
+}
+
+enum connman_service_security __connman_service_get_security(struct connman_service *service)
+{
+       if (service == NULL)
+               return CONNMAN_SERVICE_SECURITY_UNKNOWN;
+
+       return service->security;
+}
+
+connman_bool_t __connman_service_wps_enabled(struct connman_service *service)
+{
+       if (service == NULL)
+               return FALSE;
+
+       return service->wps;
 }
 
 /**
@@ -2329,30 +3357,73 @@ void __connman_service_set_string(struct connman_service *service,
        }
 }
 
+static void service_complete(struct connman_service *service)
+{
+       reply_pending(service, EIO);
+
+       if (service->userconnect == FALSE)
+               __connman_service_auto_connect();
+
+       g_get_current_time(&service->modified);
+       __connman_storage_save_service(service);
+}
+
+static void report_error_cb(struct connman_service *service,
+                       gboolean retry, void *user_data)
+{
+       if (retry == TRUE)
+               __connman_service_connect(service);
+       else {
+               service_complete(service);
+               __connman_profile_changed(FALSE);
+               __connman_element_request_scan(CONNMAN_ELEMENT_TYPE_UNKNOWN);
+       }
+}
+
 int __connman_service_indicate_state(struct connman_service *service,
-                                       enum connman_service_state state)
+                                       enum connman_service_state new_state,
+                                       enum connman_ipconfig_type type)
 {
+       enum connman_service_state service_state,
+               state = CONNMAN_SERVICE_STATE_IDLE;
        GSequenceIter *iter;
 
-       DBG("service %p state %d", service, state);
-
        if (service == NULL)
                return -EINVAL;
 
-       if (service->state == state)
-               return -EALREADY;
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
+               if (service->state_ipv4 == new_state)
+                       return -EALREADY;
+               state = combine_state(new_state, service->state_ipv6);
+       } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) {
+               if (service->state_ipv6 == new_state)
+                       return -EALREADY;
+               state = combine_state(new_state, service->state_ipv4);
+       } else
+               return -EINVAL;
+
+       service_state = combine_state(service->state_ipv4,
+                                                       service->state_ipv6);
 
-       if (service->state == CONNMAN_SERVICE_STATE_FAILURE &&
+       DBG("service %p state %s/%s => %s new %s/%d => %s",
+                                       service,
+                                       state2string(service->state_ipv4),
+                                       state2string(service->state_ipv6),
+                                       state2string(service_state),
+                                       state2string(new_state),
+                                       type,
+                                       state2string(state));
+
+       if (service_state == CONNMAN_SERVICE_STATE_FAILURE &&
                                state == CONNMAN_SERVICE_STATE_IDLE)
                return -EINVAL;
 
-       if (service->state == CONNMAN_SERVICE_STATE_IDLE &&
+       if (service_state == CONNMAN_SERVICE_STATE_IDLE &&
                                state == CONNMAN_SERVICE_STATE_DISCONNECT)
                return -EINVAL;
 
        if (state == CONNMAN_SERVICE_STATE_IDLE &&
-                       service->state != CONNMAN_SERVICE_STATE_DISCONNECT) {
-               service->state = CONNMAN_SERVICE_STATE_DISCONNECT;
+                       service_state != CONNMAN_SERVICE_STATE_DISCONNECT) {
                state_changed(service);
 
                reply_pending(service, ECONNABORTED);
@@ -2360,10 +3431,30 @@ int __connman_service_indicate_state(struct connman_service *service,
                __connman_service_disconnect(service);
        }
 
-       if (state == CONNMAN_SERVICE_STATE_CONFIGURATION)
-               __connman_ipconfig_enable(service->ipconfig);
+       if (new_state == CONNMAN_SERVICE_STATE_CONFIGURATION) {
+               if (__connman_stats_service_register(service) == 0) {
+                       __connman_stats_get(service, FALSE,
+                                               &service->stats.data);
+                       __connman_stats_get(service, TRUE,
+                                               &service->stats_roaming.data);
+               }
+
+               if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
+                       new_state == CONNMAN_SERVICE_STATE_CONFIGURATION)
+                       __connman_ipconfig_enable(service->ipconfig_ipv4);
+               else if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
+                       new_state == CONNMAN_SERVICE_STATE_CONFIGURATION)
+                       __connman_ipconfig_enable(service->ipconfig_ipv6);
+       }
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               service->state_ipv4 = new_state;
+       else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
+               service->state_ipv6 = new_state;
+
+       if (state == service_state)
+               return -EALREADY;
 
-       service->state = state;
        state_changed(service);
 
        if (state == CONNMAN_SERVICE_STATE_ONLINE) {
@@ -2384,6 +3475,9 @@ int __connman_service_indicate_state(struct connman_service *service,
        }
 
        if (state == CONNMAN_SERVICE_STATE_READY) {
+               enum connman_service_proxy_method proxy_config;
+               enum connman_ipconfig_method method;
+
                set_reconnect_state(service, TRUE);
 
                __connman_service_set_favorite(service, TRUE);
@@ -2397,12 +3491,50 @@ int __connman_service_indicate_state(struct connman_service *service,
 
                update_nameservers(service);
                dns_changed(service);
+               domain_changed(service);
+
+               proxy_config = service->proxy_config;
+
+               /*
+                * We start WPAD if we haven't got a PAC URL from DHCP and
+                * if our proxy manual configuration is either empty or set
+                * to AUTO with an empty URL.
+                */
+               if (service->proxy == CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN &&
+                       (proxy_config == CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN ||
+                               (proxy_config ==
+                                       CONNMAN_SERVICE_PROXY_METHOD_AUTO &&
+                                       service->pac == NULL)))
+                       if (__connman_wpad_start(service) < 0) {
+                               service->proxy =
+                                       CONNMAN_SERVICE_PROXY_METHOD_DIRECT;
+
+                               __connman_notifier_proxy_changed(service);
+                       }
+
+               __connman_notifier_connect(service->type);
+
+               if (service->type == CONNMAN_SERVICE_TYPE_WIFI &&
+                       connman_network_get_bool(service->network,
+                                               "WiFi.UseWPS") == TRUE) {
+                       const char *pass;
 
-               __connman_wpad_start(service);
+                       pass = connman_network_get_string(service->network,
+                                                       "WiFi.Passphrase");
 
-               __connman_notifier_connect(service->type);
+                       __connman_service_set_passphrase(service, pass);
+
+                       connman_network_set_bool(service->network,
+                                                       "WiFi.UseWPS", FALSE);
+               }
 
                default_changed();
+
+               method = __connman_ipconfig_get_method(service->ipconfig_ipv6);
+               if (method == CONNMAN_IPCONFIG_METHOD_OFF)
+                       __connman_ipconfig_disable_ipv6(
+                                               service->ipconfig_ipv6);
+
        } else if (state == CONNMAN_SERVICE_STATE_DISCONNECT) {
                __connman_location_finish(service);
 
@@ -2412,18 +3544,18 @@ int __connman_service_indicate_state(struct connman_service *service,
 
                update_nameservers(service);
                dns_changed(service);
+               domain_changed(service);
 
                __connman_notifier_disconnect(service->type);
        }
 
        if (state == CONNMAN_SERVICE_STATE_FAILURE) {
-               reply_pending(service, EIO);
-
-               if (service->userconnect == FALSE)
-                       __connman_service_auto_connect();
-
-               g_get_current_time(&service->modified);
-               __connman_storage_save_service(service);
+               if (service->userconnect == TRUE &&
+                       __connman_agent_report_error(service,
+                                       error2string(service->error),
+                                       report_error_cb, NULL) == -EIO)
+                       return 0;
+               service_complete(service);
        } else
                service->error = CONNMAN_SERVICE_ERROR_UNKNOWN;
 
@@ -2433,8 +3565,22 @@ int __connman_service_indicate_state(struct connman_service *service,
 
        __connman_profile_changed(FALSE);
 
-       if (service->state == CONNMAN_SERVICE_STATE_IDLE ||
-                       service->state == CONNMAN_SERVICE_STATE_FAILURE)
+       service_state = combine_state(service->state_ipv4,
+                                                       service->state_ipv6);
+       if (service_state == CONNMAN_SERVICE_STATE_ONLINE)
+               default_changed();
+
+       if (service_state == CONNMAN_SERVICE_STATE_DISCONNECT) {
+               struct connman_service *def_service = get_default();
+
+               if (__connman_notifier_count_connected() == 0 &&
+                       def_service != NULL &&
+                               def_service->provider != NULL)
+                       __connman_provider_disconnect(def_service->provider);
+       }
+
+       if (service_state == CONNMAN_SERVICE_STATE_IDLE ||
+                       service_state == CONNMAN_SERVICE_STATE_FAILURE)
                __connman_element_request_scan(CONNMAN_ELEMENT_TYPE_UNKNOWN);
 
        return 0;
@@ -2450,8 +3596,49 @@ int __connman_service_indicate_error(struct connman_service *service,
 
        service->error = error;
 
+       if (service->error == CONNMAN_SERVICE_ERROR_INVALID_KEY)
+               __connman_service_set_passphrase(service, NULL);
+
+       return __connman_service_indicate_state(service,
+                                               CONNMAN_SERVICE_STATE_FAILURE,
+                                               CONNMAN_IPCONFIG_TYPE_IPV4);
+}
+
+int __connman_service_clear_error(struct connman_service *service)
+{
+       enum connman_service_state state;
+
+       DBG("service %p", service);
+
+       if (service == NULL)
+               return -EINVAL;
+
+       state = combine_state(service->state_ipv4, service->state_ipv6);
+
+       if (state != CONNMAN_SERVICE_STATE_FAILURE)
+               return -EINVAL;
+
+       service->state_ipv4 = service->state_ipv6 =
+                                               CONNMAN_SERVICE_STATE_UNKNOWN;
+       service->error = CONNMAN_SERVICE_ERROR_UNKNOWN;;
+
+       if (service->favorite == TRUE)
+               set_reconnect_state(service, TRUE);
+
+       __connman_service_indicate_state(service, CONNMAN_SERVICE_STATE_IDLE,
+                                       CONNMAN_IPCONFIG_TYPE_IPV6);
+
+       /*
+        * Toggling the IPv6 state to IDLE could trigger the auto connect
+        * machinery and consequently the IPv4 state.
+        */
+       if (service->state_ipv4 != CONNMAN_SERVICE_STATE_UNKNOWN &&
+                       service->state_ipv4 != CONNMAN_SERVICE_STATE_FAILURE)
+               return 0;
+
        return __connman_service_indicate_state(service,
-                                       CONNMAN_SERVICE_STATE_FAILURE);
+                                               CONNMAN_SERVICE_STATE_IDLE,
+                                               CONNMAN_IPCONFIG_TYPE_IPV4);
 }
 
 int __connman_service_indicate_default(struct connman_service *service)
@@ -2494,22 +3681,15 @@ static connman_bool_t prepare_network(struct connman_service *service)
                                                        &ssid_len) == NULL)
                        return FALSE;
 
-               connman_network_set_string(service->network,
+               if (service->passphrase != NULL)
+                       connman_network_set_string(service->network,
                                "WiFi.Passphrase", service->passphrase);
                break;
        case CONNMAN_NETWORK_TYPE_ETHERNET:
        case CONNMAN_NETWORK_TYPE_WIMAX:
        case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
        case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
-               break;
        case CONNMAN_NETWORK_TYPE_CELLULAR:
-               connman_network_set_string(service->network,
-                                               "Cellular.APN", service->apn);
-
-               connman_network_set_string(service->network,
-                                       "Cellular.Username", service->username);
-               connman_network_set_string(service->network,
-                                       "Cellular.Password", service->password);
                break;
        }
 
@@ -2550,31 +3730,21 @@ static void prepare_8021x(struct connman_service *service)
                                                        service->phase2);
 }
 
-int __connman_service_connect(struct connman_service *service)
+static int service_connect(struct connman_service *service)
 {
        int err;
 
-       DBG("service %p", service);
-
-       if (is_connected(service) == TRUE)
-               return -EISCONN;
-
-       if (is_connecting(service) == TRUE)
-               return -EALREADY;
-
        switch (service->type) {
        case CONNMAN_SERVICE_TYPE_UNKNOWN:
        case CONNMAN_SERVICE_TYPE_SYSTEM:
        case CONNMAN_SERVICE_TYPE_GPS:
+       case CONNMAN_SERVICE_TYPE_GADGET:
                return -EINVAL;
        case CONNMAN_SERVICE_TYPE_ETHERNET:
        case CONNMAN_SERVICE_TYPE_WIMAX:
        case CONNMAN_SERVICE_TYPE_BLUETOOTH:
-       case CONNMAN_SERVICE_TYPE_VPN:
-               break;
        case CONNMAN_SERVICE_TYPE_CELLULAR:
-               if (service->apn == NULL)
-                       return -EINVAL;
+       case CONNMAN_SERVICE_TYPE_VPN:
                break;
        case CONNMAN_SERVICE_TYPE_WIFI:
                switch (service->security) {
@@ -2585,8 +3755,16 @@ int __connman_service_connect(struct connman_service *service)
                case CONNMAN_SERVICE_SECURITY_PSK:
                case CONNMAN_SERVICE_SECURITY_WPA:
                case CONNMAN_SERVICE_SECURITY_RSN:
-                       if (service->passphrase == NULL)
-                               return -ENOKEY;
+                       if (service->passphrase == NULL) {
+                               if (service->network == NULL)
+                                       return -EOPNOTSUPP;
+
+                               if (service->wps == FALSE ||
+                                       connman_network_get_bool(
+                                                       service->network,
+                                                       "WiFi.UseWPS") == FALSE)
+                                       return -ENOKEY;
+                       }
                        break;
                case CONNMAN_SERVICE_SECURITY_8021X:
                        break;
@@ -2611,7 +3789,17 @@ int __connman_service_connect(struct connman_service *service)
                        break;
                }
 
-               __connman_ipconfig_enable(service->ipconfig);
+               if (__connman_stats_service_register(service) == 0) {
+                       __connman_stats_get(service, FALSE,
+                                               &service->stats.data);
+                       __connman_stats_get(service, TRUE,
+                                               &service->stats_roaming.data);
+               }
+
+               if (service->ipconfig_ipv4)
+                       __connman_ipconfig_enable(service->ipconfig_ipv4);
+               if (service->ipconfig_ipv6)
+                       __connman_ipconfig_enable(service->ipconfig_ipv6);
 
                err = __connman_network_connect(service->network);
        } else if (service->type == CONNMAN_SERVICE_TYPE_VPN &&
@@ -2622,22 +3810,69 @@ int __connman_service_connect(struct connman_service *service)
 
        if (err < 0) {
                if (err != -EINPROGRESS) {
-                       __connman_ipconfig_disable(service->ipconfig);
-                       return err;
+                       __connman_ipconfig_disable(service->ipconfig_ipv4);
+                       __connman_ipconfig_disable(service->ipconfig_ipv6);
+                       __connman_stats_service_unregister(service);
                }
+       }
+
+       return err;
+}
+
+
+int __connman_service_connect(struct connman_service *service)
+{
+       enum connman_service_state state;
+       int err;
+
+       state = combine_state(service->state_ipv4, service->state_ipv6);
+
+       DBG("service %p state %s", service, state2string(state));
+
+       if (is_connected(service) == TRUE)
+               return -EISCONN;
+
+       if (is_connecting(service) == TRUE)
+               return -EALREADY;
+
+       switch (service->type) {
+       case CONNMAN_SERVICE_TYPE_UNKNOWN:
+       case CONNMAN_SERVICE_TYPE_SYSTEM:
+       case CONNMAN_SERVICE_TYPE_GPS:
+       case CONNMAN_SERVICE_TYPE_GADGET:
+               return -EINVAL;
+       default:
+               err = service_connect(service);
+       }
+
+       if (err >= 0)
+               return 0;
 
+       if (err == -EINPROGRESS) {
                service->timeout = g_timeout_add_seconds(CONNECT_TIMEOUT,
                                                connect_timeout, service);
 
                return -EINPROGRESS;
        }
 
-       return 0;
+       if (err == -ENOKEY)
+               return -ENOKEY;
+
+       if (service->userconnect == TRUE)
+               reply_pending(service, err);
+
+       __connman_service_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_FAILURE,
+                                       CONNMAN_IPCONFIG_TYPE_IPV4);
+       __connman_service_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_FAILURE,
+                                       CONNMAN_IPCONFIG_TYPE_IPV6);
+
+       return err;
 }
 
 int __connman_service_disconnect(struct connman_service *service)
 {
-       struct connman_ipconfig *ipv6config;
        int err;
 
        DBG("service %p", service);
@@ -2650,22 +3885,27 @@ int __connman_service_disconnect(struct connman_service *service)
        else
                return -EOPNOTSUPP;
 
-       __connman_ipconfig_clear_address(service->ipconfig);
+       if (err < 0 && err != -EINPROGRESS)
+               return err;
 
-       ipv6config = connman_ipconfig_get_ipv6config(service->ipconfig);
+       __connman_6to4_remove(service->ipconfig_ipv4);
 
-       __connman_ipconfig_clear_address(ipv6config);
+       if (service->ipconfig_ipv4)
+               __connman_ipconfig_set_proxy_autoconfig(service->ipconfig_ipv4,
+                                                       NULL);
+       else
+               __connman_ipconfig_set_proxy_autoconfig(service->ipconfig_ipv6,
+                                                       NULL);
 
-       __connman_ipconfig_disable(service->ipconfig);
+       __connman_ipconfig_address_remove(service->ipconfig_ipv4);
+       __connman_ipconfig_address_remove(service->ipconfig_ipv6);
 
-       if (err < 0) {
-               if (err != -EINPROGRESS)
-                       return err;
+       __connman_ipconfig_disable(service->ipconfig_ipv4);
+       __connman_ipconfig_disable(service->ipconfig_ipv6);
 
-               return -EINPROGRESS;
-       }
+       __connman_stats_service_unregister(service);
 
-       return 0;
+       return err;
 }
 
 /**
@@ -2755,8 +3995,6 @@ static struct connman_network *create_hidden_wifi(struct connman_device *device,
        index = connman_device_get_index(device);
        connman_network_set_index(network, index);
 
-       connman_network_set_protocol(network, CONNMAN_NETWORK_PROTOCOL_IP);
-
        if (connman_device_add_network(device, network) < 0) {
                connman_network_unref(network);
                return NULL;
@@ -2836,11 +4074,11 @@ int __connman_service_create_and_connect(DBusMessage *msg)
                                g_strcmp0(security, "ieee8021x") != 0)
                return -EINVAL;
 
-       device = __connman_element_find_device(CONNMAN_DEVICE_TYPE_WIFI);
+       device = __connman_element_find_device(CONNMAN_SERVICE_TYPE_WIFI);
        if (device == NULL)
                return -EOPNOTSUPP;
 
-       ident = __connman_device_get_ident(device);
+       ident = connman_device_get_ident(device);
        if (ident == NULL)
                return -EOPNOTSUPP;
 
@@ -2880,6 +4118,16 @@ done:
                goto failed;
        }
 
+       if (is_connected(service) == TRUE) {
+               err = -EISCONN;
+               goto failed;
+       }
+
+       if (is_connecting(service) == TRUE) {
+               err = -EALREADY;
+               goto failed;
+       }
+
        set_reconnect_state(service, FALSE);
 
        __connman_device_disconnect(device);
@@ -2895,9 +4143,7 @@ done:
        if (err < 0 && err != -EINPROGRESS)
                goto failed;
 
-       g_dbus_send_reply(connection, msg,
-                               DBUS_TYPE_OBJECT_PATH, &service->path,
-                                                       DBUS_TYPE_INVALID);
+       service->pending = dbus_message_ref(msg);
 
        return 0;
 
@@ -2930,7 +4176,7 @@ static struct connman_service *service_get(const char *identifier)
        if (iter != NULL) {
                service = g_sequence_get(iter);
                if (service != NULL)
-                       g_atomic_int_inc(&service->refcount);
+                       connman_service_ref(service);
                return service;
        }
 
@@ -2990,7 +4236,10 @@ static void service_up(struct connman_ipconfig *ipconfig)
 
        connman_info("%s up", connman_ipconfig_get_ifname(ipconfig));
 
+       link_changed(service);
+
        service->stats.valid = FALSE;
+       service->stats_roaming.valid = FALSE;
 }
 
 static void service_down(struct connman_ipconfig *ipconfig)
@@ -3020,18 +4269,52 @@ static void service_lower_down(struct connman_ipconfig *ipconfig)
 static void service_ip_bound(struct connman_ipconfig *ipconfig)
 {
        struct connman_service *service = connman_ipconfig_get_data(ipconfig);
+       enum connman_ipconfig_method method = CONNMAN_IPCONFIG_METHOD_UNKNOWN;
+       enum connman_ipconfig_type type = CONNMAN_IPCONFIG_TYPE_UNKNOWN;
 
        connman_info("%s ip bound", connman_ipconfig_get_ifname(ipconfig));
 
+       type = __connman_ipconfig_get_config_type(ipconfig);
+       method = __connman_ipconfig_get_method(ipconfig);
+
+       DBG("service %p ipconfig %p type %d method %d", service, ipconfig,
+                                                       type, method);
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
+                       method == CONNMAN_IPCONFIG_METHOD_AUTO)
+               __connman_service_indicate_state(service,
+                                               CONNMAN_SERVICE_STATE_READY,
+                                               CONNMAN_IPCONFIG_TYPE_IPV6);
+
        settings_changed(service);
 }
 
 static void service_ip_release(struct connman_ipconfig *ipconfig)
 {
        struct connman_service *service = connman_ipconfig_get_data(ipconfig);
+       enum connman_ipconfig_method method = CONNMAN_IPCONFIG_METHOD_UNKNOWN;
+       enum connman_ipconfig_type type = CONNMAN_IPCONFIG_TYPE_UNKNOWN;
 
        connman_info("%s ip release", connman_ipconfig_get_ifname(ipconfig));
 
+       type = __connman_ipconfig_get_config_type(ipconfig);
+       method = __connman_ipconfig_get_method(ipconfig);
+
+       DBG("service %p ipconfig %p type %d method %d", service, ipconfig,
+                                                       type, method);
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
+                       method == CONNMAN_IPCONFIG_METHOD_OFF)
+               __connman_service_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_DISCONNECT,
+                                       CONNMAN_IPCONFIG_TYPE_IPV6);
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
+                       method == CONNMAN_IPCONFIG_METHOD_OFF)
+               __connman_service_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_DISCONNECT,
+                                       CONNMAN_IPCONFIG_TYPE_IPV4);
+
        settings_changed(service);
 }
 
@@ -3044,34 +4327,51 @@ static const struct connman_ipconfig_ops service_ops = {
        .ip_release     = service_ip_release,
 };
 
-static void setup_ipconfig(struct connman_service *service, int index)
+static void setup_ip4config(struct connman_service *service, int index)
 {
        if (index < 0)
                return;
 
-       service->ipconfig = connman_ipconfig_create(index);
-       if (service->ipconfig == NULL)
+       service->ipconfig_ipv4 = connman_ipconfig_create(index,
+                                               CONNMAN_IPCONFIG_TYPE_IPV4);
+       if (service->ipconfig_ipv4 == NULL)
                return;
 
-       connman_ipconfig_set_method(service->ipconfig,
+       connman_ipconfig_set_method(service->ipconfig_ipv4,
                                        CONNMAN_IPCONFIG_METHOD_DHCP);
 
-       connman_ipconfig_set_data(service->ipconfig, service);
+       connman_ipconfig_set_data(service->ipconfig_ipv4, service);
+
+       connman_ipconfig_set_ops(service->ipconfig_ipv4, &service_ops);
+}
+
+static void setup_ip6config(struct connman_service *service, int index)
+{
+       if (index < 0)
+               return;
+
+       service->ipconfig_ipv6 = connman_ipconfig_create(index,
+                                               CONNMAN_IPCONFIG_TYPE_IPV6);
+       if (service->ipconfig_ipv6 == NULL)
+               return;
+
+       connman_ipconfig_set_data(service->ipconfig_ipv6, service);
 
-       connman_ipconfig_set_ops(service->ipconfig, &service_ops);
+       connman_ipconfig_set_ops(service->ipconfig_ipv6, &service_ops);
 }
 
-void __connman_service_create_ipconfig(struct connman_service *service,
+void __connman_service_create_ip4config(struct connman_service *service,
                                                                int index)
 {
-       struct connman_ipconfig *ipv6config;
        const char *ident = service->profile;
        GKeyFile *keyfile;
 
-       if (service->ipconfig != NULL)
+       DBG("ipv4 %p", service->ipconfig_ipv4);
+
+       if (service->ipconfig_ipv4 != NULL)
                return;
 
-       setup_ipconfig(service, index);
+       setup_ip4config(service, index);
 
        if (ident == NULL)
                return;
@@ -3080,14 +4380,36 @@ void __connman_service_create_ipconfig(struct connman_service *service,
        if (keyfile == NULL)
                return;
 
+       if (service->ipconfig_ipv4)
+               __connman_ipconfig_load(service->ipconfig_ipv4, keyfile,
+                                       service->identifier, "IPv4.");
+       g_key_file_free(keyfile);
+}
+
+void __connman_service_create_ip6config(struct connman_service *service,
+                                                               int index)
+{
+       const char *ident = service->profile;
+       GKeyFile *keyfile;
+
+       DBG("ipv6 %p", service->ipconfig_ipv6);
+
+       if (service->ipconfig_ipv6 != NULL)
+               return;
+
+       setup_ip6config(service, index);
+
+       if (ident == NULL)
+               return;
+
+       keyfile = __connman_storage_open_profile(ident);
+       if (keyfile == NULL)
+               return;
 
-       ipv6config = connman_ipconfig_get_ipv6config(service->ipconfig);
-       if (ipv6config != NULL)
-               __connman_ipconfig_load(ipv6config, keyfile,
+       if (service->ipconfig_ipv6 != NULL)
+               __connman_ipconfig_load(service->ipconfig_ipv6, keyfile,
                                        service->identifier, "IPv6.");
 
-       __connman_ipconfig_load(service->ipconfig, keyfile,
-                                       service->identifier, "IPv4.");
        g_key_file_free(keyfile);
 }
 
@@ -3121,6 +4443,35 @@ struct connman_service *__connman_service_lookup_from_network(struct connman_net
        return service;
 }
 
+struct connman_service *__connman_service_lookup_from_index(int index)
+{
+       struct connman_service *service;
+       GSequenceIter *iter;
+
+       iter = g_sequence_get_begin_iter(service_list);
+
+       while (g_sequence_iter_is_end(iter) == FALSE) {
+               service = g_sequence_get(iter);
+
+               if (connman_ipconfig_get_index(service->ipconfig_ipv4)
+                                                       == index)
+                       return service;
+
+               if (connman_ipconfig_get_index(service->ipconfig_ipv6)
+                                                       == index)
+                       return service;
+
+               iter = g_sequence_iter_next(iter);
+       }
+
+       return NULL;
+}
+
+const char *__connman_service_get_ident(struct connman_service *service)
+{
+       return service->identifier;
+}
+
 const char *__connman_service_get_path(struct connman_service *service)
 {
        return service->path;
@@ -3142,6 +4493,8 @@ unsigned int __connman_service_get_order(struct connman_service *service)
        if (iter != NULL) {
                if (g_sequence_iter_get_position(iter) == 0)
                        service->order = 1;
+               else if (service->type == CONNMAN_SERVICE_TYPE_VPN)
+                       service->order = 10;
                else
                        service->order = 0;
        }
@@ -3267,20 +4620,13 @@ static void update_from_network(struct connman_service *service,
        str = connman_network_get_string(network, "WiFi.Security");
        service->security = convert_wifi_security(str);
 
-       str = connman_network_get_string(network, "Cellular.MCC");
-       g_free(service->mcc);
-       service->mcc = g_strdup(str);
-
-       str = connman_network_get_string(network, "Cellular.MNC");
-       g_free(service->mnc);
-       service->mnc = g_strdup(str);
-
        if (service->type == CONNMAN_SERVICE_TYPE_CELLULAR) {
                connman_uint8_t value = connman_network_get_uint8(network,
                                                        "Cellular.Mode");
 
                service->mode = convert_cellular_mode(value);
-       }
+       } else if (service->type == CONNMAN_SERVICE_TYPE_WIFI)
+               service->wps = connman_network_get_bool(network, "WiFi.WPS");
 
        if (service->strength > strength && service->network != NULL) {
                connman_network_unref(service->network);
@@ -3306,8 +4652,10 @@ static void update_from_network(struct connman_service *service,
 struct connman_service * __connman_service_create_from_network(struct connman_network *network)
 {
        struct connman_service *service;
+       struct connman_device *device;
        const char *ident, *group;
        char *name;
+       int index;
 
        DBG("network %p", network);
 
@@ -3346,6 +4694,7 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne
        case CONNMAN_SERVICE_TYPE_BLUETOOTH:
        case CONNMAN_SERVICE_TYPE_GPS:
        case CONNMAN_SERVICE_TYPE_VPN:
+       case CONNMAN_SERVICE_TYPE_GADGET:
                service->autoconnect = FALSE;
                break;
        case CONNMAN_SERVICE_TYPE_WIFI:
@@ -3354,16 +4703,25 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne
                break;
        }
 
-       service->state = CONNMAN_SERVICE_STATE_IDLE;
+       service->state_ipv4 = service->state_ipv6 = CONNMAN_SERVICE_STATE_IDLE;
 
        update_from_network(service, network);
 
-       setup_ipconfig(service, connman_network_get_index(network));
+       index = connman_network_get_index(network);
+
+       if (service->ipconfig_ipv4 == NULL)
+               setup_ip4config(service, index);
+
+       if (service->ipconfig_ipv6 == NULL)
+               setup_ip6config(service, index);
 
        service_register(service);
 
-       if (service->favorite == TRUE)
-               __connman_service_auto_connect();
+       if (service->favorite == TRUE) {
+               device = connman_network_get_device(service->network);
+               if (device && __connman_device_scanning(device) == FALSE)
+                       __connman_service_auto_connect();
+       }
 
        return service;
 }
@@ -3376,6 +4734,7 @@ void __connman_service_update_from_network(struct connman_network *network)
        connman_bool_t roaming;
        GSequenceIter *iter;
        const char *name;
+       connman_bool_t stats_enable;
 
        DBG("network %p", network);
 
@@ -3395,6 +4754,9 @@ void __connman_service_update_from_network(struct connman_network *network)
                                DBUS_TYPE_STRING, &service->name);
        }
 
+       if (service->type == CONNMAN_SERVICE_TYPE_WIFI)
+               service->wps = connman_network_get_bool(network, "WiFi.WPS");
+
        strength = connman_network_get_uint8(service->network, "Strength");
        if (strength == service->strength)
                goto roaming;
@@ -3408,8 +4770,15 @@ roaming:
        if (roaming == service->roaming)
                goto done;
 
+       stats_enable = stats_enabled(service);
+       if (stats_enable == TRUE)
+               stats_stop(service);
+
        service->roaming = roaming;
 
+       if (stats_enable == TRUE)
+               stats_start(service);
+
        roaming_changed(service);
 
        iter = g_hash_table_lookup(service_hash, service->identifier);
@@ -3475,7 +4844,7 @@ __connman_service_create_from_provider(struct connman_provider *provider)
        service->provider = connman_provider_ref(provider);
        service->autoconnect = FALSE;
 
-       service->state = CONNMAN_SERVICE_STATE_IDLE;
+       service->state_ipv4 = service->state_ipv6 = CONNMAN_SERVICE_STATE_IDLE;
 
        str = connman_provider_get_string(provider, "Name");
        if (str != NULL) {
@@ -3490,70 +4859,33 @@ __connman_service_create_from_provider(struct connman_provider *provider)
 
        service->strength = 0;
 
-       service->ipconfig = connman_ipconfig_create(index);
-       if (service->ipconfig == NULL)
+       if (service->ipconfig_ipv4 == NULL)
+               service->ipconfig_ipv4 = connman_ipconfig_create(index,
+                                               CONNMAN_IPCONFIG_TYPE_IPV4);
+       if (service->ipconfig_ipv4 == NULL)
                return service;
 
-       connman_ipconfig_set_method(service->ipconfig,
+       connman_ipconfig_set_method(service->ipconfig_ipv4,
                                        CONNMAN_IPCONFIG_METHOD_MANUAL);
+       connman_ipconfig_set_data(service->ipconfig_ipv4, service);
+       connman_ipconfig_set_ops(service->ipconfig_ipv4, &service_ops);
 
-       connman_ipconfig_set_data(service->ipconfig, service);
+       if (service->ipconfig_ipv6 == NULL)
+               service->ipconfig_ipv6 = connman_ipconfig_create(index,
+                                               CONNMAN_IPCONFIG_TYPE_IPV6);
+       if (service->ipconfig_ipv6 == NULL)
+               return service;
 
-       connman_ipconfig_set_ops(service->ipconfig, &service_ops);
+       connman_ipconfig_set_method(service->ipconfig_ipv6,
+                                       CONNMAN_IPCONFIG_METHOD_OFF);
+       connman_ipconfig_set_data(service->ipconfig_ipv6, service);
+       connman_ipconfig_set_ops(service->ipconfig_ipv6, &service_ops);
 
        service_register(service);
 
        return service;
 }
 
-void __connman_service_stats_update(struct connman_service *service,
-                               unsigned int rx_packets, unsigned int tx_packets,
-                               unsigned int rx_bytes, unsigned int tx_bytes,
-                               unsigned int rx_errors, unsigned int tx_errors,
-                               unsigned int rx_dropped, unsigned int tx_dropped)
-{
-       unsigned int seconds;
-       struct connman_stats *stats = &service->stats;
-
-       DBG("service %p", service);
-
-       if (is_connected(service) == FALSE)
-               return;
-
-       if (stats->valid == TRUE) {
-               stats->rx_packets +=
-                       rx_packets - stats->rx_packets_last;
-               stats->tx_packets +=
-                       tx_packets - stats->tx_packets_last;
-               stats->rx_bytes +=
-                       rx_bytes - stats->rx_bytes_last;
-               stats->tx_bytes +=
-                       tx_bytes - stats->tx_bytes_last;
-               stats->rx_errors +=
-                       rx_errors - stats->rx_errors_last;
-               stats->tx_errors +=
-                       tx_errors - stats->tx_errors_last;
-               stats->rx_dropped +=
-                       rx_dropped - stats->rx_dropped_last;
-               stats->tx_dropped +=
-                       tx_dropped - stats->tx_dropped_last;
-       } else {
-               stats->valid = TRUE;
-       }
-
-       stats->rx_packets_last = rx_packets;
-       stats->tx_packets_last = tx_packets;
-       stats->rx_bytes_last = rx_bytes;
-       stats->tx_bytes_last = tx_bytes;
-       stats->rx_errors_last = rx_errors;
-       stats->tx_errors_last = tx_errors;
-       stats->rx_dropped_last = rx_dropped;
-       stats->tx_dropped_last = tx_dropped;
-
-       seconds = g_timer_elapsed(stats->timer, NULL);
-       stats->time = stats->time_start + seconds;
-}
-
 static int service_load(struct connman_service *service)
 {
        const char *ident = service->profile;
@@ -3598,6 +4930,7 @@ static int service_load(struct connman_service *service)
        case CONNMAN_SERVICE_TYPE_ETHERNET:
        case CONNMAN_SERVICE_TYPE_GPS:
        case CONNMAN_SERVICE_TYPE_VPN:
+       case CONNMAN_SERVICE_TYPE_GADGET:
                break;
        case CONNMAN_SERVICE_TYPE_WIFI:
                if (service->name == NULL) {
@@ -3652,15 +4985,6 @@ static int service_load(struct connman_service *service)
        case CONNMAN_SERVICE_TYPE_WIMAX:
        case CONNMAN_SERVICE_TYPE_BLUETOOTH:
        case CONNMAN_SERVICE_TYPE_CELLULAR:
-               service->apn = g_key_file_get_string(keyfile,
-                                       service->identifier, "APN", NULL);
-
-               service->username = g_key_file_get_string(keyfile,
-                                       service->identifier, "Username", NULL);
-
-               service->password = g_key_file_get_string(keyfile,
-                                       service->identifier, "Password", NULL);
-
                service->favorite = g_key_file_get_boolean(keyfile,
                                service->identifier, "Favorite", NULL);
 
@@ -3673,7 +4997,9 @@ static int service_load(struct connman_service *service)
                str = g_key_file_get_string(keyfile,
                                service->identifier, "Failure", NULL);
                if (str != NULL) {
-                       service->state = CONNMAN_SERVICE_STATE_FAILURE;
+                       if (service->favorite == FALSE)
+                               service->state_ipv4 = service->state_ipv6 =
+                                       CONNMAN_SERVICE_STATE_FAILURE;
                        service->error = string2error(str);
                }
                break;
@@ -3693,24 +5019,19 @@ static int service_load(struct connman_service *service)
                service->passphrase = str;
        }
 
-       if (service->ipconfig != NULL) {
-               struct connman_ipconfig *ipv6config;
+       if (service->ipconfig_ipv4 != NULL)
+               __connman_ipconfig_load(service->ipconfig_ipv4, keyfile,
+                                       service->identifier, "IPv4.");
 
-               ipv6config = connman_ipconfig_get_ipv6config(
-                                               service->ipconfig);
-               if (ipv6config != NULL)
-                       __connman_ipconfig_load(ipv6config, keyfile,
+       if (service->ipconfig_ipv6 != NULL)
+               __connman_ipconfig_load(service->ipconfig_ipv6, keyfile,
                                        service->identifier, "IPv6.");
 
-               __connman_ipconfig_load(service->ipconfig, keyfile,
-                                       service->identifier, "IPv4.");
-       }
-
-       service->nameservers = g_key_file_get_string_list(keyfile,
+       service->nameservers_config = g_key_file_get_string_list(keyfile,
                        service->identifier, "Nameservers", &length, NULL);
-       if (service->nameservers != NULL && length == 0) {
-               g_strfreev(service->nameservers);
-               service->nameservers = NULL;
+       if (service->nameservers_config != NULL && length == 0) {
+               g_strfreev(service->nameservers_config);
+               service->nameservers_config = NULL;
        }
 
        service->domains = g_key_file_get_string_list(keyfile,
@@ -3720,7 +5041,34 @@ static int service_load(struct connman_service *service)
                service->domains = NULL;
        }
 
-       stats_load(service, keyfile, service->identifier);
+       str = g_key_file_get_string(keyfile,
+                               service->identifier, "Proxy.Method", NULL);
+       if (str != NULL)
+               service->proxy_config = string2proxymethod(str);
+
+       g_free(str);
+
+       service->proxies = g_key_file_get_string_list(keyfile,
+                       service->identifier, "Proxy.Servers", &length, NULL);
+       if (service->proxies != NULL && length == 0) {
+               g_strfreev(service->proxies);
+               service->proxies = NULL;
+       }
+
+       service->excludes = g_key_file_get_string_list(keyfile,
+                       service->identifier, "Proxy.Excludes", &length, NULL);
+       if (service->excludes != NULL && length == 0) {
+               g_strfreev(service->excludes);
+               service->excludes = NULL;
+       }
+
+       str = g_key_file_get_string(keyfile,
+                               service->identifier, "Proxy.URL", NULL);
+       if (str != NULL) {
+               g_free(service->pac);
+               service->pac = str;
+       }
+
 done:
        g_key_file_free(keyfile);
 
@@ -3734,6 +5082,7 @@ static int service_save(struct connman_service *service)
        gchar *pathname, *data = NULL;
        gsize length;
        gchar *str;
+       const char *cst_str = NULL;
        int err = 0;
 
        DBG("service %p", service);
@@ -3769,6 +5118,7 @@ update:
        case CONNMAN_SERVICE_TYPE_ETHERNET:
        case CONNMAN_SERVICE_TYPE_GPS:
        case CONNMAN_SERVICE_TYPE_VPN:
+       case CONNMAN_SERVICE_TYPE_GADGET:
                break;
        case CONNMAN_SERVICE_TYPE_WIFI:
                if (service->network) {
@@ -3804,18 +5154,6 @@ update:
        case CONNMAN_SERVICE_TYPE_WIMAX:
        case CONNMAN_SERVICE_TYPE_BLUETOOTH:
        case CONNMAN_SERVICE_TYPE_CELLULAR:
-               if (service->apn != NULL)
-                       g_key_file_set_string(keyfile, service->identifier,
-                                                       "APN", service->apn);
-
-               if (service->username != NULL)
-                       g_key_file_set_string(keyfile, service->identifier,
-                                               "Username", service->username);
-
-               if (service->password != NULL)
-                       g_key_file_set_string(keyfile, service->identifier,
-                                               "Password", service->password);
-
                g_key_file_set_boolean(keyfile, service->identifier,
                                        "Favorite", service->favorite);
 
@@ -3823,7 +5161,8 @@ update:
                        g_key_file_set_boolean(keyfile, service->identifier,
                                        "AutoConnect", service->autoconnect);
 
-               if (service->state == CONNMAN_SERVICE_STATE_FAILURE) {
+               if (service->state_ipv4 == CONNMAN_SERVICE_STATE_FAILURE ||
+                       service->state_ipv6 == CONNMAN_SERVICE_STATE_FAILURE) {
                        const char *failure = error2string(service->error);
                        if (failure != NULL)
                                g_key_file_set_string(keyfile,
@@ -3850,24 +5189,20 @@ update:
                g_key_file_remove_key(keyfile, service->identifier,
                                                        "Passphrase", NULL);
 
-       if (service->ipconfig != NULL) {
-               struct connman_ipconfig *ipv6config;
+       if (service->ipconfig_ipv4 != NULL)
+               __connman_ipconfig_save(service->ipconfig_ipv4, keyfile,
+                                       service->identifier, "IPv4.");
 
-               ipv6config = connman_ipconfig_get_ipv6config(service->ipconfig);
-               if (ipv6config != NULL)
-                       __connman_ipconfig_save(ipv6config, keyfile,
+       if (service->ipconfig_ipv6 != NULL)
+               __connman_ipconfig_save(service->ipconfig_ipv6, keyfile,
                                                service->identifier, "IPv6.");
 
-               __connman_ipconfig_save(service->ipconfig, keyfile,
-                                       service->identifier, "IPv4.");
-       }
-
-       if (service->nameservers != NULL) {
-               guint len = g_strv_length(service->nameservers);
+       if (service->nameservers_config != NULL) {
+               guint len = g_strv_length(service->nameservers_config);
 
                g_key_file_set_string_list(keyfile, service->identifier,
                                                                "Nameservers",
-                               (const gchar **) service->nameservers, len);
+                               (const gchar **) service->nameservers_config, len);
        } else
                g_key_file_remove_key(keyfile, service->identifier,
                                                        "Nameservers", NULL);
@@ -3882,7 +5217,37 @@ update:
                g_key_file_remove_key(keyfile, service->identifier,
                                                        "Domains", NULL);
 
-       stats_save(service, keyfile, service->identifier);
+       cst_str = proxymethod2string(service->proxy_config);
+       if (cst_str != NULL)
+               g_key_file_set_string(keyfile, service->identifier,
+                               "Proxy.Method", cst_str);
+
+       if (service->proxies != NULL) {
+               guint len = g_strv_length(service->proxies);
+
+               g_key_file_set_string_list(keyfile, service->identifier,
+                               "Proxy.Servers",
+                               (const gchar **) service->proxies, len);
+       } else
+               g_key_file_remove_key(keyfile, service->identifier,
+                                               "Proxy.Servers", NULL);
+
+       if (service->excludes != NULL) {
+               guint len = g_strv_length(service->excludes);
+
+               g_key_file_set_string_list(keyfile, service->identifier,
+                               "Proxy.Excludes",
+                               (const gchar **) service->excludes, len);
+       } else
+               g_key_file_remove_key(keyfile, service->identifier,
+                                               "Proxy.Excludes", NULL);
+
+       if (service->pac != NULL && strlen(service->pac) > 0)
+               g_key_file_set_string(keyfile, service->identifier,
+                                       "Proxy.URL", service->pac);
+       else
+               g_key_file_remove_key(keyfile, service->identifier,
+                                                       "Proxy.URL", NULL);
 
        data = g_key_file_to_data(keyfile, &length, NULL);
 
@@ -3933,6 +5298,9 @@ void __connman_service_cleanup(void)
        g_hash_table_destroy(service_hash);
        service_hash = NULL;
 
+       g_slist_free(counter_list);
+       counter_list = NULL;
+
        connman_storage_unregister(&service_storage);
 
        dbus_connection_unref(connection);