unit: Add Session API binding
[framework/connectivity/connman.git] / plugins / ofono.c
index 1cb0961..0c23462 100644 (file)
@@ -25,6 +25,7 @@
 #endif
 
 #include <errno.h>
+#include <stdlib.h>
 
 #include <gdbus.h>
 #include <string.h>
 #include <connman/element.h>
 #include <connman/device.h>
 #include <connman/network.h>
+#include <connman/ipconfig.h>
 #include <connman/dbus.h>
 #include <connman/inet.h>
+#include <connman/technology.h>
 #include <connman/log.h>
 
+#include "mcc.h"
+
 #define OFONO_SERVICE                  "org.ofono"
 
 #define OFONO_MANAGER_INTERFACE                OFONO_SERVICE ".Manager"
@@ -74,10 +79,28 @@ struct modem_data {
        gboolean has_gprs;
        gboolean available;
        gboolean pending_online;
-       gboolean requested_online;
-       gboolean online;
+       dbus_bool_t requested_online;
+       dbus_bool_t online;
+
+       /* org.ofono.ConnectionManager properties */
+       dbus_bool_t powered;
+       dbus_bool_t attached;
+       dbus_bool_t roaming_allowed;
 
+       connman_bool_t registered;
+       connman_bool_t roaming;
        uint8_t strength, has_strength;
+       char *operator;
+};
+
+struct network_info {
+       struct connman_network *network;
+
+       enum connman_ipconfig_method ipv4_method;
+       struct connman_ipaddress ipv4_address;
+
+       enum connman_ipconfig_method ipv6_method;
+       struct connman_ipaddress ipv6_address;
 };
 
 static int modem_probe(struct connman_device *device)
@@ -270,6 +293,16 @@ static void set_online_reply(DBusPendingCall *call, void *user_data)
 
 static int modem_change_online(char const *path, dbus_bool_t online)
 {
+       struct modem_data *modem = g_hash_table_lookup(modem_hash, path);
+
+       if (modem == NULL)
+               return -ENODEV;
+
+       if (modem->online == online)
+               return -EALREADY;
+
+       modem->requested_online = online;
+
        return set_property(path, OFONO_MODEM_INTERFACE, "Online",
                                DBUS_TYPE_BOOLEAN, &online,
                                set_online_reply,
@@ -324,13 +357,18 @@ static void remove_modem(gpointer data)
        modem_remove_device(modem);
 
        g_free(modem->path);
+       g_free(modem->operator);
 
        g_free(modem);
 }
 
 static void remove_network(gpointer data)
 {
-       connman_network_unref(data);
+       struct network_info *info = data;
+
+       connman_network_unref(info->network);
+
+       g_free(info);
 }
 
 static char *get_ident(const char *path)
@@ -377,20 +415,70 @@ static gboolean pending_network_is_available(struct connman_network *network)
        return TRUE;
 }
 
+static void set_connected(struct network_info *info,
+                               connman_bool_t connected)
+{
+       DBG("network %p connected %d", info->network, connected);
+
+       switch (info->ipv4_method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       case CONNMAN_IPCONFIG_METHOD_OFF:
+       case CONNMAN_IPCONFIG_METHOD_MANUAL:
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
+               return;
+
+       case CONNMAN_IPCONFIG_METHOD_FIXED:
+               connman_network_set_ipv4_method(info->network,
+                                                       info->ipv4_method);
+               connman_network_set_ipaddress(info->network,
+                                                       &info->ipv4_address);
+
+               break;
+
+       case CONNMAN_IPCONFIG_METHOD_DHCP:
+               connman_network_set_ipv4_method(info->network,
+                                                       info->ipv4_method);
+
+               break;
+       }
+
+       switch (info->ipv6_method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       case CONNMAN_IPCONFIG_METHOD_OFF:
+       case CONNMAN_IPCONFIG_METHOD_MANUAL:
+       case CONNMAN_IPCONFIG_METHOD_DHCP:
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
+               return;
+
+       case CONNMAN_IPCONFIG_METHOD_FIXED:
+               connman_network_set_ipv6_method(info->network,
+                                                       info->ipv6_method);
+               connman_network_set_ipaddress(info->network,
+                                                       &info->ipv6_address);
+
+               break;
+       }
+
+       connman_network_set_connected(info->network, connected);
+}
+
 static void set_active_reply(DBusPendingCall *call, void *user_data)
 {
        char const *path = user_data;
        DBusMessage *reply;
        DBusError error;
-       struct connman_network *network;
-
-       network = g_hash_table_lookup(network_hash, path);
+       struct network_info *info;
 
-       DBG("path %s network %p", path, network);
+       info = g_hash_table_lookup(network_hash, path);
 
        reply = dbus_pending_call_steal_reply(call);
 
-       if (!pending_network_is_available(network))
+       if (info == NULL)
+               goto done;
+
+       DBG("path %s network %p", path, info->network);
+
+       if (!pending_network_is_available(info->network))
                goto done;
 
        dbus_error_init(&error);
@@ -399,12 +487,13 @@ static void set_active_reply(DBusPendingCall *call, void *user_data)
                connman_error("SetProperty(Active) %s %s",
                                error.name, error.message);
 
-               if (connman_network_get_index(network) < 0)
-                       connman_network_set_error(network,
+               if (connman_network_get_index(info->network) < 0)
+                       connman_network_set_error(info->network,
                                CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
 
                dbus_error_free(&error);
-       }
+       } else if (connman_network_get_index(info->network) >= 0)
+               set_connected(info, TRUE);
 
 done:
        dbus_message_unref(reply);
@@ -412,52 +501,61 @@ done:
        dbus_pending_call_unref(call);
 }
 
-static int set_network_active(struct connman_network *network,
-                                               dbus_bool_t active)
+static int set_network_active(struct connman_network *network)
 {
-       int error;
-
+       dbus_bool_t value = TRUE;
        const char *path = connman_network_get_string(network, "Path");
 
-       DBG("network %p, path %s, active %d", network, path, active);
-
-       error = set_property(path, OFONO_CONTEXT_INTERFACE,
-                               "Active", DBUS_TYPE_BOOLEAN, &active,
-                               set_active_reply, g_strdup(path), NULL);
-
-       if (active == FALSE && error == -EINPROGRESS)
-               error = 0;
+       DBG("network %p, path %s", network, path);
 
-       return error;
+       return set_property(path, OFONO_CONTEXT_INTERFACE,
+                               "Active", DBUS_TYPE_BOOLEAN, &value,
+                               set_active_reply, g_strdup(path), g_free);
 }
 
-static void set_apn(struct connman_network *network)
+static int set_network_inactive(struct connman_network *network)
 {
-       const char *apn, *path;
+       int err;
+       dbus_bool_t value = FALSE;
+       const char *path = connman_network_get_string(network, "Path");
 
-       apn = connman_network_get_string(network, "Cellular.APN");
-       if (apn == NULL)
-               return;
+       DBG("network %p, path %s", network, path);
 
-       path = connman_network_get_string(network, "Path");
-       if (path == NULL)
-               return;
+       err = set_property(path, OFONO_CONTEXT_INTERFACE,
+                               "Active", DBUS_TYPE_BOOLEAN, &value,
+                               NULL, NULL, NULL);
 
-       DBG("path %s, apn %s", path, apn);
+       if (err == -EINPROGRESS)
+               err = 0;
 
-       set_property(path, OFONO_CONTEXT_INTERFACE,
-                       "AccessPointName", DBUS_TYPE_STRING, &apn,
-                       NULL, NULL, NULL);
+       return err;
 }
 
 static int network_connect(struct connman_network *network)
 {
+       struct connman_device *device;
+       struct modem_data *modem;
+
        DBG("network %p", network);
 
-       if (connman_network_get_index(network) >= 0)
-               return -EISCONN;
+       device = connman_network_get_device(network);
+       if (device == NULL)
+               return -ENODEV;
+
+       modem = connman_device_get_data(device);
+       if (modem == NULL)
+               return -ENODEV;
 
-       return set_network_active(network, TRUE);
+       if (modem->registered == FALSE)
+               return -ENOLINK;
+
+       if (modem->powered == FALSE)
+               return -ENOLINK;
+
+       if (modem->roaming_allowed == FALSE && modem->roaming == TRUE)
+               return -ENOLINK;
+
+       return set_network_active(network);
 }
 
 static int network_disconnect(struct connman_network *network)
@@ -467,7 +565,9 @@ static int network_disconnect(struct connman_network *network)
        if (connman_network_get_index(network) < 0)
                return -ENOTCONN;
 
-       return set_network_active(network, FALSE);
+       connman_network_set_associating(network, FALSE);
+
+       return set_network_inactive(network);
 }
 
 static void network_remove(struct connman_network *network)
@@ -479,14 +579,6 @@ static void network_remove(struct connman_network *network)
        g_hash_table_remove(network_hash, path);
 }
 
-static int network_setup(struct connman_network *network, const char *key)
-{
-       if (g_strcmp0(key, "Cellular.APN") == 0)
-               set_apn(network);
-
-       return 0;
-}
-
 static struct connman_network_driver network_driver = {
        .name           = "network",
        .type           = CONNMAN_NETWORK_TYPE_CELLULAR,
@@ -494,24 +586,228 @@ static struct connman_network_driver network_driver = {
        .remove         = network_remove,
        .connect        = network_connect,
        .disconnect     = network_disconnect,
-       .setup          = network_setup,
 };
 
-static void update_settings(DBusMessageIter *array,
-                               struct connman_network *network);
+static void get_dns(DBusMessageIter *array, struct network_info *info)
+{
+       DBusMessageIter entry;
+       gchar *nameservers = NULL, *nameservers_old = NULL;
+
+       DBG("");
+
+
+       dbus_message_iter_recurse(array, &entry);
+
+       while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
+               const char *dns;
+
+               dbus_message_iter_get_basic(&entry, &dns);
+
+               DBG("dns %s", dns);
+
+               if (nameservers == NULL) {
+
+                       nameservers = g_strdup(dns);
+               } else {
+
+                       nameservers_old = nameservers;
+                       nameservers = g_strdup_printf("%s %s",
+                                               nameservers_old, dns);
+                       g_free(nameservers_old);
+               }
+
+               dbus_message_iter_next(&entry);
+       }
+
+       connman_network_set_nameservers(info->network, nameservers);
+
+       g_free(nameservers);
+}
+
+static void update_ipv4_settings(DBusMessageIter *array,
+                               struct network_info *info)
+{
+       DBusMessageIter dict;
+       char *address = NULL, *netmask = NULL, *gateway = NULL;
+       const char *interface = NULL;
+
+       DBG("network %p", info->network);
+
+       if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
+               return;
+
+       dbus_message_iter_recurse(array, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key, *val;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               DBG("key %s", key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               if (g_str_equal(key, "Interface") == TRUE) {
+                       int index;
+
+                       dbus_message_iter_get_basic(&value, &interface);
+
+                       DBG("interface %s", interface);
+
+                       index = connman_inet_ifindex(interface);
+                       if (index >= 0) {
+                               connman_network_set_index(info->network, index);
+                       } else {
+                               connman_error("Can not find interface %s",
+                                                               interface);
+                               break;
+                       }
+               } else if (g_str_equal(key, "Method") == TRUE) {
+                       const char *method;
+
+                       dbus_message_iter_get_basic(&value, &method);
+
+                       if (g_strcmp0(method, "static") == 0) {
+
+                               info->ipv4_method =
+                                       CONNMAN_IPCONFIG_METHOD_FIXED;
+                       } else if (g_strcmp0(method, "dhcp") == 0) {
+
+                               info->ipv4_method =
+                                       CONNMAN_IPCONFIG_METHOD_DHCP;
+                               break;
+                       }
+               } else if (g_str_equal(key, "Address") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &val);
+
+                       address = g_strdup(val);
+
+                       DBG("address %s", address);
+               } else if (g_str_equal(key, "Netmask") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &val);
+
+                       netmask = g_strdup(val);
+
+                       DBG("netmask %s", netmask);
+               } else if (g_str_equal(key, "DomainNameServers") == TRUE) {
+
+                       get_dns(&value, info);
+               } else if (g_str_equal(key, "Gateway") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &val);
+
+                       gateway = g_strdup(val);
+
+                       DBG("gateway %s", gateway);
+               }
+
+               dbus_message_iter_next(&dict);
+       }
+
+
+       if (info->ipv4_method == CONNMAN_IPCONFIG_METHOD_FIXED) {
+               connman_ipaddress_set_ipv4(&info->ipv4_address, address,
+                                               netmask, gateway);
+       }
+
+       /* deactive, oFono send NULL inteface before deactive signal */
+       if (interface == NULL)
+               connman_network_set_index(info->network, -1);
+
+       g_free(address);
+       g_free(netmask);
+       g_free(gateway);
+}
+
+static void update_ipv6_settings(DBusMessageIter *array,
+                               struct network_info *info)
+{
+       DBusMessageIter dict;
+       char *address = NULL, *gateway = NULL;
+       unsigned char prefix_length;
+       const char *interface = NULL;
+
+       DBG("network %p", info->network);
 
-static void set_connected(struct connman_network *network,
-                               connman_bool_t connected);
+       if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
+               return;
+
+       dbus_message_iter_recurse(array, &dict);
+
+       info->ipv6_method = CONNMAN_IPCONFIG_METHOD_FIXED;
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key, *val;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               DBG("key %s", key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               if (g_str_equal(key, "Interface") == TRUE) {
+                       int index;
+
+                       dbus_message_iter_get_basic(&value, &interface);
+
+                       DBG("interface %s", interface);
+
+                       index = connman_inet_ifindex(interface);
+                       if (index >= 0) {
+                               connman_network_set_index(info->network, index);
+                       } else {
+                               connman_error("Can not find interface %s",
+                                                               interface);
+                               break;
+                       }
+               } else if (g_str_equal(key, "Address") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &val);
+
+                       address = g_strdup(val);
+
+                       DBG("address %s", address);
+               } else if (g_str_equal(key, "PrefixLength") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &prefix_length);
+
+                       DBG("prefix length %d", prefix_length);
+               } else if (g_str_equal(key, "DomainNameServers") == TRUE) {
+
+                       get_dns(&value, info);
+               } else if (g_str_equal(key, "Gateway") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &val);
+
+                       gateway = g_strdup(val);
+
+                       DBG("gateway %s", gateway);
+               }
+
+               dbus_message_iter_next(&dict);
+       }
+
+       connman_ipaddress_set_ipv6(&info->ipv6_address, address,
+                                               prefix_length, gateway);
+
+       /* deactive, oFono send NULL inteface before deactive signal */
+       if (interface == NULL)
+               connman_network_set_index(info->network, -1);
+
+       g_free(address);
+       g_free(gateway);
+}
 
 static int add_network(struct connman_device *device,
                        const char *path, DBusMessageIter *dict)
 {
        struct modem_data *modem = connman_device_get_data(device);
        struct connman_network *network;
+       struct network_info *info;
        char *ident;
        const char *hash_path;
-       char const *operator;
-       char const *reg_status;
        dbus_bool_t active = FALSE;
 
        DBG("modem %p device %p path %s", modem, device, path);
@@ -522,11 +818,11 @@ static int add_network(struct connman_device *device,
        if (network != NULL)
                return -EALREADY;
 
-       network = g_hash_table_lookup(network_hash, path);
-       if (network != NULL) {
+       info = g_hash_table_lookup(network_hash, path);
+       if (info != NULL) {
                DBG("path %p already exists with device %p", path,
-                       connman_network_get_device(network));
-               if (connman_network_get_device(network))
+                       connman_network_get_device(info->network));
+               if (connman_network_get_device(info->network))
                        return -EALREADY;
                g_hash_table_remove(network_hash, path);
        }
@@ -535,32 +831,39 @@ static int add_network(struct connman_device *device,
        if (network == NULL)
                return -ENOMEM;
 
+       info = g_try_new0(struct network_info, 1);
+       if (info == NULL) {
+               connman_network_unref(network);
+               return -ENOMEM;
+       }
+
+       connman_ipaddress_clear(&info->ipv4_address);
+       connman_ipaddress_clear(&info->ipv6_address);
+       info->network = network;
+
        connman_network_set_string(network, "Path", path);
        hash_path = connman_network_get_string(network, "Path");
-       if (hash_path == NULL)
-               goto error;
+       if (hash_path == NULL) {
+               connman_network_unref(network);
+               g_free(info);
+               return -EIO;
+       }
 
        create_service(network);
 
        connman_network_ref(network);
-       g_hash_table_insert(network_hash, (char *)hash_path, network);
+       g_hash_table_insert(network_hash, (char *) hash_path, info);
 
        connman_network_set_available(network, TRUE);
        connman_network_set_index(network, -1);
 
-       operator = connman_device_get_string(device, "Operator");
-       if (operator)
-               connman_network_set_name(network, operator);
+       if (modem->operator)
+               connman_network_set_name(network, modem->operator);
 
        if (modem->has_strength)
                connman_network_set_strength(network, modem->strength);
 
-       reg_status = connman_device_get_string(device, "RegistrationStatus");
-
-       if (!g_strcmp0(reg_status, "roaming"))
-               connman_network_set_roaming(network, TRUE);
-       else if (!g_strcmp0(reg_status, "registered"))
-               connman_network_set_roaming(network, FALSE);
+       connman_network_set_roaming(network, modem->roaming);
 
        while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
                DBusMessageIter entry, value;
@@ -580,14 +883,10 @@ static int add_network(struct connman_device *device,
                                DBG("path %p type %s", path, type);
                                goto error;
                        }
-               } else if (g_str_equal(key, "AccessPointName")) {
-                       const char *ap;
-
-                       dbus_message_iter_get_basic(&value, &ap);
-
-                       connman_network_set_string(network, "Cellular.APN", ap);
                } else if (g_str_equal(key, "Settings"))
-                       update_settings(&value, network);
+                       update_ipv4_settings(&value, info);
+               else if (g_str_equal(key, "IPv6.Settings"))
+                       update_ipv6_settings(&value, info);
                else if (g_str_equal(key, "Active") == TRUE)
                        dbus_message_iter_get_basic(&value, &active);
 
@@ -597,8 +896,9 @@ static int add_network(struct connman_device *device,
        if (connman_device_add_network(device, network) != 0)
                goto error;
 
-       if (active)
-               set_connected(network, active);
+       /* Connect only if requested to do so */
+       if (active && connman_network_get_connecting(network) == TRUE)
+               set_connected(info, active);
 
        return 0;
 
@@ -663,6 +963,25 @@ static void check_networks(struct modem_data *modem)
                        DBUS_TYPE_INVALID);
 }
 
+static void modem_clear_network_errors(struct modem_data *modem)
+{
+       struct connman_device *device = modem->device;
+       GHashTableIter i;
+       gpointer value;
+
+       if (device == NULL)
+               return;
+
+       g_hash_table_iter_init(&i, network_hash);
+
+       while (g_hash_table_iter_next(&i, NULL, &value)) {
+               struct network_info *info = value;
+
+               if (connman_network_get_device(info->network) == device)
+                       connman_network_clear_error(info->network);
+       }
+}
+
 static void modem_operator_name_changed(struct modem_data *modem,
                                        char const *name)
 {
@@ -673,15 +992,17 @@ static void modem_operator_name_changed(struct modem_data *modem,
        if (device == NULL)
                return;
 
-       connman_device_set_string(device, "Operator", name);
+       if (modem->operator != NULL)
+               g_free(modem->operator);
+       modem->operator = g_strdup(name);
 
        for (g_hash_table_iter_init(&i, network_hash);
             g_hash_table_iter_next(&i, NULL, &value);) {
-               struct connman_network *network = value;
+               struct network_info *info = value;
 
-               if (connman_network_get_device(network) == device) {
-                       connman_network_set_name(network, name);
-                       connman_network_update(network);
+               if (connman_network_get_device(info->network) == device) {
+                       connman_network_set_name(info->network, name);
+                       connman_network_update(info->network);
                }
        }
 }
@@ -700,11 +1021,11 @@ static void modem_strength_changed(struct modem_data *modem, uint8_t strength)
 
        for (g_hash_table_iter_init(&i, network_hash);
             g_hash_table_iter_next(&i, NULL, &value);) {
-               struct connman_network *network = value;
+               struct network_info *info = value;
 
-               if (connman_network_get_device(network) == device) {
-                       connman_network_set_strength(network, strength);
-                       connman_network_update(network);
+               if (connman_network_get_device(info->network) == device) {
+                       connman_network_set_strength(info->network, strength);
+                       connman_network_update(info->network);
                }
        }
 }
@@ -713,33 +1034,49 @@ static void modem_roaming_changed(struct modem_data *modem,
                                        char const *status)
 {
        struct connman_device *device = modem->device;
-       connman_bool_t roaming;
+       connman_bool_t roaming = FALSE;
+       connman_bool_t registered = FALSE;
+       connman_bool_t was_roaming = modem->roaming;
        GHashTableIter i;
        gpointer value;
 
-       if (device == NULL)
-               return;
-
-       connman_device_set_string(device, "RegistrationStatus", status);
-
        if (g_str_equal(status, "roaming"))
                roaming = TRUE;
        else if (g_str_equal(status, "registered"))
-               roaming = FALSE;
-       else
+               registered = TRUE;
+
+       registered = registered || roaming;
+
+       if (modem->roaming == roaming && modem->registered == registered)
                return;
 
-       for (g_hash_table_iter_init(&i, network_hash);
-            g_hash_table_iter_next(&i, NULL, &value);) {
-               struct connman_network *network = value;
+       modem->registered = registered;
+       modem->roaming = roaming;
+
+       if (roaming == was_roaming)
+               return;
 
-               if (connman_network_get_device(network) == device) {
-                       connman_network_set_roaming(network, roaming);
-                       connman_network_update(network);
+       if (device == NULL)
+               return;
+
+       g_hash_table_iter_init(&i, network_hash);
+
+       while (g_hash_table_iter_next(&i, NULL, &value)) {
+               struct network_info *info = value;
+
+               if (connman_network_get_device(info->network) == device) {
+                       connman_network_set_roaming(info->network, roaming);
+                       connman_network_update(info->network);
                }
        }
 }
 
+static void modem_registration_removed(struct modem_data *modem)
+{
+       modem->registered = FALSE;
+       modem->roaming = FALSE;
+}
+
 static void modem_registration_changed(struct modem_data *modem,
                                        DBusMessageIter *entry)
 {
@@ -747,7 +1084,7 @@ static void modem_registration_changed(struct modem_data *modem,
        const char *key;
        int type;
        connman_uint8_t strength;
-       char const *name, *status;
+       char const *name, *status, *mcc_s;
 
        dbus_message_iter_get_basic(entry, &key);
 
@@ -770,6 +1107,19 @@ static void modem_registration_changed(struct modem_data *modem,
        } else if (g_str_equal(key, "Status") && type == DBUS_TYPE_STRING) {
                dbus_message_iter_get_basic(&iter, &status);
                modem_roaming_changed(modem, status);
+       } else if (g_str_equal(key, "MobileCountryCode") &&
+                                       type == DBUS_TYPE_STRING) {
+               int mcc;
+               char *alpha2;
+
+               dbus_message_iter_get_basic(&iter, &mcc_s);
+
+               mcc = atoi(mcc_s);
+               if (mcc > 799)
+                       return;
+
+               alpha2 = mcc_country_codes[mcc - 200];
+               connman_technology_set_regdom(alpha2);
        }
 
 }
@@ -838,6 +1188,94 @@ static void check_registration(struct modem_data *modem)
                        DBUS_TYPE_INVALID);
 }
 
+static void modem_gprs_changed(struct modem_data *modem,
+                                       DBusMessageIter *entry)
+{
+       DBusMessageIter iter;
+       const char *key;
+       int type;
+       dbus_bool_t value;
+
+       dbus_message_iter_get_basic(entry, &key);
+
+       DBG("key %s", key);
+
+       dbus_message_iter_next(entry);
+
+       dbus_message_iter_recurse(entry, &iter);
+
+       type = dbus_message_iter_get_arg_type(&iter);
+
+       if (type != DBUS_TYPE_BOOLEAN)
+               return;
+
+       dbus_message_iter_get_basic(&iter, &value);
+
+       if (g_str_equal(key, "Attached") == TRUE) {
+               DBG("Attached %d", value);
+
+               modem->attached = value;
+
+               if (value)
+                       modem_clear_network_errors(modem);
+       } else if (g_str_equal(key, "Powered") == TRUE) {
+               DBG("Powered %d", value);
+
+               modem->powered = value;
+       } else if (g_str_equal(key, "RoamingAllowed") == TRUE) {
+               DBG("RoamingAllowed %d", value);
+
+               modem->roaming_allowed = value;
+       }
+}
+
+static void check_gprs_reply(DBusPendingCall *call, void *user_data)
+{
+       char const *path = user_data;
+       struct modem_data *modem;
+       DBusMessage *reply;
+       DBusMessageIter array, dict, entry;
+
+       DBG("path %s", path);
+
+       modem = g_hash_table_lookup(modem_hash, path);
+       if (modem == NULL)
+               return;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       if (dbus_message_iter_init(reply, &array) == FALSE)
+               goto done;
+
+       if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
+               goto done;
+
+       dbus_message_iter_recurse(&array, &dict);
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               dbus_message_iter_recurse(&dict, &entry);
+               modem_gprs_changed(modem, &entry);
+               dbus_message_iter_next(&dict);
+       }
+
+done:
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+
+       check_networks(modem);
+}
+
+static void check_gprs(struct modem_data *modem)
+{
+       char const *path = modem->path;
+
+       DBG("modem %p path %s", modem, path);
+
+       call_ofono(path, OFONO_GPRS_INTERFACE, GET_PROPERTIES,
+                       check_gprs_reply, g_strdup(path), g_free,
+                       DBUS_TYPE_INVALID);
+}
+
 static void add_device(const char *path, const char *imsi)
 {
        struct modem_data *modem;
@@ -884,8 +1322,9 @@ static void add_device(const char *path, const char *imsi)
 
        if (modem->has_reg)
                check_registration(modem);
+
        if (modem->has_gprs)
-               check_networks(modem);
+               check_gprs(modem);
 }
 
 static void sim_properties_reply(DBusPendingCall *call, void *user_data)
@@ -1000,7 +1439,6 @@ static void add_modem(const char *path, DBusMessageIter *prop)
        struct modem_data *modem;
        dbus_bool_t powered = FALSE;
        dbus_bool_t online = FALSE;
-       dbus_bool_t has_online = FALSE;
        dbus_bool_t locked = FALSE;
        gboolean has_sim = FALSE;
        gboolean has_reg = FALSE;
@@ -1035,10 +1473,7 @@ static void add_modem(const char *path, DBusMessageIter *prop)
                        dbus_message_iter_get_basic(&value, &powered);
                else if (g_str_equal(key, "Lockdown") == TRUE)
                        dbus_message_iter_get_basic(&value, &locked);
-               else if (g_str_equal(key, "Online") == TRUE) {
-                       has_online = TRUE;
-                       dbus_message_iter_get_basic(&value, &online);
-               } else if (g_str_equal(key, "Interfaces") == TRUE) {
+               else if (g_str_equal(key, "Interfaces") == TRUE) {
                        has_sim = modem_has_sim(&value);
                        has_reg = modem_has_reg(&value);
                        has_gprs = modem_has_gprs(&value);
@@ -1190,9 +1625,9 @@ static gboolean modem_changed(DBusConnection *connection, DBusMessage *message,
        } else if (g_str_equal(key, "Interfaces") == TRUE) {
                gboolean has_sim = modem_has_sim(&value);
                gboolean has_reg = modem_has_reg(&value);
-               gboolean added_reg = has_reg && !modem->has_reg;
+               gboolean had_reg = modem->has_reg;
                gboolean has_gprs = modem_has_gprs(&value);
-               gboolean added_gprs = has_gprs && !modem->has_gprs;
+               gboolean had_gprs = modem->has_gprs;
 
                modem->has_sim = has_sim;
                modem->has_reg = has_reg;
@@ -1204,10 +1639,15 @@ static gboolean modem_changed(DBusConnection *connection, DBusMessage *message,
                } else if (!has_sim) {
                        modem_remove_device(modem);
                } else {
-                       if (added_reg)
+                       if (has_reg && !had_reg)
                                check_registration(modem);
-                       if (added_gprs)
+                       else if (had_reg && !has_reg)
+                               modem_registration_removed(modem);
+
+                       if (has_gprs && !had_gprs) {
                                gprs_change_powered(modem->path, TRUE);
+                               check_gprs(modem);
+                       }
                }
        }
 
@@ -1265,8 +1705,7 @@ static gboolean gprs_changed(DBusConnection *connection, DBusMessage *message,
 {
        const char *path = dbus_message_get_path(message);
        struct modem_data *modem;
-       DBusMessageIter iter, value;
-       const char *key;
+       DBusMessageIter iter;
 
        DBG("path %s", path);
 
@@ -1274,27 +1713,65 @@ static gboolean gprs_changed(DBusConnection *connection, DBusMessage *message,
        if (modem == NULL)
                return TRUE;
 
+       if (dbus_message_iter_init(message, &iter))
+               modem_gprs_changed(modem, &iter);
+
+       return TRUE;
+}
+
+static gboolean context_added(DBusConnection *connection,
+                               DBusMessage *message, void *user_data)
+{
+       const char *path = dbus_message_get_path(message);
+       const char *network_path;
+       struct modem_data *modem;
+       DBusMessageIter iter, properties;
+
+       DBG("path %s", path);
+
+       modem = g_hash_table_lookup(modem_hash, path);
+       if (modem == NULL || modem->device == NULL)
+               return TRUE;
+
        if (dbus_message_iter_init(message, &iter) == FALSE)
                return TRUE;
 
-       dbus_message_iter_get_basic(&iter, &key);
+       dbus_message_iter_get_basic(&iter, &network_path);
 
        dbus_message_iter_next(&iter);
-       dbus_message_iter_recurse(&iter, &value);
+       dbus_message_iter_recurse(&iter, &properties);
 
-       if (g_str_equal(key, "Attached") == TRUE) {
-               dbus_bool_t attached;
+       add_network(modem->device, network_path, &properties);
+
+       return TRUE;
+}
 
-               dbus_message_iter_get_basic(&value, &attached);
+static gboolean context_removed(DBusConnection *connection,
+                               DBusMessage *message, void *user_data)
+{
+       const char *path = dbus_message_get_path(message);
+       const char *network_path, *identifier;
+       struct modem_data *modem;
+       struct network_info *info;
+       DBusMessageIter iter;
 
-               DBG("Attached %d", attached);
+       DBG("path %s", path);
 
-               if (attached == TRUE)
-                       check_networks(modem);
-               else if (modem->device != NULL)
-                       connman_device_remove_all_networks(modem->device);
+       modem = g_hash_table_lookup(modem_hash, path);
+       if (modem == NULL || modem->device == NULL)
+               return TRUE;
 
-       }
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &network_path);
+
+       info = g_hash_table_lookup(network_hash, network_path);
+       if (info == NULL)
+               return TRUE;
+
+       identifier = connman_network_get_identifier(info->network);
+       connman_device_remove_network(modem->device, identifier);
 
        return TRUE;
 }
@@ -1338,197 +1815,22 @@ static gboolean modem_removed(DBusConnection *connection,
        return TRUE;
 }
 
-
-static void get_dns(DBusMessageIter *array, struct connman_element *parent)
-{
-       DBusMessageIter entry;
-       gchar *nameserver = NULL, *nameserver_old = NULL;
-
-       DBG("");
-
-       dbus_message_iter_recurse(array, &entry);
-
-       while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
-               const char *dns;
-
-               dbus_message_iter_get_basic(&entry, &dns);
-
-               DBG("dns %s", dns);
-
-               if (nameserver == NULL) {
-
-                       nameserver = g_strdup(dns);
-               } else {
-
-                       nameserver_old = nameserver;
-                       nameserver = g_strdup_printf("%s %s",
-                                               nameserver_old, dns);
-                       g_free(nameserver_old);
-               }
-
-               dbus_message_iter_next(&entry);
-       }
-
-       parent->ipv4.nameserver = nameserver;
-}
-
-static void update_settings(DBusMessageIter *array,
-                               struct connman_network *network)
-{
-       struct connman_element *parent = connman_network_get_element(network);
-       DBusMessageIter dict;
-       const char *interface = NULL;
-
-       DBG("network %p", network);
-
-       if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
-               return;
-
-       dbus_message_iter_recurse(array, &dict);
-
-       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
-               DBusMessageIter entry, value;
-               const char *key;
-
-               dbus_message_iter_recurse(&dict, &entry);
-               dbus_message_iter_get_basic(&entry, &key);
-
-               DBG("key %s", key);
-
-               dbus_message_iter_next(&entry);
-               dbus_message_iter_recurse(&entry, &value);
-
-               if (g_str_equal(key, "Interface") == TRUE) {
-                       int index;
-
-                       dbus_message_iter_get_basic(&value, &interface);
-
-                       DBG("interface %s", interface);
-
-                       index = connman_inet_ifindex(interface);
-                       if (index >= 0) {
-                               connman_network_set_index(network, index);
-                       } else {
-                               connman_error("Can not find interface %s",
-                                                               interface);
-                               break;
-                       }
-               } else if (g_str_equal(key, "Method") == TRUE) {
-                       const char *method;
-
-                       dbus_message_iter_get_basic(&value, &method);
-                       if (g_strcmp0(method, "static") == 0) {
-
-                               parent->ipv4.method =
-                                       CONNMAN_IPCONFIG_METHOD_FIXED;
-                       } else if (g_strcmp0(method, "dhcp") == 0) {
-
-                               parent->ipv4.method =
-                                       CONNMAN_IPCONFIG_METHOD_DHCP;
-                               break;
-                       }
-               } else if (g_str_equal(key, "Address") == TRUE) {
-                       const char *address;
-
-                       dbus_message_iter_get_basic(&value, &address);
-
-                       DBG("address %s", address);
-
-                       parent->ipv4.address = g_strdup(address);
-               } else if (g_str_equal(key, "Netmask") == TRUE) {
-                       const char *netmask;
-
-                       dbus_message_iter_get_basic(&value, &netmask);
-
-                       DBG("netmask %s", netmask);
-
-                       parent->ipv4.netmask = g_strdup(netmask);
-               } else if (g_str_equal(key, "DomainNameServers") == TRUE) {
-
-                       get_dns(&value, parent);
-               } else if (g_str_equal(key, "Gateway") == TRUE) {
-                       const char *gateway;
-
-                       dbus_message_iter_get_basic(&value, &gateway);
-
-                       DBG("gateway %s", gateway);
-
-                       parent->ipv4.gateway = g_strdup(gateway);
-               }
-
-               dbus_message_iter_next(&dict);
-       }
-
-       /* deactive, oFono send NULL inteface before deactive signal */
-       if (interface == NULL)
-               connman_network_set_index(network, -1);
-}
-
-static void cleanup_ipconfig(struct connman_network *network)
-{
-       struct connman_element *parent = connman_network_get_element(network);
-
-       g_free(parent->ipv4.address);
-       parent->ipv4.address = NULL;
-
-       g_free(parent->ipv4.netmask);
-       parent->ipv4.netmask = NULL;
-
-       g_free(parent->ipv4.nameserver);
-       parent->ipv4.nameserver = NULL;
-
-       g_free(parent->ipv4.gateway);
-       parent->ipv4.gateway = NULL;
-
-       parent->ipv4.method = CONNMAN_IPCONFIG_METHOD_UNKNOWN;
-}
-
-
-static void set_connected(struct connman_network *network,
-                               connman_bool_t connected)
-{
-       struct connman_element *parent = connman_network_get_element(network);
-       enum connman_ipconfig_method method = parent->ipv4.method;
-
-       switch (method) {
-       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
-       case CONNMAN_IPCONFIG_METHOD_OFF:
-       case CONNMAN_IPCONFIG_METHOD_MANUAL:
-               return;
-
-       case CONNMAN_IPCONFIG_METHOD_FIXED:
-               connman_network_set_ipv4_method(network, method);
-
-               if (connected == FALSE)
-                       cleanup_ipconfig(network);
-
-               connman_network_set_connected(network, connected);
-               break;
-
-       case CONNMAN_IPCONFIG_METHOD_DHCP:
-               connman_network_set_ipv4_method(network, method);
-
-               connman_network_set_connected(network, connected);
-               break;
-       }
-}
-
 static gboolean context_changed(DBusConnection *connection,
                                        DBusMessage *message, void *user_data)
 {
        const char *path = dbus_message_get_path(message);
-       struct connman_network *network;
+       struct network_info *info;
        DBusMessageIter iter, value;
        const char *key;
 
        DBG("path %s", path);
 
-       network = g_hash_table_lookup(network_hash, path);
-       if (network == NULL)
+       info = g_hash_table_lookup(network_hash, path);
+       if (info == NULL)
                return TRUE;
 
-       if (!pending_network_is_available(network)) {
-               remove_network(network);
+       if (!pending_network_is_available(info->network)) {
+               g_hash_table_remove(network_hash, path);
                return TRUE;
        }
 
@@ -1540,14 +1842,24 @@ static gboolean context_changed(DBusConnection *connection,
        dbus_message_iter_next(&iter);
        dbus_message_iter_recurse(&iter, &value);
 
+       DBG("key %s", key);
+
        if (g_str_equal(key, "Settings") == TRUE)
-               update_settings(&value, network);
+               update_ipv4_settings(&value, info);
+       else if (g_str_equal(key, "IPv6.Settings"))
+                       update_ipv6_settings(&value, info);
        else if (g_str_equal(key, "Active") == TRUE) {
                dbus_bool_t active;
 
                dbus_message_iter_get_basic(&value, &active);
 
-               set_connected(network, active);
+               if (active == FALSE)
+                       set_connected(info, active);
+
+               /* Connect only if requested to do so */
+               if (active == TRUE &&
+                       connman_network_get_connecting(info->network) == TRUE)
+                       set_connected(info, active);
        }
 
        return TRUE;
@@ -1557,6 +1869,8 @@ static guint watch;
 static guint reg_watch;
 static guint sim_watch;
 static guint gprs_watch;
+static guint context_added_watch;
+static guint context_removed_watch;
 static guint modem_watch;
 static guint modem_added_watch;
 static guint modem_removed_watch;
@@ -1585,6 +1899,18 @@ static int ofono_init(void)
                                                gprs_changed,
                                                NULL, NULL);
 
+       context_added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               OFONO_GPRS_INTERFACE,
+                                               CONTEXT_ADDED,
+                                               context_added,
+                                               NULL, NULL);
+
+       context_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               OFONO_GPRS_INTERFACE,
+                                               CONTEXT_REMOVED,
+                                               context_removed,
+                                               NULL, NULL);
+
        modem_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
                                                OFONO_MODEM_INTERFACE,
                                                PROPERTY_CHANGED,
@@ -1615,7 +1941,8 @@ static int ofono_init(void)
                                                context_changed,
                                                NULL, NULL);
 
-       if (watch == 0 || gprs_watch == 0 || modem_watch == 0 ||
+       if (watch == 0 || gprs_watch == 0 || context_added_watch == 0 ||
+                       context_removed_watch == 0 || modem_watch == 0 ||
                        reg_watch == 0 || sim_watch == 0 ||
                        modem_added_watch == 0 || modem_removed_watch == 0 ||
                                context_watch == 0) {
@@ -1640,6 +1967,8 @@ remove:
        g_dbus_remove_watch(connection, sim_watch);
        g_dbus_remove_watch(connection, reg_watch);
        g_dbus_remove_watch(connection, gprs_watch);
+       g_dbus_remove_watch(connection, context_added_watch);
+       g_dbus_remove_watch(connection, context_removed_watch);
        g_dbus_remove_watch(connection, modem_watch);
        g_dbus_remove_watch(connection, modem_added_watch);
        g_dbus_remove_watch(connection, modem_removed_watch);
@@ -1656,6 +1985,8 @@ static void ofono_exit(void)
        g_dbus_remove_watch(connection, sim_watch);
        g_dbus_remove_watch(connection, reg_watch);
        g_dbus_remove_watch(connection, gprs_watch);
+       g_dbus_remove_watch(connection, context_added_watch);
+       g_dbus_remove_watch(connection, context_removed_watch);
        g_dbus_remove_watch(connection, modem_watch);
        g_dbus_remove_watch(connection, modem_added_watch);
        g_dbus_remove_watch(connection, modem_removed_watch);