diff --git a/src/core/devices/wwan/nm-modem-ofono.c b/src/core/devices/wwan/nm-modem-ofono.c index d2f3f38b82..c18c2778bc 100644 --- a/src/core/devices/wwan/nm-modem-ofono.c +++ b/src/core/devices/wwan/nm-modem-ofono.c @@ -8,7 +8,10 @@ #include "nm-modem-ofono.h" #include "libnm-core-intern/nm-core-internal.h" +#include "libnm-glib-aux/nm-uuid.h" #include "devices/nm-device-private.h" +#include "nm-setting-gsm.h" +#include "settings/nm-settings.h" #include "nm-modem.h" #include "libnm-platform/nm-platform.h" #include "nm-l3-config-data.h" @@ -26,28 +29,42 @@ /*****************************************************************************/ +typedef struct { + NMModemOfono *self; + char *name; + char *type; + gboolean preferred; + GDBusProxy *proxy; +} OfonoContextData; + typedef struct { GHashTable *connect_properties; + GHashTable *connections; + GHashTable *contexts; GDBusProxy *modem_proxy; GDBusProxy *connman_proxy; - GDBusProxy *context_proxy; GDBusProxy *sim_proxy; GCancellable *modem_proxy_cancellable; GCancellable *connman_proxy_cancellable; - GCancellable *context_proxy_cancellable; + GCancellable *connect_cancellable; GCancellable *sim_proxy_cancellable; GError *property_error; - char *context_path; char *imsi; gboolean modem_online; gboolean gprs_attached; NML3ConfigData *l3cd_4; + NMSettings *settings; + + guint n_context_proxy_pending; + + /* unowned; reference held by 'contexts' above. */ + OfonoContextData *current_octx; } NMModemOfonoPrivate; struct _NMModemOfono { @@ -96,6 +113,21 @@ G_DEFINE_TYPE(NMModemOfono, nm_modem_ofono, NM_TYPE_MODEM) /*****************************************************************************/ +/* + * Deterministic UUID is used to pair imsi+context with exported connection + * (via a pair of hash tables). + */ + +static char * +_generate_uuid(const char *imsi, const char *object_path) +{ + return nm_uuid_generate_from_strings( + NM_UUID_TYPE_VERSION5, + &NM_UUID_INIT(b5, 6b, ad, f5, ef, 9d, 4a, 21, a8, e7, 7d, db, 69, bb, 6b, ee), + imsi, + object_path); +} + static void get_capabilities(NMModem *_self, NMDeviceModemCapabilities *modem_caps, @@ -213,7 +245,8 @@ disconnect(NMModem *modem, ctx->callback = callback; ctx->callback_user_data = user_data; - if (state != NM_MODEM_STATE_CONNECTED || g_cancellable_is_cancelled(cancellable)) { + if (state != NM_MODEM_STATE_CONNECTED || g_cancellable_is_cancelled(cancellable) + || priv->current_octx == NULL) { nm_utils_invoke_on_idle(cancellable, disconnect_context_complete_on_idle, ctx); return; } @@ -222,7 +255,7 @@ disconnect(NMModem *modem, NM_MODEM_STATE_DISCONNECTING, nm_modem_state_to_string(NM_MODEM_STATE_DISCONNECTING)); - g_dbus_proxy_call(priv->context_proxy, + g_dbus_proxy_call(priv->current_octx->proxy, "SetProperty", g_variant_new("(sv)", "Active", g_variant_new("b", warn)), G_DBUS_CALL_FLAGS_NONE, @@ -251,7 +284,7 @@ check_connection_compatible_with_modem(NMModem *modem, NMConnection *connection, { NMModemOfono *self = NM_MODEM_OFONO(modem); NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self); - const char *id; + const char *uuid; if (!_nm_connection_check_main_setting(connection, NM_SETTING_GSM_SETTING_NAME, NULL)) { nm_utils_error_set(error, @@ -268,19 +301,12 @@ check_connection_compatible_with_modem(NMModem *modem, NMConnection *connection, return FALSE; } - id = nm_connection_get_id(connection); + uuid = nm_connection_get_uuid(connection); - if (!strstr(id, "/context")) { + if (!g_hash_table_contains(priv->contexts, uuid)) { nm_utils_error_set_literal(error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, - "the connection ID has no context"); - return FALSE; - } - - if (!strstr(id, priv->imsi)) { - nm_utils_error_set_literal(error, - NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, - "the connection ID does not contain the IMSI"); + "connection ID does not match known contexts"); return FALSE; } @@ -500,8 +526,6 @@ connman_get_properties_done(GObject *source, GAsyncResult *result, gpointer user self = NM_MODEM_OFONO(user_data); priv = NM_MODEM_OFONO_GET_PRIVATE(self); - g_clear_object(&priv->connman_proxy_cancellable); - if (!v_properties) { g_dbus_error_strip_remote_error(error); _LOGW("error getting connman properties: %s", error->message); @@ -525,6 +549,366 @@ connman_get_properties_done(GObject *source, GAsyncResult *result, gpointer user } } +static void +ofono_context_data_free(OfonoContextData *octx) +{ + g_free(octx->name); + g_free(octx->type); + + if (octx->proxy) { + g_signal_handlers_disconnect_by_data(octx->proxy, octx); + g_object_unref(octx->proxy); + } + + g_slice_free(OfonoContextData, octx); +} + +static void +add_or_update_connection(NMModemOfono *self, const char *context_name, const char *uuid) +{ + NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self); + gs_unref_object NMConnection *connection = NULL; + NMSetting *setting; + NMSettingsConnection *sett_conn; + gs_free_error GError *error = NULL; + + /* + * See first if we have an existing connection (from previous or current + * run of NM) that we can update in-place. + */ + + sett_conn = nm_settings_get_connection_by_uuid(priv->settings, uuid); + if (sett_conn + && !NM_FLAGS_HAS(nm_settings_connection_get_flags(sett_conn), + NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)) { + /* + * Either we have a coliding connection, or our connection has been + * modified by user. For the latter case it's safe to leave it alone; + * the UUID will still let it connect. However, for the first case, + * we can't really do anything about it unless we re-write the way we + * track connection <-> context, which I (Ratchanan) don't want to do + * right now... + */ + return; + } + + connection = nm_simple_connection_new(); + setting = nm_setting_connection_new(); + g_object_set(setting, + NM_SETTING_CONNECTION_ID, + context_name, + NM_SETTING_CONNECTION_UUID, + uuid, + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_GSM_SETTING_NAME, + NULL); + nm_connection_add_setting(connection, setting); + + setting = nm_setting_gsm_new(); + + /* + * oFono should already know how to handle placing the call, but NM + * insists on having a number. Pass the usual *99#. + */ + g_object_set(setting, NM_SETTING_GSM_NUMBER, "*99#", NULL); + nm_connection_add_setting(connection, setting); + + if (!nm_connection_normalize(connection, NULL, NULL, &error)) { + nm_log_err(LOGD_MB, + "ofono: could not not generate a connection for %s: %s", + context_name, + error->message); + return; + } + + if (!sett_conn) { + nm_settings_add_connection(priv->settings, + NULL, /* plugin */ + connection, + NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY, + NM_SETTINGS_CONNECTION_ADD_REASON_NONE, + NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED, + &sett_conn, + &error); + } else { + nm_settings_update_connection(priv->settings, + sett_conn, + NULL, /* plugin_name */ + connection, + NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY, + NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED, + NM_SETTINGS_CONNECTION_INT_FLAGS_NONE, + NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE, + /* log_context_name */ "ofono", + &error); + } + + if (!sett_conn) { + nm_log_warn(LOGD_MB, + "ofono: could not add or update new connection for '%s' (%s): %s", + context_name, + uuid, + error->message); + return; + } + + g_hash_table_insert(priv->connections, g_strdup(uuid), g_object_ref(sett_conn)); +} + +/* + * Used when we decide a connection is not needed. Rationale being that the + * connection could have been modified by user since it's created by us, + * after which we don't want to delete, but we don't want to track it either. + */ + +static void +untrack_connection_and_delete_if_generated(NMModemOfono *self, char const *uuid) +{ + NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self); + NMSettingsConnection *sett_conn; + + sett_conn = g_hash_table_lookup(priv->connections, uuid); + if (!sett_conn) + return; + + if (NM_FLAGS_HAS(nm_settings_connection_get_flags(sett_conn), + NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)) + nm_settings_connection_delete(sett_conn, FALSE); + + g_hash_table_remove(priv->connections, uuid); +} + +static void +update_connection_list(NMModemOfono *self) +{ + NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self); + GHashTableIter iter; + char *uuid; + OfonoContextData *octx; + OfonoContextData *octx_preferred = NULL; + + _LOGI("(re-)checking which context needs a connection"); + + g_hash_table_iter_init(&iter, priv->contexts); + while (g_hash_table_iter_next(&iter, (gpointer *) &uuid, (gpointer *) &octx)) { + if (octx->preferred) { + octx_preferred = octx; + break; + } + } + + g_hash_table_iter_init(&iter, priv->contexts); + while (g_hash_table_iter_next(&iter, (gpointer *) &uuid, (gpointer *) &octx)) { + gboolean connection_should_exist = + (!octx_preferred || octx_preferred == octx) + && (nm_streq(octx->type, "internet") || nm_streq(octx->type, "internet+mms")); + gboolean connection_exists = g_hash_table_contains(priv->connections, uuid); + + if (connection_should_exist && !connection_exists) { + _LOGI("creating connection for %s%s", + octx_preferred ? "preferred context " : "", + g_dbus_proxy_get_object_path(octx->proxy)); + add_or_update_connection(self, octx->name, uuid); + } else if (!connection_should_exist && connection_exists) { + _LOGI("removing connection for %s", g_dbus_proxy_get_object_path(octx->proxy)); + untrack_connection_and_delete_if_generated(self, uuid); + + /* priv->current_octx is deliberately not cleared here because the + * disconnection chain happens in another main loop iteration. + */ + } + } +} + +static void handle_settings(NMModemOfono *self, GVariant *v_dict); + +static void +context_property_changed(GDBusProxy *proxy, const char *property, GVariant *v, gpointer user_data) +{ + OfonoContextData *octx = user_data; + NMModemOfono *self = octx->self; + NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self); + gs_unref_variant GVariant *v_inner = g_variant_get_child_value(v, 0); + + if (!v_inner) { + _LOGW("ofono: (%s): error handling PropertyChanged signal", + nm_modem_get_uid(NM_MODEM(self))); + return; + } + + if (nm_streq(property, "Name")) { + gs_free char *uuid = NULL; + + g_return_if_fail(g_variant_is_of_type(v_inner, G_VARIANT_TYPE_STRING)); + g_free(octx->name); + octx->name = g_variant_dup_string(v_inner, /* &length */ NULL); + + uuid = _generate_uuid(priv->imsi, g_dbus_proxy_get_object_path(proxy)); + if (g_hash_table_contains(priv->connections, uuid)) + add_or_update_connection(self, octx->name, uuid); + } else if (nm_streq(property, "Type")) { + g_return_if_fail(g_variant_is_of_type(v_inner, G_VARIANT_TYPE_STRING)); + g_free(octx->type); + octx->type = g_variant_dup_string(v_inner, /* &length */ NULL); + + update_connection_list(self); + } else if (nm_streq(property, "Preferred")) { + g_return_if_fail(g_variant_is_of_type(v_inner, G_VARIANT_TYPE_BOOLEAN)); + octx->preferred = g_variant_get_boolean(v_inner); + + update_connection_list(self); + } else if (nm_streq(property, "Settings") && priv->current_octx == octx) { + g_return_if_fail(g_variant_is_of_type(v_inner, G_VARIANT_TYPE_VARDICT)); + handle_settings(self, v_inner); + } +} + +static void +_context_proxy_new_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + OfonoContextData *octx = user_data; + NMModemOfono *self; + NMModemOfonoPrivate *priv; + gs_free_error GError *error = NULL; + GDBusProxy *proxy; + char *uuid; + + proxy = g_dbus_proxy_new_for_bus_finish(result, &error); + if (!proxy && g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + ofono_context_data_free(octx); + return; + } + + self = octx->self; + priv = NM_MODEM_OFONO_GET_PRIVATE(self); + + if (!proxy) { + _LOGW("failed to create ConnectionContext proxy: %s", error->message); + ofono_context_data_free(octx); + return; + } + + _LOGD("recieved proxy for %s", g_dbus_proxy_get_object_path(proxy)); + octx->proxy = proxy; + + _nm_dbus_signal_connect(proxy, + "PropertyChanged", + G_VARIANT_TYPE("(sv)"), + G_CALLBACK(context_property_changed), + octx); + + uuid = _generate_uuid(priv->imsi, g_dbus_proxy_get_object_path(proxy)); + g_hash_table_insert(priv->contexts, uuid, octx); + priv->n_context_proxy_pending--; + + if (priv->n_context_proxy_pending == 0) + update_connection_list(self); +} + +static void +connman_context_removed(GDBusProxy *proxy, const char *object_path, gpointer user_data) +{ + NMModemOfono *self = NM_MODEM_OFONO(user_data); + NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self); + gs_free char *uuid = NULL; + OfonoContextData *octx; + + /* look up the connction, and if we have connection disconnect and remove it */ + uuid = _generate_uuid(priv->imsi, object_path); + untrack_connection_and_delete_if_generated(self, uuid); + + octx = g_hash_table_lookup(priv->contexts, uuid); + if (octx) { + gboolean preferred = octx->preferred; + + if (octx == priv->current_octx) + priv->current_octx = NULL; + + g_hash_table_remove(priv->contexts, uuid); + + if (preferred) + update_connection_list(self); + } +} + +static void +connman_context_added(GDBusProxy *proxy, const char *object_path, GVariant *v, gpointer user_data) +{ + NMModemOfono *self = user_data; + NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self); + OfonoContextData *octx; + gs_unref_variant GVariant *v_context_type = NULL; + gs_unref_variant GVariant *v_context_name = NULL; + gs_unref_variant GVariant *v_context_preferred = NULL; + + nm_log_info(LOGD_MB, "ofono: processing context %s", object_path); + + v_context_name = g_variant_lookup_value(v, "Name", G_VARIANT_TYPE_STRING); + v_context_type = g_variant_lookup_value(v, "Type", G_VARIANT_TYPE_STRING); + if (!v_context_name || !v_context_type) { + nm_log_err(LOGD_MB, "ofono: context dictionary is missing required key(s)."); + return; + } + + /* Preferred property exists in some oFono fork only (mostly Ubuntu Touch's). */ + v_context_preferred = g_variant_lookup_value(v, "Preferred", G_VARIANT_TYPE_BOOLEAN); + + octx = g_slice_new0(OfonoContextData); + octx->self = self; + octx->name = g_variant_dup_string(v_context_name, NULL); + octx->type = g_variant_dup_string(v_context_type, NULL); + octx->preferred = v_context_preferred && g_variant_get_boolean(v_context_preferred); + + priv->n_context_proxy_pending++; + g_dbus_proxy_new_for_bus(G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES + | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, + NULL, /* GDBusInterfaceInfo */ + OFONO_DBUS_SERVICE, + object_path, + OFONO_DBUS_INTERFACE_CONNECTION_CONTEXT, + priv->connman_proxy_cancellable, + _context_proxy_new_cb, + octx); +} + +static void +connman_get_contexts_done(GObject *source, GAsyncResult *result, gpointer user_data) +{ + NMModemOfono *self; + gs_free_error GError *error = NULL; + gs_unref_variant GVariant *v_contexts = NULL; + gs_unref_variant GVariant *v_objects = NULL; + gs_unref_variant GVariant *v = NULL; + GVariantIter i; + const char *object_path; + + v_contexts = _nm_dbus_proxy_call_finish(G_DBUS_PROXY(source), + result, + G_VARIANT_TYPE("(a(oa{sv}))"), + &error); + if (!v_contexts && g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = user_data; + + if (!v_contexts) { + g_dbus_error_strip_remote_error(error); + _LOGW("Error getting list of contexts: %s", error->message); + return; + } + + nm_log_info(LOGD_MB, "ofono: printing %s", g_variant_get_type_string(v_contexts)); + + v_objects = g_variant_get_child_value(v_contexts, 0); + + g_variant_iter_init(&i, v_objects); + while (g_variant_iter_loop(&i, "(&o@a{sv})", &object_path, &v)) + connman_context_added(NULL, object_path, v, self); +} + static void _connman_proxy_new_cb(GObject *source, GAsyncResult *result, gpointer user_data) { @@ -554,6 +938,18 @@ _connman_proxy_new_cb(GObject *source, GAsyncResult *result, gpointer user_data) G_CALLBACK(connman_property_changed), self); + _nm_dbus_signal_connect(priv->connman_proxy, + "ContextAdded", + G_VARIANT_TYPE("(oa{sv})"), + G_CALLBACK(connman_context_added), + self); + + _nm_dbus_signal_connect(priv->connman_proxy, + "ContextRemoved", + G_VARIANT_TYPE("(o)"), + G_CALLBACK(connman_context_removed), + self); + g_dbus_proxy_call(priv->connman_proxy, "GetProperties", NULL, @@ -562,6 +958,15 @@ _connman_proxy_new_cb(GObject *source, GAsyncResult *result, gpointer user_data) priv->connman_proxy_cancellable, connman_get_properties_done, self); + + g_dbus_proxy_call(priv->connman_proxy, + "GetContexts", + NULL, + G_DBUS_CALL_FLAGS_NONE, + 20000, + priv->connman_proxy_cancellable, + connman_get_contexts_done, + self); } static void @@ -572,6 +977,9 @@ handle_connman_iface(NMModemOfono *self, gboolean found) _LOGD("ConnectionManager interface %sfound", found ? "" : "not "); if (!found && (priv->connman_proxy || priv->connman_proxy_cancellable)) { + GHashTableIter iter; + NMSettingsConnection *conn; + _LOGI("ConnectionManager interface disappeared"); nm_clear_g_cancellable(&priv->connman_proxy_cancellable); if (priv->connman_proxy) { @@ -584,6 +992,18 @@ handle_connman_iface(NMModemOfono *self, gboolean found) */ priv->gprs_attached = FALSE; + g_hash_table_iter_init(&iter, priv->connections); + while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &conn)) { + if (NM_FLAGS_HAS(nm_settings_connection_get_flags(conn), + NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)) { + nm_settings_connection_delete(conn, FALSE); + } + } + + priv->current_octx = NULL; + g_hash_table_remove_all(priv->connections); + g_hash_table_remove_all(priv->contexts); + update_modem_state(self); } else if (found && (!priv->connman_proxy && !priv->connman_proxy_cancellable)) { _LOGI("found new ConnectionManager interface"); @@ -716,7 +1136,7 @@ stage1_prepare_done(GObject *source, GAsyncResult *result, gpointer user_data) self = NM_MODEM_OFONO(user_data); priv = NM_MODEM_OFONO_GET_PRIVATE(self); - g_clear_object(&priv->context_proxy_cancellable); + g_clear_object(&priv->connect_cancellable); nm_clear_pointer(&priv->connect_properties, g_hash_table_destroy); @@ -730,9 +1150,8 @@ stage1_prepare_done(GObject *source, GAsyncResult *result, gpointer user_data) } static void -handle_settings(GVariant *v_dict, gpointer user_data) +handle_settings(NMModemOfono *self, GVariant *v_dict) { - NMModemOfono *self = NM_MODEM_OFONO(user_data); NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self); char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; NMPlatformIP4Address address; @@ -890,28 +1309,6 @@ out: } } -static void -context_property_changed(GDBusProxy *proxy, const char *property, GVariant *v, gpointer user_data) -{ - NMModemOfono *self = NM_MODEM_OFONO(user_data); - gs_unref_variant GVariant *v_dict = NULL; - - _LOGD("PropertyChanged: %s", property); - - if (g_strcmp0(property, "Settings") != 0) - return; - - v_dict = g_variant_get_child_value(v, 0); - if (!v_dict) { - _LOGW("ofono: (%s): error getting IPv4 Settings", nm_modem_get_uid(NM_MODEM(self))); - return; - } - - g_assert(g_variant_is_of_type(v_dict, G_VARIANT_TYPE_VARDICT)); - - handle_settings(v_dict, user_data); -} - static void stage3_ip_config_start(NMModem *modem, int addr_family, NMModemIPMethod ip_method) { @@ -946,17 +1343,14 @@ out: static void context_properties_cb(GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) { - NMModemOfono *self; - NMModemOfonoPrivate *priv; + NMModemOfono *self = user_data; + NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self); gs_free_error GError *error = NULL; gs_unref_variant GVariant *properties = NULL; gs_unref_variant GVariant *settings = NULL; gs_unref_variant GVariant *v_dict = NULL; gboolean active; - self = NM_MODEM_OFONO(user_data); - priv = NM_MODEM_OFONO_GET_PRIVATE(self); - properties = g_dbus_proxy_call_finish(proxy, result, &error); if (!properties) { @@ -976,13 +1370,6 @@ context_properties_cb(GDBusProxy *proxy, GAsyncResult *result, gpointer user_dat goto error; } - /* Watch for custom ofono PropertyChanged signals */ - _nm_dbus_signal_connect(priv->context_proxy, - "PropertyChanged", - G_VARIANT_TYPE("(sv)"), - G_CALLBACK(context_property_changed), - self); - if (active) { _LOGD("ofono: connection is already Active"); @@ -992,9 +1379,9 @@ context_properties_cb(GDBusProxy *proxy, GAsyncResult *result, gpointer user_dat goto error; } - handle_settings(settings, user_data); + handle_settings(self, settings); } else { - g_dbus_proxy_call(priv->context_proxy, + g_dbus_proxy_call(proxy, "SetProperty", g_variant_new("(sv)", "Active", g_variant_new("b", TRUE)), G_DBUS_CALL_FLAGS_NONE, @@ -1010,36 +1397,15 @@ error: } static void -context_proxy_new_cb(GObject *source, GAsyncResult *result, gpointer user_data) +do_context_activate(NMModemOfono *self) { - NMModemOfono *self; - NMModemOfonoPrivate *priv; - gs_free_error GError *error = NULL; - GDBusProxy *proxy; + NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self); - proxy = g_dbus_proxy_new_for_bus_finish(result, &error); - if (!proxy || g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - return; + g_return_if_fail(NM_IS_MODEM_OFONO(self)); - self = NM_MODEM_OFONO(user_data); - priv = NM_MODEM_OFONO_GET_PRIVATE(self); + nm_clear_g_cancellable(&priv->connect_cancellable); - if (!proxy) { - _LOGE("failed to create ofono ConnectionContext DBus proxy: %s", error->message); - g_clear_object(&priv->context_proxy_cancellable); - nm_modem_emit_prepare_result(NM_MODEM(self), FALSE, NM_DEVICE_STATE_REASON_MODEM_BUSY); - return; - } - - priv->context_proxy = proxy; - - if (!priv->gprs_attached) { - g_clear_object(&priv->context_proxy_cancellable); - nm_modem_emit_prepare_result(NM_MODEM(self), - FALSE, - NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER); - return; - } + priv->connect_cancellable = g_cancellable_new(); /* We have an old copy of the settings from a previous activation, * clear it so that we can gate getting the IP config from oFono @@ -1049,39 +1415,16 @@ context_proxy_new_cb(GObject *source, GAsyncResult *result, gpointer user_data) /* We need to directly query ConnectionContextinteface to get the current * property values */ - g_dbus_proxy_call(priv->context_proxy, + g_dbus_proxy_call(priv->current_octx->proxy, "GetProperties", NULL, G_DBUS_CALL_FLAGS_NONE, 20000, - NULL, + priv->connect_cancellable, (GAsyncReadyCallback) context_properties_cb, self); } -static void -do_context_activate(NMModemOfono *self) -{ - NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self); - - g_return_if_fail(NM_IS_MODEM_OFONO(self)); - - nm_clear_g_cancellable(&priv->context_proxy_cancellable); - g_clear_object(&priv->context_proxy); - - priv->context_proxy_cancellable = g_cancellable_new(); - - g_dbus_proxy_new_for_bus(G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, - NULL, - OFONO_DBUS_SERVICE, - priv->context_path, - OFONO_DBUS_INTERFACE_CONNECTION_CONTEXT, - priv->context_proxy_cancellable, - context_proxy_new_cb, - self); -} - static GHashTable * create_connect_properties(NMConnection *connection) { @@ -1114,20 +1457,10 @@ modem_act_stage1_prepare(NMModem *modem, { NMModemOfono *self = NM_MODEM_OFONO(modem); NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self); - const char *context_id; - char **id = NULL; + const char *uuid = nm_connection_get_uuid(connection); + OfonoContextData *octx = g_hash_table_lookup(priv->contexts, uuid); - context_id = nm_connection_get_id(connection); - id = g_strsplit(context_id, "/", 0); - g_return_val_if_fail(id[2], NM_ACT_STAGE_RETURN_FAILURE); - - _LOGD("trying %s %s", id[1], id[2]); - - g_free(priv->context_path); - priv->context_path = g_strdup_printf("%s/%s", nm_modem_get_path(modem), id[2]); - g_strfreev(id); - - if (!priv->context_path) { + if (!octx) { NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_GSM_APN_FAILED); return NM_ACT_STAGE_RETURN_FAILURE; } @@ -1137,7 +1470,8 @@ modem_act_stage1_prepare(NMModem *modem, priv->connect_properties = create_connect_properties(connection); - _LOGI("activating context %s", priv->context_path); + _LOGI("activating context %s", g_dbus_proxy_get_object_path(octx->proxy)); + priv->current_octx = octx; update_modem_state(self); if (nm_modem_get_state(modem) == NM_MODEM_STATE_REGISTERED) { @@ -1194,7 +1528,17 @@ modem_proxy_new_cb(GObject *source, GAsyncResult *result, gpointer user_data) static void nm_modem_ofono_init(NMModemOfono *self) -{} +{ + NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self); + + priv->modem_proxy_cancellable = g_cancellable_new(); + priv->connections = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_object_unref); + priv->contexts = g_hash_table_new_full(nm_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) ofono_context_data_free); + priv->settings = g_object_ref(NM_SETTINGS_GET); +} static void constructed(GObject *object) @@ -1202,8 +1546,6 @@ constructed(GObject *object) NMModemOfono *self = NM_MODEM_OFONO(object); NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self); - priv->modem_proxy_cancellable = g_cancellable_new(); - g_dbus_proxy_new_for_bus(G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, NULL, @@ -1255,7 +1597,7 @@ dispose(GObject *object) nm_clear_g_cancellable(&priv->modem_proxy_cancellable); nm_clear_g_cancellable(&priv->connman_proxy_cancellable); - nm_clear_g_cancellable(&priv->context_proxy_cancellable); + nm_clear_g_cancellable(&priv->connect_cancellable); nm_clear_g_cancellable(&priv->sim_proxy_cancellable); if (priv->connect_properties) { @@ -1263,6 +1605,16 @@ dispose(GObject *object) priv->connect_properties = NULL; } + if (priv->connections) { + g_hash_table_destroy(priv->connections); + priv->connections = NULL; + } + + if (priv->contexts) { + g_hash_table_destroy(priv->contexts); + priv->contexts = NULL; + } + nm_clear_l3cd(&priv->l3cd_4); if (priv->modem_proxy) { @@ -1275,16 +1627,16 @@ dispose(GObject *object) g_clear_object(&priv->connman_proxy); } - if (priv->context_proxy) { - g_signal_handlers_disconnect_by_data(priv->context_proxy, self); - g_clear_object(&priv->context_proxy); - } - if (priv->sim_proxy) { g_signal_handlers_disconnect_by_data(priv->sim_proxy, self); g_clear_object(&priv->sim_proxy); } + if (priv->settings) { + g_signal_handlers_disconnect_by_data(priv->settings, self); + g_clear_object(&priv->settings); + } + g_free(priv->imsi); priv->imsi = NULL;