Merge cros/upstream to cros/master

Contains the following commits:
 8bc90b71 broadband-modem-qmi: Enable AT URCs and QMI indications (Dylan Van Assche)
 438ff54d libmm-glib,tests: include string.h explicitly (Aleksander Morgado)
 da0e610f modem-helpers-mbim: include string.h explicitly (Aleksander Morgado)
 a9611c62 utils: import ptr array lookup with GEqualFunc from GLib 2.54 (Aleksander Morgado)
 e1372a71 iface-modem: detect hotswap on all slots (Pavan Holla)
 fbc16360 docs: add api index for 1.18 (Aleksander Morgado)
 a4aba0a6 mmcli,sim: add preferred networks list to SIM properties (Teijo Kinnunen)
 816beeff libmm-glib,modem-helpers,mm-base-sim: implement Sim.PreferredNetworks (Teijo Kinnunen)
 c7d36667 shared-qmi: network registration cancellation logic with asserts disabled (Aleksander Morgado)
 f10e4af9 libmm-glib,bearer-properties: fix 'allow roaming' comparison (Aleksander Morgado)
 297a8c85 examples: sms: resolve PEP8 issues (Yegor Yefremov)
 aba237df broadband-modem-qmi: allow lookup of QMI for data without SIO port (Aleksander Morgado)
 381e2f38 base-modem: separate method to lookup exact port by name (Aleksander Morgado)
 b8e076f9 kernel-device-udev: keep track of the client object (Aleksander Morgado)
 1b35d74c kernel-device: add get_interface_number() method (Aleksander Morgado)
 cc07d214 examples: network-scan: get rid of global variables (Yegor Yefremov)
 62506034 build: improve releasing notes (Aleksander Morgado)
 4a06a027 charsets: detect iconv() support in runtime (Aleksander Morgado)
 8a8e0016 charsets: define common translit fallback character (Aleksander Morgado)
 c84454c1 charsets: remove charset_hex_to_utf8() (Aleksander Morgado)
 0ff3eb7e charsets: remove take_and_convert methods (Aleksander Morgado)
 ab4c31ec cinterion: rework mno decoding to use str_to_utf8() (Aleksander Morgado)
 6bc07b4b cinterion: rework band encoding to use str_to_utf8() (Aleksander Morgado)
 16df1e17 helpers: rework normalize_operator() to use str_to_utf8() (Aleksander Morgado)
 63fa9eee charsets,tests: update take_and_convert tests to str_from/to (Aleksander Morgado)
 3ac248a7 cinterion: move sequence to set bands to private ctx (Aleksander Morgado)
 e5363b54 charsets: use new str_from_utf8() instead of take_and_convert_to_current_charset() (Aleksander Morgado)
 395ab06c charsets: use new bytearray_to_utf8() instead of hex_to_utf8() (Aleksander Morgado)
 5ea4a591 charsets: use new bytearray_to_utf8() instead of byte_array_to_utf8() (Aleksander Morgado)
 033e174e charsets: make charset_gsm_unpacked_to_utf8() private (Aleksander Morgado)
 8bfdfb18 charsets: use new bytearray_from_utf8() instead of byte_array_append() (Aleksander Morgado)
 75b37e16 charsets: make charset_utf8_to_unpacked_gsm() private (Aleksander Morgado)
 9c613d33 charsets: new common APIs to convert from/to charsets and UTF-8 (Aleksander Morgado)
 6f32c8d3 charsets: avoid //TRANSLIT when converting to/from charsets (Aleksander Morgado)
 bc449cbe charsets: make translit optional in utf8_to_unpacked_gsm() (Aleksander Morgado)
 5ce97abd charsets: make translit optional in gsm_unpacked_to_utf8() (Aleksander Morgado)
 5480cb67 libmm-glib,tests: add ishexstr/hexstr2bin/bin2hexstr unit tests (Aleksander Morgado)
 34de613d libmm-glib,common-helpers: make hexstr2bin() return a guint8 array (Aleksander Morgado)
 6d8610d6 libmm-glib,common-helpers: ishexstr() fails on empty input string (Aleksander Morgado)
 8c30a6b6 libmm-glib,common-helpers: hexstr2bin fails on empty input string (Aleksander Morgado)
 a211981d libmm-glib,common-helpers: make hexstr2bin() accept input string length (Aleksander Morgado)
 657cabcf libmm-glib,common-helpers: make hexstr2bin() return a GError (Aleksander Morgado)
 dbdf67e9 charsets: remove unused charset_utf8_to_hex() method (Aleksander Morgado)
 8b590721 charsets: don't allow quoting in byte_array_append() (Aleksander Morgado)
 38a4a9c8 charsets: remove HEX charset type (Aleksander Morgado)
 a025e83e charsets: define charset enum explicitly as flags (Aleksander Morgado)
 19e5d5f9 build: post-release version bump to 1.17.0 (Aleksander Morgado)
 7a5a49b7 release: bump version to 1.16.0 (Aleksander Morgado)
 7a5eae2a NEWS: update for 1.16.0 (Aleksander Morgado)
 bbd3638d build: require libqmi 1.28.0 (Aleksander Morgado)
 a5462014 bearer-mbim: IP type may be reported as deactivated and still have IP settings (Aleksander Morgado)

Cq-Depend: chromium:2729495
Change-Id: Ib418fd49adc23055e82037d69469da794442425e
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..131ce16
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,6 @@
+set noparent
+ejcaruso@chromium.org
+pholla@chromium.org
+andrewlassalle@chromium.org
+madhavadas@google.com
+vpalatin@chromium.org
diff --git a/PRESUBMIT.cfg b/PRESUBMIT.cfg
new file mode 100644
index 0000000..51d8dd6
--- /dev/null
+++ b/PRESUBMIT.cfg
@@ -0,0 +1,9 @@
+# This sample config file disables all of the ChromiumOS source style checks.
+# Comment out the disable-flags for any checks you want to leave enabled.
+
+[Hook Overrides]
+stray_whitespace_check: false
+long_line_check: false
+cros_license_check: false
+tab_check: false
+
diff --git a/README.chromium b/README.chromium
new file mode 100644
index 0000000..10c881e
--- /dev/null
+++ b/README.chromium
@@ -0,0 +1,20 @@
+DESCRIPTION="Broadband modem support daemon (new API)"
+HOMEPAGE="http://projects.gnome.org/NetworkManager/"
+UPSTREAM_REPO="git://anongit.freedesktop.org/ModemManager/ModemManager"
+LOCAL_GIT_REPO="https://chromium.googlesource.com/chromiumos/third_party/modemmanager-next.git"
+UPSTREAM_BUGSDB="https://bugzilla.gnome.org/enter_bug.cgi?product=NetworkManager"
+LOCAL_BUGSDB="http://crosbug.com"
+LICENSE="GPLv2"
+LICENSE_FILE="COPYING"
+
+Description:
+
+ModemManager provides a DBus interface to control broadband modem
+devices. The intended user is a network manager program, such as
+NetworkManager, flimflam, or shill.
+
+This repository mirrors the 0.6-api branch of the upstream repository
+while it is under active development as a branch.
+
+Local changes should be minimal, but support for particular modems may
+make it here before they make it upstream.
diff --git a/introspection/org.freedesktop.ModemManager1.Modem.Modem3gpp.xml b/introspection/org.freedesktop.ModemManager1.Modem.Modem3gpp.xml
index e741ae9..8abea6a 100644
--- a/introspection/org.freedesktop.ModemManager1.Modem.Modem3gpp.xml
+++ b/introspection/org.freedesktop.ModemManager1.Modem.Modem3gpp.xml
@@ -247,5 +247,28 @@
     -->
     <property name="InitialEpsBearerSettings" type="a{sv}" access="read" />
 
+    <!--
+        Profiles:
+
+        Profiles or contexts provisioned on the modem.
+
+        A list of dictionaries whose entries are:
+        <variablelist>
+        <varlistentry><term><literal>"profile-id"</literal></term>
+          <listitem><para>Unique identifier for this profile (signature <literal>"u"</literal>).</para></listitem></varlistentry>
+        <varlistentry><term><literal>"apn"</literal></term>
+          <listitem><para>Access Point Name, given as a string value (signature <literal>"s"</literal>).</para></listitem></varlistentry>
+        <varlistentry><term><literal>"auth-type"</literal></term>
+          <listitem><para>The authentication method to use, given as a <link linkend="MMBearerAllowedAuth">MMBearerAllowedAuth</link> value (signature <literal>"u"</literal>). Optional.</para></listitem></varlistentry>
+        <varlistentry><term><literal>"user"</literal></term>
+          <listitem><para>User name (if any) required by the network, given as a string value (signature <literal>"s"</literal>). Optional.</para></listitem></varlistentry>
+        <varlistentry><term><literal>"password"</literal></term>
+          <listitem><para>Password (if any) required by the network, given as a string value (signature <literal>"s"</literal>). Optional.</para></listitem></varlistentry>
+        </variablelist>
+
+        This is a read-only property.
+    -->
+    <property name="Profiles" type="aa{sv}" access="read" />
+
   </interface>
 </node>
diff --git a/plugins/generic/mm-plugin-generic.c b/plugins/generic/mm-plugin-generic.c
index f4c7342..209366a 100644
--- a/plugins/generic/mm-plugin-generic.c
+++ b/plugins/generic/mm-plugin-generic.c
@@ -58,6 +58,13 @@
               GError **error)
 {
 #if defined WITH_QMI
+#if QMI_QRTR_SUPPORTED //TODO(crbug.com/1103840): Remove the ifdefs and upstream
+    return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid,
+                                                      drivers,
+                                                      mm_plugin_get_name (self),
+                                                      vendor,
+                                                      product));
+#endif
     if (mm_port_probe_list_has_qmi_port (probes)) {
         mm_obj_dbg (self, "QMI-powered generic modem found...");
         return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid,
diff --git a/plugins/novatel/mm-broadband-modem-novatel-lte.c b/plugins/novatel/mm-broadband-modem-novatel-lte.c
index df33138..3748ac9 100644
--- a/plugins/novatel/mm-broadband-modem-novatel-lte.c
+++ b/plugins/novatel/mm-broadband-modem-novatel-lte.c
@@ -635,6 +635,49 @@
 }
 
 /*****************************************************************************/
+/* Initializing the modem (during first enabling) */
+
+static const MMBaseModemAtCommand modem_init_sequence[] = {
+    /* Init command. ITU rec v.250 (6.1.1) says:
+     *   The DTE should not include additional commands on the same command line
+     *   after the Z command because such commands may be ignored.
+     * So run ATZ alone.
+     */
+    { "Z",       6, FALSE, mm_base_modem_response_processor_no_result_continue },
+
+    /* Temporarily force the modem into LTE only mode to prevent it from falling
+     * back to 3G.
+     * TODO(benchan): Remove this constraint
+     */
+    { "$NWPREFMODE=30", 6, FALSE, mm_base_modem_response_processor_continue_on_error },
+
+    { NULL }
+};
+
+static gboolean
+enabling_modem_init_finish (MMBroadbandModem *self,
+                            GAsyncResult *res,
+                            GError **error)
+{
+    return !!mm_base_modem_at_command_full_finish (MM_BASE_MODEM (self), res, error);
+}
+
+static void
+enabling_modem_init (MMBroadbandModem *self,
+                     GAsyncReadyCallback callback,
+                     gpointer user_data)
+{
+    mm_base_modem_at_sequence_full (MM_BASE_MODEM (self),
+                                    mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)),
+                                    modem_init_sequence,
+                                    NULL,  /* response_processor_context */
+                                    NULL,  /* response_processor_context_free */
+                                    NULL,  /* cancellable */
+                                    callback,
+                                    user_data);
+}
+
+/*****************************************************************************/
 
 MMBroadbandModemNovatelLte *
 mm_broadband_modem_novatel_lte_new (const gchar *device,
@@ -649,6 +692,11 @@
                          MM_BASE_MODEM_PLUGIN, plugin,
                          MM_BASE_MODEM_VENDOR_ID, vendor_id,
                          MM_BASE_MODEM_PRODUCT_ID, product_id,
+                         /* Temporarily allows only EPS network registration status */
+                         /* TODO(benchan): Remove this constraint */
+                         MM_IFACE_MODEM_3GPP_CS_NETWORK_SUPPORTED, FALSE,
+                         MM_IFACE_MODEM_3GPP_PS_NETWORK_SUPPORTED, FALSE,
+                         MM_IFACE_MODEM_3GPP_EPS_NETWORK_SUPPORTED, TRUE,
                          NULL);
 }
 
@@ -695,4 +743,8 @@
 static void
 mm_broadband_modem_novatel_lte_class_init (MMBroadbandModemNovatelLteClass *klass)
 {
+    MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass);
+
+    broadband_modem_class->enabling_modem_init = enabling_modem_init;
+    broadband_modem_class->enabling_modem_init_finish = enabling_modem_init_finish;
 }
diff --git a/plugins/tests/test-fixture.c b/plugins/tests/test-fixture.c
index 29eb8d5..ac2d3e6 100644
--- a/plugins/tests/test-fixture.c
+++ b/plugins/tests/test-fixture.c
@@ -142,7 +142,7 @@
             break;
 
         /* Blocking wait */
-        g_assert_cmpuint (wait_time, <=, 20);
+        g_assert_cmpuint (wait_time, <=, 120);
         wait_time++;
         sleep (1);
     }
diff --git a/src/mm-base-manager.c b/src/mm-base-manager.c
index 4e866f8..f3f7e51 100644
--- a/src/mm-base-manager.c
+++ b/src/mm-base-manager.c
@@ -1263,6 +1263,73 @@
 
 /*****************************************************************************/
 
+#if QMI_QRTR_SUPPORTED //TODO(crbug.com/1103840): Remove hacks before merging to upstream
+static void
+create_virtual_device (const gchar *id,
+                       const gchar *plugin_name,
+                       const gchar *const *ports,
+                       MMBaseManager *self)
+{
+    MMPlugin *plugin;
+    MMDevice *device;
+    gchar *physdev_uid;
+    GError *error = NULL;
+
+    mm_obj_info (self, "Creating virtual device '%s'", id);
+
+    /* Create device and keep it listed in the Manager */
+    physdev_uid = g_strdup_printf ("/virtual/%s", id);
+    device = mm_device_new (physdev_uid, TRUE, TRUE, self->priv->object_manager);
+    g_hash_table_insert (self->priv->devices, physdev_uid, device);
+
+    /* Grab virtual ports */
+    mm_device_virtual_grab_ports (device, (const gchar **)ports);
+
+    /* Set plugin to use */
+    plugin = mm_plugin_manager_peek_plugin (self->priv->plugin_manager, plugin_name);
+    if (!plugin) {
+        error = g_error_new (MM_CORE_ERROR,
+                             MM_CORE_ERROR_NOT_FOUND,
+                             "Requested plugin '%s' not found",
+                             plugin_name);
+        mm_obj_warn (self, "Couldn't set plugin for virtual device '%s': %s",
+                 mm_device_get_uid (device),
+                 error->message);
+        goto out;
+    }
+    mm_device_set_plugin (device, G_OBJECT (plugin));
+
+    /* Create modem */
+    if (!mm_device_create_modem (device, &error)) {
+        mm_obj_warn (self, "Couldn't create modem for virtual device '%s': %s",
+                 mm_device_get_uid (device),
+                 error->message);
+        goto out;
+    }
+
+    mm_obj_info (self, "Modem for virtual device '%s' successfully created",
+             mm_device_get_uid (device));
+
+out:
+
+    if (error) {
+        mm_device_remove_modem (device);
+        g_hash_table_remove (self->priv->devices, mm_device_get_uid (device));
+        g_error_free (error);
+    }
+}
+
+static gboolean
+create_fake_modem (MMBaseManager *self)
+{
+    const gchar *ports[] = { "qmi0", "rmnet_data0", NULL };
+
+    create_virtual_device ("fake", "generic", ports, self);
+
+    return FALSE;
+}
+#endif
+
 MMBaseManager *
 mm_base_manager_new (GDBusConnection  *connection,
                      const gchar      *plugin_dir,
@@ -1458,6 +1525,9 @@
                                                error))
             return FALSE;
     }
+#if QMI_QRTR_SUPPORTED //TODO(crbug.com/1103840): Remove hacks before merging to upstream
+    g_timeout_add_seconds (3, (GSourceFunc)create_fake_modem, MM_BASE_MANAGER (initable));
+#endif
 
     /* All good */
     return TRUE;
diff --git a/src/mm-base-modem.c b/src/mm-base-modem.c
index 72b9b84..cfa50a2 100644
--- a/src/mm-base-modem.c
+++ b/src/mm-base-modem.c
@@ -288,6 +288,18 @@
         port = base_modem_create_ignored_port (self, name);
     else if (g_str_equal (subsys, "net"))
         port = base_modem_create_net_port (self, name);
+#if QMI_QRTR_SUPPORTED //TODO(crbug.com/1103840): Remove hacks before merging to upstream
+    else if (g_str_has_prefix (subsys, "virtual") &&
+             g_str_has_prefix (name, "rmnet_data0")) {
+        mm_obj_info (self, "@@ %s: virtual net port = %s", __FUNCTION__, name);
+        port = base_modem_create_net_port (self, name);
+    }
+    else if (g_str_has_prefix (subsys, "virtual") &&
+             g_str_has_prefix (name, "qmi")) {
+        mm_obj_info (self, "@@ %s: virtual qmi port = %s", __FUNCTION__, name);
+        port = base_modem_create_usbmisc_port (self, name, ptype);
+    }
+#endif
     else if (g_str_equal (subsys, "tty"))
         port = base_modem_create_tty_port (self, name, kernel_device, ptype);
     else if (g_str_equal (subsys, "usbmisc"))
diff --git a/src/mm-bearer-qmi.c b/src/mm-bearer-qmi.c
index 670fb80..e14c77e 100644
--- a/src/mm-bearer-qmi.c
+++ b/src/mm-bearer-qmi.c
@@ -412,6 +412,9 @@
     CONNECT_STEP_IP_FAMILY_IPV4,
     CONNECT_STEP_BIND_DATA_PORT_IPV4,
     CONNECT_STEP_ENABLE_INDICATIONS_IPV4,
+#if QMI_QRTR_SUPPORTED //TODO(crbug.com/1103840): Remove hacks before merging to upstream
+    CONNECT_STEP_BIND_MUX_DATA_PORT_IPV4,
+#endif
     CONNECT_STEP_START_NETWORK_IPV4,
     CONNECT_STEP_GET_CURRENT_SETTINGS_IPV4,
     CONNECT_STEP_IPV6,
@@ -419,6 +422,9 @@
     CONNECT_STEP_IP_FAMILY_IPV6,
     CONNECT_STEP_BIND_DATA_PORT_IPV6,
     CONNECT_STEP_ENABLE_INDICATIONS_IPV6,
+#if QMI_QRTR_SUPPORTED //TODO(crbug.com/1103840): Remove hacks before merging to upstream
+    CONNECT_STEP_BIND_MUX_DATA_PORT_IPV6,
+#endif
     CONNECT_STEP_START_NETWORK_IPV6,
     CONNECT_STEP_GET_CURRENT_SETTINGS_IPV6,
     CONNECT_STEP_LAST
@@ -553,6 +559,53 @@
 
 static void connect_context_step (GTask *task);
 
+#if QMI_QRTR_SUPPORTED //TODO(crbug.com/1103840): Remove hacks before merging to upstream
+static void bind_mux_data_port_ready (QmiClientWds *client,
+                                      GAsyncResult *res,
+                                      GTask *task)
+{
+    MMBearerQmi *self;
+    ConnectContext *ctx;
+    GError *error = NULL;
+    QmiMessageWdsBindMuxDataPortOutput *output;
+
+    self = g_task_get_task_data (task);
+    ctx = g_task_get_task_data (task);
+    g_assert (ctx->running_ipv4 || ctx->running_ipv6);
+    g_assert (!(ctx->running_ipv4 && ctx->running_ipv6));
+
+    output = qmi_client_wds_bind_mux_data_port_finish (client, res, &error);
+    if (!output ||
+        !qmi_message_wds_bind_mux_data_port_output_get_result (output, &error)) {
+        mm_obj_info (self, "error: couldn't bind mux data port: %s\n", error->message);
+
+        if (ctx->running_ipv4)
+            ctx->error_ipv4 = error;
+        else
+            ctx->error_ipv6 = error;
+
+        ctx->step = CONNECT_STEP_LAST;
+    } else
+        ctx->step++;
+
+    if (output)
+       qmi_message_wds_bind_mux_data_port_output_unref (output);
+
+    connect_context_step (task);
+}
+
+static QmiMessageWdsBindMuxDataPortInput *
+build_bind_mux_data_port_input (void)
+{
+    QmiMessageWdsBindMuxDataPortInput *input;
+
+    input = qmi_message_wds_bind_mux_data_port_input_new ();
+    qmi_message_wds_bind_mux_data_port_input_set_endpoint_info (input, 0x4, 0x1, NULL);
+    qmi_message_wds_bind_mux_data_port_input_set_mux_id (input, 0x1, NULL);
+    return input;
+}
+#endif
+
 static void
 start_network_ready (QmiClientWds *client,
                      GAsyncResult *res,
@@ -1364,10 +1417,14 @@
          * to request. If the LLP is raw-ip, we force Static IP, because not
          * all DHCP clients support the raw-ip interfaces; otherwise default
          * to DHCP as always. */
+#if QMI_QRTR_SUPPORTED //TODO(crbug.com/1103840): Remove hacks before merging to upstream
+	ctx->ip_method = MM_BEARER_IP_METHOD_STATIC;
+#else
         if (mm_port_qmi_llp_is_raw_ip (ctx->qmi))
             ctx->ip_method = MM_BEARER_IP_METHOD_STATIC;
         else
             ctx->ip_method = MM_BEARER_IP_METHOD_DHCP;
+#endif
 
         mm_obj_dbg (self, "defaulting to use %s IP method", mm_bearer_ip_method_get_string (ctx->ip_method));
         ctx->step++;
@@ -1462,6 +1519,24 @@
                                                task);
         return;
 
+#if QMI_QRTR_SUPPORTED //TODO(crbug.com/1103840): Remove hacks before merging to upstream
+    case CONNECT_STEP_BIND_MUX_DATA_PORT_IPV4: {
+        QmiMessageWdsBindMuxDataPortInput *input;
+
+        mm_obj_dbg (ctx->self, "Binding mux data port for IPv4...");
+
+        input = build_bind_mux_data_port_input ();
+        qmi_client_wds_bind_mux_data_port (ctx->client_ipv4,
+                                           input,
+                                           10,
+                                           g_task_get_cancellable (task),
+                                           (GAsyncReadyCallback) bind_mux_data_port_ready,
+                                           task);
+        qmi_message_wds_bind_mux_data_port_input_unref (input);
+        return;
+    }
+#endif
+
     case CONNECT_STEP_START_NETWORK_IPV4: {
         QmiMessageWdsStartNetworkInput *input;
 
@@ -1573,6 +1648,24 @@
                                                task);
         return;
 
+#if QMI_QRTR_SUPPORTED //TODO(crbug.com/1103840): Remove hacks before merging to upstream
+    case CONNECT_STEP_BIND_MUX_DATA_PORT_IPV6: {
+        QmiMessageWdsBindMuxDataPortInput *input;
+
+        mm_obj_dbg (self, "Binding mux data port for IPv6...");
+
+        input = build_bind_mux_data_port_input ();
+        qmi_client_wds_bind_mux_data_port (ctx->client_ipv6,
+                                           input,
+                                           10,
+                                           g_task_get_cancellable (task),
+                                           (GAsyncReadyCallback) bind_mux_data_port_ready,
+                                           task);
+        qmi_message_wds_bind_mux_data_port_input_unref (input);
+        return;
+    }
+#endif
+
     case CONNECT_STEP_START_NETWORK_IPV6: {
         QmiMessageWdsStartNetworkInput *input;
 
@@ -1712,7 +1805,13 @@
     }
 
     /* Each data port has a single QMI port associated */
+
+#if QMI_QRTR_SUPPORTED //TODO(crbug.com/1103840): Remove hacks before merging to upstream
+    qmi = mm_broadband_modem_qmi_get_port_qmi (MM_BROADBAND_MODEM_QMI (modem));
+#else
     qmi = mm_broadband_modem_qmi_get_port_qmi_for_data (MM_BROADBAND_MODEM_QMI (modem), data, &sio_port, &error);
+#endif
+
     if (!qmi) {
         g_task_report_error (
             self,
diff --git a/src/mm-broadband-modem-mbim.c b/src/mm-broadband-modem-mbim.c
index 4ee5b22..c524af4 100644
--- a/src/mm-broadband-modem-mbim.c
+++ b/src/mm-broadband-modem-mbim.c
@@ -84,6 +84,7 @@
     PROCESS_NOTIFICATION_FLAG_PCO                  = 1 << 6,
     PROCESS_NOTIFICATION_FLAG_USSD                 = 1 << 7,
     PROCESS_NOTIFICATION_FLAG_LTE_ATTACH_STATUS    = 1 << 8,
+    PROCESS_NOTIFICATION_FLAG_PROVISIONED_CONTEXTS = 1 << 9,
 } ProcessNotificationFlag;
 
 #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
@@ -3307,6 +3308,30 @@
     update_access_technologies (self);
 }
 
+static void
+basic_connect_notification_provisioned_contexts (MMBroadbandModemMbim *self,
+                                                 MbimMessage *notification)
+{
+    MbimProvisionedContextElement **provisioned_contexts;
+    guint32 n_provisioned_contexts;
+    GList *profiles;
+
+    if (!mbim_message_provisioned_contexts_notification_parse (
+            notification,
+            &n_provisioned_contexts,
+            &provisioned_contexts,
+            NULL)) {
+        return;
+    }
+
+    profiles = mm_3gpp_profile_list_from_mbim_provisioned_contexts (
+        (const MbimProvisionedContextElement *const *)provisioned_contexts,
+        n_provisioned_contexts);
+    mbim_provisioned_context_element_array_free (provisioned_contexts);
+
+    mm_iface_modem_3gpp_update_profiles (MM_IFACE_MODEM_3GPP (self), profiles);
+}
+
 static void add_sms_part (MMBroadbandModemMbim *self,
                           const MbimSmsPduReadRecord *pdu);
 
@@ -3362,6 +3387,10 @@
         if (self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE)
             basic_connect_notification_packet_service (self, notification);
         break;
+    case MBIM_CID_BASIC_CONNECT_PROVISIONED_CONTEXTS:
+        if (self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_PROVISIONED_CONTEXTS)
+            basic_connect_notification_provisioned_contexts (self, notification);
+        break;
     default:
         /* Ignore */
         break;
@@ -3627,7 +3656,7 @@
     if (!device)
         return;
 
-    mm_obj_dbg (self, "supported notifications: signal (%s), registration (%s), sms (%s), connect (%s), subscriber (%s), packet (%s), pco (%s), ussd (%s), lte attach status (%s)",
+    mm_obj_dbg (self, "supported notifications: signal (%s), registration (%s), sms (%s), connect (%s), subscriber (%s), packet (%s), pco (%s), ussd (%s), lte attach status (%s), provisioned contexts (%s)",
                 self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_SIGNAL_QUALITY ? "yes" : "no",
                 self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_REGISTRATION_UPDATES ? "yes" : "no",
                 self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_SMS_READ ? "yes" : "no",
@@ -3636,7 +3665,8 @@
                 self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE ? "yes" : "no",
                 self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_PCO ? "yes" : "no",
                 self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_USSD ? "yes" : "no",
-                self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_LTE_ATTACH_STATUS ? "yes" : "no");
+                self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_LTE_ATTACH_STATUS ? "yes" : "no",
+                self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_PROVISIONED_CONTEXTS ? "yes" : "no");
 
     if (setup) {
         /* Don't re-enable it if already there */
@@ -3705,6 +3735,7 @@
     self->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_SIGNAL_QUALITY;
     self->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_CONNECT;
     self->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE;
+    self->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_PROVISIONED_CONTEXTS;
     if (self->priv->is_pco_supported)
         self->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_PCO;
     if (self->priv->is_lte_attach_status_supported)
@@ -3723,6 +3754,7 @@
     self->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_CONNECT;
     self->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO;
     self->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE;
+    self->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_PROVISIONED_CONTEXTS;
     if (self->priv->is_pco_supported)
         self->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_PCO;
     if (self->priv->is_lte_attach_status_supported)
@@ -3801,7 +3833,7 @@
     if (!peek_device (self, &device, callback, user_data))
         return;
 
-    mm_obj_dbg (self, "enabled notifications: signal (%s), registration (%s), sms (%s), connect (%s), subscriber (%s), packet (%s), pco (%s), ussd (%s), lte attach status (%s)",
+    mm_obj_dbg (self, "enabled notifications: signal (%s), registration (%s), sms (%s), connect (%s), subscriber (%s), packet (%s), pco (%s), ussd (%s), lte attach status (%s), provisioned contexts (%s)",
                 self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_SIGNAL_QUALITY ? "yes" : "no",
                 self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_REGISTRATION_UPDATES ? "yes" : "no",
                 self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_SMS_READ ? "yes" : "no",
@@ -3810,7 +3842,8 @@
                 self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE ? "yes" : "no",
                 self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_PCO ? "yes" : "no",
                 self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_USSD ? "yes" : "no",
-                self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_LTE_ATTACH_STATUS ? "yes" : "no");
+                self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_LTE_ATTACH_STATUS ? "yes" : "no",
+                self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_PROVISIONED_CONTEXTS ? "yes" : "no");
 
     entries = g_new0 (MbimEventEntry *, 5);
 
@@ -3819,11 +3852,12 @@
         self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_REGISTRATION_UPDATES ||
         self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_CONNECT ||
         self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO ||
-        self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE) {
+        self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE ||
+        self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_PROVISIONED_CONTEXTS) {
         entries[n_entries] = g_new (MbimEventEntry, 1);
         memcpy (&(entries[n_entries]->device_service_id), MBIM_UUID_BASIC_CONNECT, sizeof (MbimUuid));
         entries[n_entries]->cids_count = 0;
-        entries[n_entries]->cids = g_new0 (guint32, 5);
+        entries[n_entries]->cids = g_new0 (guint32, 6);
         if (self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_SIGNAL_QUALITY)
             entries[n_entries]->cids[entries[n_entries]->cids_count++] = MBIM_CID_BASIC_CONNECT_SIGNAL_STATE;
         if (self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_REGISTRATION_UPDATES)
@@ -3834,6 +3868,8 @@
             entries[n_entries]->cids[entries[n_entries]->cids_count++] = MBIM_CID_BASIC_CONNECT_SUBSCRIBER_READY_STATUS;
         if (self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE)
             entries[n_entries]->cids[entries[n_entries]->cids_count++] = MBIM_CID_BASIC_CONNECT_PACKET_SERVICE;
+        if (self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_PROVISIONED_CONTEXTS)
+            entries[n_entries]->cids[entries[n_entries]->cids_count++] = MBIM_CID_BASIC_CONNECT_PROVISIONED_CONTEXTS;
         n_entries++;
     }
 
@@ -4079,6 +4115,7 @@
     if (is_sim_hot_swap_configured)
         self->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO;
     self->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE;
+    self->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_PROVISIONED_CONTEXTS;
     if (self->priv->is_pco_supported)
         self->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_PCO;
     if (self->priv->is_lte_attach_status_supported)
@@ -4097,6 +4134,7 @@
     self->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_CONNECT;
     self->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO;
     self->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE;
+    self->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_PROVISIONED_CONTEXTS;
     if (self->priv->is_pco_supported)
         self->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_PCO;
     if (self->priv->is_lte_attach_status_supported)
@@ -4487,6 +4525,86 @@
 }
 
 /*****************************************************************************/
+/* Load profiles (3GPP interface) */
+
+static gboolean
+modem_3gpp_load_profiles_finish (MMIfaceModem3gpp *self,
+                                 GAsyncResult *res,
+                                 GList **out_list,
+                                 GError **error)
+{
+    GTask *task;
+
+    task = G_TASK (res);
+    if (!g_task_propagate_boolean (task, error))
+        return FALSE;
+
+    if (out_list)
+        *out_list = mm_3gpp_profile_list_copy (g_task_get_task_data (task));
+    return TRUE;
+}
+
+static void
+provisioned_contexts_ready (MbimDevice *device,
+                            GAsyncResult *res,
+                            GTask *task)
+{
+    MbimMessage *response;
+    MbimProvisionedContextElement **provisioned_contexts;
+    guint32 n_provisioned_contexts;
+    GError *error = NULL;
+
+    response = mbim_device_command_finish (device, res, &error);
+    if (response &&
+        mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) &&
+        mbim_message_provisioned_contexts_response_parse (response,
+                                                          &n_provisioned_contexts,
+                                                          &provisioned_contexts,
+                                                          &error)) {
+        GList *profiles;
+
+        profiles = mm_3gpp_profile_list_from_mbim_provisioned_contexts (
+            (const MbimProvisionedContextElement *const *)provisioned_contexts,
+            n_provisioned_contexts);
+        mbim_provisioned_context_element_array_free (provisioned_contexts);
+
+        g_task_set_task_data (task, profiles, (GDestroyNotify)mm_3gpp_profile_list_free);
+        g_task_return_boolean (task, TRUE);
+    } else
+        g_task_return_error (task, error);
+
+    g_object_unref (task);
+
+    if (response)
+        mbim_message_unref (response);
+}
+
+static void
+modem_3gpp_load_profiles (MMIfaceModem3gpp *self,
+                          GAsyncReadyCallback callback,
+                          gpointer user_data)
+{
+    MbimDevice *device;
+    MbimMessage *message;
+    GTask *task;
+
+    if (!peek_device (self, &device, callback, user_data))
+        return;
+
+    task = g_task_new (self, NULL, callback, user_data);
+
+    mm_obj_dbg (self, "loading provisioned contexts...");
+    message = mbim_message_provisioned_contexts_query_new (NULL);
+    mbim_device_command (device,
+                         message,
+                         300,
+                         NULL,
+                         (GAsyncReadyCallback)provisioned_contexts_ready,
+                         task);
+    mbim_message_unref (message);
+}
+
+/*****************************************************************************/
 /* Check support (Signal interface) */
 
 static gboolean
@@ -5767,6 +5885,8 @@
     iface->register_in_network_finish = modem_3gpp_register_in_network_finish;
     iface->scan_networks = modem_3gpp_scan_networks;
     iface->scan_networks_finish = modem_3gpp_scan_networks_finish;
+    iface->load_profiles = modem_3gpp_load_profiles;
+    iface->load_profiles_finish = modem_3gpp_load_profiles_finish;
 }
 
 static void
diff --git a/src/mm-broadband-modem-qmi.c b/src/mm-broadband-modem-qmi.c
index 3a45213..f51146e 100644
--- a/src/mm-broadband-modem-qmi.c
+++ b/src/mm-broadband-modem-qmi.c
@@ -101,6 +101,10 @@
     /* Index of the WDS profile used as initial EPS bearer */
     guint16 default_attach_pdn;
 
+    /* The WDS profile for the initial EPS bearer is owned by ModemManager
+     * and can be modified */
+    gboolean mm_owned_attach_pdn;
+
     /* 3GPP/CDMA registration helpers */
     gchar *current_operator_id;
     gchar *current_operator_description;
@@ -2575,6 +2579,223 @@
 }
 
 /*****************************************************************************/
+/* Load profiles (3GPP interface) */
+
+typedef struct {
+    QmiClientWds *client;
+    guint i;
+    GArray *profile_ids;
+    GList *profiles;
+} GetProfileListContext;
+
+static void
+get_profile_list_context_free (GetProfileListContext *ctx)
+{
+    g_object_unref (ctx->client);
+    g_array_unref (ctx->profile_ids);
+    g_list_free_full (ctx->profiles, (GDestroyNotify) qmi_message_wds_get_profile_settings_output_unref);
+    g_slice_free (GetProfileListContext, ctx);
+}
+
+static gboolean
+modem_3gpp_load_profiles_finish (MMIfaceModem3gpp *self,
+                                 GAsyncResult *res,
+                                 GList **out_list,
+                                 GError **error)
+{
+    GTask *task;
+
+    task = G_TASK (res);
+    if (!g_task_propagate_boolean (task, error))
+        return FALSE;
+
+    if (out_list)
+        *out_list = mm_3gpp_profile_list_copy (g_task_get_task_data (task));
+    return TRUE;
+}
+
+static void get_next_profile_settings (GTask *task);
+
+static void
+get_profile_settings_ready (QmiClientWds *client,
+                            GAsyncResult *res,
+                            GTask *task)
+{
+    GetProfileListContext *ctx;
+    QmiMessageWdsGetProfileSettingsOutput *output;
+    GError *error = NULL;
+
+    ctx = g_task_get_task_data (task);
+
+    output = qmi_client_wds_get_profile_settings_finish (client, res, &error);
+    if (!output) {
+        g_task_return_error (task, error);
+        g_object_unref (task);
+        return;
+    }
+
+    if (!qmi_message_wds_get_profile_settings_output_get_result (output, &error)) {
+        QmiWdsDsProfileError ds_profile_error;
+
+        if (g_error_matches (error,
+                             QMI_PROTOCOL_ERROR,
+                             QMI_PROTOCOL_ERROR_EXTENDED_INTERNAL) &&
+            qmi_message_wds_get_profile_settings_output_get_extended_error_code (
+                output,
+                &ds_profile_error,
+                NULL)) {
+            g_task_return_new_error (task,
+                                     QMI_PROTOCOL_ERROR,
+                                     QMI_PROTOCOL_ERROR_EXTENDED_INTERNAL,
+                                     "DS profile error: %s\n",
+                                     qmi_wds_ds_profile_error_get_string (ds_profile_error));
+            g_error_free (error);
+        } else {
+            g_task_return_error (task, error);
+        }
+
+        qmi_message_wds_get_profile_settings_output_unref (output);
+        g_object_unref (task);
+        return;
+    }
+
+    ctx->profiles = g_list_prepend (ctx->profiles, output);
+    ctx->i++;
+    get_next_profile_settings (task);
+}
+
+static void
+get_next_profile_settings (GTask *task)
+{
+    QmiMessageWdsGetProfileListOutputProfileListProfile *profile;
+    QmiMessageWdsGetProfileSettingsInput *input;
+    GetProfileListContext *ctx;
+
+    ctx = g_task_get_task_data (task);
+
+    if (ctx->i == ctx->profile_ids->len) {
+        g_task_set_task_data (task,
+                              mm_3gpp_profile_list_from_qmi_profile_settings (ctx->profiles),
+                              (GDestroyNotify) mm_3gpp_profile_list_free);
+        g_task_return_boolean (task, TRUE);
+        g_object_unref (task);
+        return;
+    }
+
+    profile = &g_array_index (ctx->profile_ids, QmiMessageWdsGetProfileListOutputProfileListProfile, ctx->i);
+
+    input = qmi_message_wds_get_profile_settings_input_new ();
+    qmi_message_wds_get_profile_settings_input_set_profile_id (
+        input,
+        profile->profile_type,
+        profile->profile_index,
+        NULL);
+    qmi_client_wds_get_profile_settings (ctx->client,
+                                         input,
+                                         3,
+                                         NULL,
+                                         (GAsyncReadyCallback)get_profile_settings_ready,
+                                         task);
+    qmi_message_wds_get_profile_settings_input_unref (input);
+}
+
+static void
+get_profile_list_ready (QmiClientWds *client,
+                        GAsyncResult *res,
+                        GTask *task)
+{
+    GError *error = NULL;
+    QmiMessageWdsGetProfileListOutput *output;
+    GetProfileListContext *ctx;
+    GArray *profile_ids = NULL;
+
+    ctx = g_task_get_task_data (task);
+
+    output = qmi_client_wds_get_profile_list_finish (client, res, &error);
+    if (!output) {
+        g_task_return_error (task, error);
+        g_object_unref (task);
+        return;
+    }
+
+    if (!qmi_message_wds_get_profile_list_output_get_result (output, &error)) {
+        QmiWdsDsProfileError ds_profile_error;
+
+        if (g_error_matches (error,
+                             QMI_PROTOCOL_ERROR,
+                             QMI_PROTOCOL_ERROR_EXTENDED_INTERNAL) &&
+            qmi_message_wds_get_profile_list_output_get_extended_error_code (
+                output,
+                &ds_profile_error,
+                NULL)) {
+            g_task_return_new_error (task,
+                                     QMI_PROTOCOL_ERROR,
+                                     QMI_PROTOCOL_ERROR_EXTENDED_INTERNAL,
+                                     "DS profile error: %s\n",
+                                     qmi_wds_ds_profile_error_get_string (ds_profile_error));
+            g_error_free (error);
+        } else {
+            g_task_return_error (task, error);
+        }
+
+        qmi_message_wds_get_profile_list_output_unref (output);
+        g_object_unref (task);
+        return;
+    }
+
+    qmi_message_wds_get_profile_list_output_get_profile_list (output, &profile_ids, NULL);
+
+    if (!profile_ids || !profile_ids->len) {
+        /* No profiles to get details for. */
+        g_task_return_pointer (task, NULL, NULL);
+        g_object_unref (task);
+        return;
+    }
+
+    ctx->profile_ids = profile_ids;
+
+    get_next_profile_settings (task);
+}
+
+static void
+modem_3gpp_load_profiles (MMIfaceModem3gpp *_self,
+                          GAsyncReadyCallback callback,
+                          gpointer user_data)
+{
+    MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
+    GTask *task;
+    QmiClient *client;
+    GetProfileListContext *ctx;
+    QmiMessageWdsGetProfileListInput *input;
+    GError *error = NULL;
+
+    task = g_task_new (self, NULL, callback, user_data);
+    client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+                                        QMI_SERVICE_WDS,
+                                        MM_PORT_QMI_FLAG_DEFAULT,
+                                        &error);
+    if (!client) {
+        g_task_return_error (task, error);
+        g_object_unref (task);
+    }
+
+    ctx = g_slice_new0 (GetProfileListContext);
+    ctx->client = g_object_ref (QMI_CLIENT_WDS (client));
+    g_task_set_task_data (task, ctx, (GDestroyNotify) get_profile_list_context_free);
+
+    input = qmi_message_wds_get_profile_list_input_new ();
+    qmi_message_wds_get_profile_list_input_set_profile_type (input, QMI_WDS_PROFILE_TYPE_3GPP, NULL);
+
+    qmi_client_wds_get_profile_list (ctx->client,
+                                     input,
+                                     10,
+                                     NULL,
+                                     (GAsyncReadyCallback) get_profile_list_ready,
+                                     task);
+    qmi_message_wds_get_profile_list_input_unref (input);
+}
+
+/*****************************************************************************/
 /* Registration checks (3GPP interface) */
 
 static gboolean
@@ -7878,6 +8099,7 @@
     SET_INITIAL_EPS_BEARER_SETTINGS_STEP_FIRST,
     SET_INITIAL_EPS_BEARER_SETTINGS_STEP_LOAD_POWER_STATE,
     SET_INITIAL_EPS_BEARER_SETTINGS_STEP_POWER_DOWN,
+    SET_INITIAL_EPS_BEARER_SETTINGS_STEP_CREATE_PROFILE,
     SET_INITIAL_EPS_BEARER_SETTINGS_STEP_MODIFY_PROFILE,
     SET_INITIAL_EPS_BEARER_SETTINGS_STEP_POWER_UP,
     SET_INITIAL_EPS_BEARER_SETTINGS_STEP_LAST_SETTING,
@@ -7930,14 +8152,82 @@
 }
 
 static void
+set_initial_eps_bearer_set_lte_attach_pdn_ready (QmiClientWds *client,
+                                                 GAsyncResult *res,
+                                                 GTask        *task)
+{
+    g_autoptr(QmiMessageWdsSetLteAttachPdnListOutput)  output = NULL;
+    GError                                            *error = NULL;
+    MMBroadbandModemQmi                               *self;
+    SetInitialEpsBearerSettingsContext                *ctx;
+
+    self = g_task_get_source_object (task);
+    ctx = g_task_get_task_data (task);
+
+    output = qmi_client_wds_set_lte_attach_pdn_list_finish (client, res, &error);
+    if (!output) {
+        g_prefix_error (&error, "QMI operation failed: ");
+        g_task_return_error (task, error);
+        g_object_unref (task);
+        return;
+    }
+
+    if (!qmi_message_wds_set_lte_attach_pdn_list_output_get_result (output, &error)) {
+        g_prefix_error (&error, "Couldn't set the LTE attach PDN list: ");
+        g_task_return_error (task, error);
+        g_object_unref (task);
+        return;
+    }
+
+    self->priv->mm_owned_attach_pdn = TRUE;
+
+    ctx->step++;
+    set_initial_eps_bearer_settings_step (task);
+}
+
+static void
+set_initial_eps_bearer_set_lte_attach_pdn (GTask *task)
+{
+    g_autoptr(QmiMessageWdsSetLteAttachPdnListInput)  input = NULL;
+    MMBroadbandModemQmi                *self;
+    SetInitialEpsBearerSettingsContext *ctx;
+    GArray                             *pdn_list;
+
+    self = g_task_get_source_object (task);
+    ctx  = g_task_get_task_data (task);
+
+    pdn_list = g_array_sized_new (FALSE, FALSE, sizeof (guint16), 1);
+    g_array_append_val (pdn_list, self->priv->default_attach_pdn);
+    //TODO re-add the list from wds_get_lte_attach_pdn_list ?
+
+    input = qmi_message_wds_set_lte_attach_pdn_list_input_new ();
+    qmi_message_wds_set_lte_attach_pdn_list_input_set_list (input,
+                                                            pdn_list,
+                                                            NULL);
+    g_array_unref (pdn_list);
+    qmi_message_wds_set_lte_attach_pdn_list_input_set_action (input,
+                                                              QMI_WDS_ATTACH_PDN_LIST_ACTION_DETACH_OR_PDN_DISCONNECT,
+                                                              NULL);
+
+    qmi_client_wds_set_lte_attach_pdn_list (ctx->client,
+                                            input,
+                                            10,
+                                            NULL,
+                                            (GAsyncReadyCallback)set_initial_eps_bearer_set_lte_attach_pdn_ready,
+                                            task);
+}
+
+static void
 set_initial_eps_bearer_modify_profile_ready (QmiClientWds *client,
                                              GAsyncResult *res,
                                              GTask        *task)
 {
     g_autoptr(QmiMessageWdsModifyProfileOutput)  output = NULL;
     GError                             *error = NULL;
+    MMBroadbandModemQmi                *self;
     SetInitialEpsBearerSettingsContext *ctx;
 
+    self = g_task_get_source_object (task);
     ctx = g_task_get_task_data (task);
 
     output = qmi_client_wds_modify_profile_finish (client, res, &error);
@@ -7962,6 +8252,11 @@
         return;
     }
 
+    if (!self->priv->mm_owned_attach_pdn) {
+        set_initial_eps_bearer_set_lte_attach_pdn (task);
+        return;
+    }
+
     ctx->step++;
     set_initial_eps_bearer_settings_step (task);
 }
@@ -8017,6 +8312,74 @@
 }
 
 static void
+set_initial_eps_bearer_create_profile_ready (QmiClientWds *client,
+                                             GAsyncResult *res,
+                                             GTask        *task)
+{
+    g_autoptr(QmiMessageWdsCreateProfileOutput)  output = NULL;
+    GError                             *error = NULL;
+    MMBroadbandModemQmi                *self;
+    SetInitialEpsBearerSettingsContext *ctx;
+    QmiWdsProfileType                   profile_type;
+    guint8                              index;
+
+    self = g_task_get_source_object (task);
+    ctx = g_task_get_task_data (task);
+
+    output = qmi_client_wds_create_profile_finish (client, res, &error);
+    if (!output) {
+        g_prefix_error (&error, "QMI operation failed: ");
+        g_task_return_error (task, error);
+        g_object_unref (task);
+        return;
+    }
+
+    if (!qmi_message_wds_create_profile_output_get_result (output, &error)) {
+        QmiWdsDsProfileError ds_profile_error;
+
+        if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_EXTENDED_INTERNAL) &&
+            qmi_message_wds_create_profile_output_get_extended_error_code (output, &ds_profile_error, NULL)) {
+            g_prefix_error (&error, "DS profile error: %s: ",
+                            qmi_wds_ds_profile_error_get_string (ds_profile_error));
+        }
+        g_prefix_error (&error, "Couldn't create a new profile: ");
+        g_task_return_error (task, error);
+        g_object_unref (task);
+        return;
+    }
+
+    if (qmi_message_wds_create_profile_output_get_profile_identifier (output, &profile_type, &index, NULL))
+        self->priv->default_attach_pdn = index;
+
+    ctx->step++;
+    set_initial_eps_bearer_settings_step (task);
+}
+
+static void
+set_initial_eps_bearer_create_profile (GTask *task)
+{
+    g_autoptr(QmiMessageWdsCreateProfileInput)  input = NULL;
+    SetInitialEpsBearerSettingsContext *ctx;
+
+    ctx  = g_task_get_task_data (task);
+
+    input = qmi_message_wds_create_profile_input_new ();
+    qmi_message_wds_create_profile_input_set_profile_type (input,
+                                                           QMI_WDS_PROFILE_TYPE_3GPP,
+                                                           NULL);
+    qmi_message_wds_create_profile_input_set_profile_name (input,
+                                                           MM_BROADBAND_MODEM_QMI_PROFILE_NAME,
+                                                           NULL);
+
+    qmi_client_wds_create_profile (ctx->client,
+                                   input,
+                                   10,
+                                   NULL,
+                                   (GAsyncReadyCallback)set_initial_eps_bearer_create_profile_ready,
+                                   task);
+}
+
+static void
 set_initial_eps_bearer_power_down_ready (MMIfaceModem *self,
                                          GAsyncResult *res,
                                          GTask        *task)
@@ -8088,8 +8451,18 @@
                                   task);
                 return;
             }
-            ctx->step++;
-            /* fall through */
+            if (self->priv->mm_owned_attach_pdn) {
+                ctx->step = SET_INITIAL_EPS_BEARER_SETTINGS_STEP_MODIFY_PROFILE;
+                set_initial_eps_bearer_settings_step (task);
+                return;
+            } else {
+                ctx->step++;
+                /* fall through */
+            }
+        case SET_INITIAL_EPS_BEARER_SETTINGS_STEP_CREATE_PROFILE:
+            mm_obj_dbg (self, "creating a profile for initial EPS bearer settings...");
+            set_initial_eps_bearer_create_profile (task);
+            return;
 
         case SET_INITIAL_EPS_BEARER_SETTINGS_STEP_MODIFY_PROFILE:
             mm_obj_dbg (self, "modifying initial EPS bearer settings profile...");
@@ -8173,6 +8546,9 @@
     QmiWdsAuthentication  auth;
     gboolean              flag;
     MMBearerProperties   *properties;
+    MMBroadbandModemQmi  *self;
+
+    self = g_task_get_source_object (task);
 
     output = qmi_client_wds_get_profile_settings_finish (client, res, &error);
     if (!output) {
@@ -8196,6 +8572,12 @@
         return;
     }
 
+    self->priv->mm_owned_attach_pdn = FALSE;
+    if (qmi_message_wds_get_profile_settings_output_get_profile_name (output, &str, NULL)) {
+        if (g_strcmp0(str, MM_BROADBAND_MODEM_QMI_PROFILE_NAME) == 0)
+            self->priv->mm_owned_attach_pdn = TRUE;
+    }
+
     properties = mm_bearer_properties_new ();
     if (qmi_message_wds_get_profile_settings_output_get_apn_name (output, &str, NULL))
         mm_bearer_properties_set_apn (properties, str);
@@ -10231,6 +10613,8 @@
     iface->load_initial_eps_bearer_settings_finish = modem_3gpp_load_initial_eps_bearer_settings_finish;
     iface->set_initial_eps_bearer_settings = modem_3gpp_set_initial_eps_bearer_settings;
     iface->set_initial_eps_bearer_settings_finish = modem_3gpp_set_initial_eps_bearer_settings_finish;
+    iface->load_profiles = modem_3gpp_load_profiles;
+    iface->load_profiles_finish = modem_3gpp_load_profiles_finish;
 }
 
 static void
diff --git a/src/mm-broadband-modem-qmi.h b/src/mm-broadband-modem-qmi.h
index 9b71210..da794b6 100644
--- a/src/mm-broadband-modem-qmi.h
+++ b/src/mm-broadband-modem-qmi.h
@@ -25,6 +25,9 @@
 #define MM_IS_BROADBAND_MODEM_QMI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  MM_TYPE_BROADBAND_MODEM_QMI))
 #define MM_BROADBAND_MODEM_QMI_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  MM_TYPE_BROADBAND_MODEM_QMI, MMBroadbandModemQmiClass))
 
+/* Chromium OS specific profile */
+#define MM_BROADBAND_MODEM_QMI_PROFILE_NAME    "CrOS_attach_PDN"
+
 typedef struct _MMBroadbandModemQmi MMBroadbandModemQmi;
 typedef struct _MMBroadbandModemQmiClass MMBroadbandModemQmiClass;
 typedef struct _MMBroadbandModemQmiPrivate MMBroadbandModemQmiPrivate;
diff --git a/src/mm-iface-modem-3gpp.c b/src/mm-iface-modem-3gpp.c
index 9389935..ec46adc 100644
--- a/src/mm-iface-modem-3gpp.c
+++ b/src/mm-iface-modem-3gpp.c
@@ -1829,6 +1829,62 @@
 
 /*****************************************************************************/
 
+static GVariant *
+profiles_build_result (const GList *profiles)
+{
+    const GList *l;
+    GVariantBuilder builder;
+
+    g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}"));
+
+    for (l = profiles; l; l = g_list_next (l)) {
+        const MM3gppProfile *profile = l->data;
+
+        g_variant_builder_open (&builder, G_VARIANT_TYPE ("a{sv}"));
+
+        g_variant_builder_add (&builder, "{sv}",
+                               "profile-id", g_variant_new_uint32 (profile->profile_id));
+        if (profile->apn) {
+            g_variant_builder_add (&builder, "{sv}",
+                                   "apn", g_variant_new_string (profile->apn));
+        } else {
+            g_variant_builder_add (&builder, "{sv}", "apn", g_variant_new_string (""));
+        }
+        g_variant_builder_add (&builder, "{sv}",
+                               "auth-type", g_variant_new_uint32 (profile->auth_type));
+        if (profile->username)
+            g_variant_builder_add (&builder, "{sv}",
+                                   "username", g_variant_new_string (profile->username));
+        if (profile->password)
+            g_variant_builder_add (&builder, "{sv}",
+                                   "password", g_variant_new_string (profile->password));
+        g_variant_builder_close (&builder);
+    }
+
+    return g_variant_ref_sink (g_variant_builder_end (&builder));
+}
+
+void
+mm_iface_modem_3gpp_update_profiles (MMIfaceModem3gpp *self,
+                                     const GList *profiles)
+{
+    MmGdbusModem3gpp *skeleton = NULL;
+    GVariant *variant;
+
+    g_object_get (self,
+                  MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &skeleton,
+                  NULL);
+    if (!skeleton)
+        return;
+
+    variant = profiles_build_result (profiles);
+    mm_gdbus_modem3gpp_set_profiles (skeleton, variant);
+    g_variant_unref (variant);
+    g_object_unref (skeleton);
+}
+
+/*****************************************************************************/
+
 typedef struct _DisablingContext DisablingContext;
 static void interface_disabling_step (GTask *task);
 
@@ -2043,6 +2099,7 @@
     ENABLING_STEP_SETUP_UNSOLICITED_REGISTRATION_EVENTS,
     ENABLING_STEP_ENABLE_UNSOLICITED_REGISTRATION_EVENTS,
     ENABLING_STEP_INITIAL_EPS_BEARER,
+    ENABLING_STEP_LOAD_PROFILES,
     ENABLING_STEP_LAST
 } EnablingStep;
 
@@ -2197,6 +2254,34 @@
 }
 
 static void
+load_profiles_ready (MMIfaceModem3gpp *self,
+                     GAsyncResult     *res,
+                     GTask            *task)
+{
+    gboolean         success;
+    GList           *profiles = NULL;
+    EnablingContext *ctx;
+    GError          *error = NULL;
+
+    ctx = g_task_get_task_data (task);
+
+    success = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_profiles_finish (self, res, &profiles, &error);
+    if (!success) {
+        mm_obj_dbg (self, "couldn't load initial profiles: '%s'", error->message);
+        g_error_free (error);
+        goto out;
+    }
+
+    mm_iface_modem_3gpp_update_profiles (self, profiles);
+    mm_3gpp_profile_list_free (profiles);
+
+out:
+    /* Go on to next step */
+    ctx->step++;
+    interface_enabling_step (task);
+}
+
+static void
 interface_enabling_step (GTask *task)
 {
     MMIfaceModem3gpp *self;
@@ -2296,6 +2381,18 @@
         ctx->step++;
     } /* fall through */
 
+    case ENABLING_STEP_LOAD_PROFILES:
+        if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_profiles &&
+            MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_profiles_finish) {
+            MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_profiles (
+                self,
+                (GAsyncReadyCallback)load_profiles_ready,
+                task);
+            return;
+        }
+        /* Fall down to next step */
+        ctx->step++;
+
     case ENABLING_STEP_LAST:
         /* We are done without errors! */
         g_task_return_boolean (task, TRUE);
@@ -2640,6 +2737,7 @@
         mm_gdbus_modem3gpp_set_subscription_state (skeleton, MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN);
         mm_gdbus_modem3gpp_set_pco (skeleton, NULL);
         mm_gdbus_modem3gpp_set_initial_eps_bearer (skeleton, NULL);
+        mm_gdbus_modem3gpp_set_profiles (skeleton, NULL);
 
         /* Bind our RegistrationState property */
         g_object_bind_property (self, MM_IFACE_MODEM_3GPP_REGISTRATION_STATE,
diff --git a/src/mm-iface-modem-3gpp.h b/src/mm-iface-modem-3gpp.h
index 258e5c1..4982029 100644
--- a/src/mm-iface-modem-3gpp.h
+++ b/src/mm-iface-modem-3gpp.h
@@ -234,6 +234,15 @@
     gboolean (* set_initial_eps_bearer_settings_finish) (MMIfaceModem3gpp     *self,
                                                          GAsyncResult         *res,
                                                          GError              **error);
+
+    /* Get profiles or provisioned contexts from the modem as a list of MM3gppProfile */
+    void     (* load_profiles) (MMIfaceModem3gpp         *self,
+                                GAsyncReadyCallback       callback,
+                                gpointer                  user_data);
+    gboolean (* load_profiles_finish) (MMIfaceModem3gpp  *self,
+                                       GAsyncResult      *res,
+                                       GList            **out_list,
+                                       GError           **error);
 };
 
 GType mm_iface_modem_3gpp_get_type (void);
@@ -293,6 +302,8 @@
 void mm_iface_modem_3gpp_update_initial_eps_bearer  (MMIfaceModem3gpp *self,
                                                      MMBearerProperties *properties);
 void mm_iface_modem_3gpp_reload_initial_eps_bearer  (MMIfaceModem3gpp *self);
+void mm_iface_modem_3gpp_update_profiles            (MMIfaceModem3gpp *self,
+                                                     const GList *profiles);
 
 /* Run all registration checks */
 void mm_iface_modem_3gpp_run_registration_checks (MMIfaceModem3gpp *self,
diff --git a/src/mm-modem-helpers-mbim.c b/src/mm-modem-helpers-mbim.c
index e418969..b70f379 100644
--- a/src/mm-modem-helpers-mbim.c
+++ b/src/mm-modem-helpers-mbim.c
@@ -207,6 +207,33 @@
 
 /*****************************************************************************/
 
+GList *
+mm_3gpp_profile_list_from_mbim_provisioned_contexts (
+    const MbimProvisionedContextElement *const *contexts,
+    guint n_contexts)
+{
+    GList *profiles = NULL;
+    guint i;
+
+    for (i = 0; i < n_contexts; i++) {
+        MM3gppProfile *profile;
+
+        profile = g_slice_new0 (MM3gppProfile);
+        profile->profile_id = contexts[i]->context_id;
+        profile->apn = g_strdup (contexts[i]->access_string);
+        profile->username = g_strdup (contexts[i]->user_name);
+        profile->password = g_strdup (contexts[i]->password);
+        profile->auth_type =
+            mm_bearer_allowed_auth_from_mbim_auth_protocol (contexts[i]->auth_protocol);
+
+        profiles = g_list_prepend (profiles, profile);
+    }
+
+    return profiles;
+}
+
+/*****************************************************************************/
+
 GError *
 mm_mobile_equipment_error_from_mbim_nw_error (MbimNwError nw_error)
 {
diff --git a/src/mm-modem-helpers-mbim.h b/src/mm-modem-helpers-mbim.h
index 9cebe95..02c4e6b 100644
--- a/src/mm-modem-helpers-mbim.h
+++ b/src/mm-modem-helpers-mbim.h
@@ -38,6 +38,9 @@
 
 GList *mm_3gpp_network_info_list_from_mbim_providers (const MbimProvider *const *providers, guint n_providers);
 
+GList *mm_3gpp_profile_list_from_mbim_provisioned_contexts (const MbimProvisionedContextElement *const *contexts,
+                                                            guint n_contexts);
+
 GError *mm_mobile_equipment_error_from_mbim_nw_error (MbimNwError nw_error);
 
 MMBearerAllowedAuth mm_bearer_allowed_auth_from_mbim_auth_protocol (MbimAuthProtocol      auth_protocol);
diff --git a/src/mm-modem-helpers-qmi.c b/src/mm-modem-helpers-qmi.c
index e4bc7e2..6ab0adf 100644
--- a/src/mm-modem-helpers-qmi.c
+++ b/src/mm-modem-helpers-qmi.c
@@ -1540,6 +1540,40 @@
 
 /*****************************************************************************/
 
+GList *
+mm_3gpp_profile_list_from_qmi_profile_settings (GList *profiles)
+{
+    GList *mm_profiles = NULL;
+    GList *iter;
+
+    for (iter = profiles; iter; iter = g_list_next (iter)) {
+        QmiMessageWdsGetProfileSettingsOutput *wds_profile;
+        MM3gppProfile *mm_profile;
+        const gchar *str;
+        guint8 context_number;
+        QmiWdsAuthentication auth;
+
+        wds_profile = iter->data;
+        mm_profile = g_slice_new0 (MM3gppProfile);
+        if (qmi_message_wds_get_profile_settings_output_get_apn_name (wds_profile, &str, NULL))
+            mm_profile->apn = g_strdup(str);
+        if (qmi_message_wds_get_profile_settings_output_get_pdp_context_number (wds_profile, &context_number, NULL))
+            mm_profile->profile_id = context_number;
+        if (qmi_message_wds_get_profile_settings_output_get_username (wds_profile, &str, NULL))
+            mm_profile->username = g_strdup(str);
+        if (qmi_message_wds_get_profile_settings_output_get_password (wds_profile, &str, NULL))
+            mm_profile->password = g_strdup(str);
+        if (qmi_message_wds_get_profile_settings_output_get_authentication (wds_profile, &auth, NULL))
+            mm_profile->auth_type = mm_bearer_allowed_auth_from_qmi_authentication (auth);
+
+        mm_profiles = g_list_prepend (mm_profiles, mm_profile);
+    }
+
+    return mm_profiles;
+}
+
+/*****************************************************************************/
+
 /**
  * The only case where we need to apply some logic to decide what the current
  * capabilities are is when we have a multimode CDMA/EVDO+GSM/UMTS device, in
diff --git a/src/mm-modem-helpers-qmi.h b/src/mm-modem-helpers-qmi.h
index 829ac76..54d23ac 100644
--- a/src/mm-modem-helpers-qmi.h
+++ b/src/mm-modem-helpers-qmi.h
@@ -120,6 +120,9 @@
 gboolean             mm_bearer_ip_family_to_qmi_pdp_type            (MMBearerIpFamily  ip_family,
                                                                      QmiWdsPdpType    *out_pdp_type);
 
+/* Input is a GList of QmiMessageWdsGetProfileSettingsOutput. */
+GList *mm_3gpp_profile_list_from_qmi_profile_settings (GList *profile_list);
+
 /*****************************************************************************/
 /* QMI/OMA to MM translations */
 
diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c
index b6b7f11..7ff7484 100644
--- a/src/mm-modem-helpers.c
+++ b/src/mm-modem-helpers.c
@@ -1771,6 +1771,41 @@
     return list;
 }
 
+static void
+mm_3gpp_profile_free (MM3gppProfile *profile)
+{
+    g_free (profile->apn);
+    g_free (profile->username);
+    g_free (profile->password);
+    g_slice_free (MM3gppProfile, profile);
+}
+
+void
+mm_3gpp_profile_list_free (GList *list)
+{
+    g_list_free_full (list, (GDestroyNotify) mm_3gpp_profile_free);
+}
+
+static MM3gppProfile *
+mm_3gpp_profile_copy (MM3gppProfile *profile)
+{
+    MM3gppProfile *copy;
+
+    copy = g_slice_new0 (MM3gppProfile);
+    copy->profile_id = profile->profile_id;
+    copy->apn = g_strdup (profile->apn);
+    copy->username = g_strdup (profile->username);
+    copy->password = g_strdup (profile->password);
+    copy->auth_type = profile->auth_type;
+    return copy;
+}
+
+GList *
+mm_3gpp_profile_list_copy (GList *list)
+{
+    return g_list_copy_deep (list, (GCopyFunc)mm_3gpp_profile_copy, NULL);
+}
+
 /*************************************************************************/
 
 static void
diff --git a/src/mm-modem-helpers.h b/src/mm-modem-helpers.h
index 806a8e7..785fd53 100644
--- a/src/mm-modem-helpers.h
+++ b/src/mm-modem-helpers.h
@@ -204,6 +204,16 @@
                                gboolean         *out_cid_reused,
                                gboolean         *out_cid_overwritten);
 
+typedef struct {
+    guint profile_id;
+    gchar *apn;
+    gchar *username;
+    gchar *password;
+    MMBearerAllowedAuth auth_type;
+} MM3gppProfile;
+void mm_3gpp_profile_list_free (GList *profiles);
+GList *mm_3gpp_profile_list_copy (GList *profiles);
+
 /* AT+CGACT? (active PDP context query) response parser */
 typedef struct {
     guint cid;
diff --git a/src/mm-plugin.c b/src/mm-plugin.c
index 13b46a7..20b0124 100644
--- a/src/mm-plugin.c
+++ b/src/mm-plugin.c
@@ -50,7 +50,7 @@
                         G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init))
 
 /* Virtual port corresponding to the embedded modem */
-static const gchar *virtual_port[] = {"smd0", NULL};
+static const gchar *virtual_port[] = {"smd0", "rmnet_data0", "qmi0", NULL};
 
 #define HAS_POST_PROBING_FILTERS(self)          \
     (self->priv->vendor_strings ||              \
@@ -1069,7 +1069,11 @@
                 g_clear_error (&inner_error);
             } else if (!mm_base_modem_grab_port (modem,
                                                  kernel_device,
+#if QMI_QRTR_SUPPORTED //TODO(crbug.com/1103840): Remove hacks before merging to upstream
+                                                 MM_PORT_TYPE_QMI,
+#else
                                                  MM_PORT_TYPE_AT,
+#endif
                                                  MM_PORT_SERIAL_AT_FLAG_NONE,
                                                  &inner_error)) {
                 mm_obj_warn (self, "could not grab virtual port %s: %s",
diff --git a/src/mm-port-qmi.c b/src/mm-port-qmi.c
index e3d99f7..61d42db 100644
--- a/src/mm-port-qmi.c
+++ b/src/mm-port-qmi.c
@@ -228,6 +228,10 @@
     PORT_OPEN_STEP_FIRST,
     PORT_OPEN_STEP_CHECK_OPENING,
     PORT_OPEN_STEP_CHECK_ALREADY_OPEN,
+#if QMI_QRTR_SUPPORTED //TODO(b/118897479): Remove hacks before merging to upstream
+    PORT_OPEN_STEP_OPEN_QRTR_NODE,
+    PORT_OPEN_STEP_WAIT_FOR_SERVICES,
+#endif
     PORT_OPEN_STEP_DEVICE_NEW,
     PORT_OPEN_STEP_OPEN_WITHOUT_DATA_FORMAT,
     PORT_OPEN_STEP_GET_KERNEL_DATA_FORMAT,
@@ -241,6 +245,10 @@
 } PortOpenStep;
 
 typedef struct {
+#if QMI_QRTR_SUPPORTED //TODO(b/118897479): Remove hacks before merging to upstream
+    QrtrBus                     *qrtr_bus;
+    QrtrNode                    *node;
+#endif
     QmiDevice                   *device;
     QmiClient                   *wda;
     GError                      *error;
@@ -261,6 +269,10 @@
                                    3, NULL, NULL, NULL);
     g_clear_object (&ctx->wda);
     g_clear_object (&ctx->device);
+#if QMI_QRTR_SUPPORTED //TODO(b/118897479): Remove hacks before merging to upstream
+    g_clear_object (&ctx->node);
+    g_clear_object (&ctx->qrtr_bus);
+#endif
     g_slice_free (PortOpenContext, ctx);
 }
 
@@ -475,6 +487,68 @@
     port_open_step (task);
 }
 
+#if QMI_QRTR_SUPPORTED //TODO(b/118897479): Remove hacks before merging to upstream
+static void
+qrtr_node_services_ready (QrtrNode *node,
+                          GAsyncResult *res,
+                          GTask *task)
+{
+    PortOpenContext *ctx;
+
+    ctx = g_task_get_task_data (task);
+    if (!qrtr_node_wait_for_services_finish (node, res, &ctx->error)) {
+        /* Error creating the device */
+        ctx->step = PORT_OPEN_STEP_LAST;
+    } else {
+        /* Go on to next step */
+        ctx->step++;
+    }
+    port_open_step (task);
+}
+static void
+qrtr_node_ready (GObject *unused,
+                 GAsyncResult *res,
+                 GTask *task)
+{
+    PortOpenContext *ctx;
+
+    ctx = g_task_get_task_data (task);
+
+    ctx->node = qrtr_bus_wait_for_node_finish (ctx->qrtr_bus, res, &ctx->error);
+    if (!ctx->node)
+        /* Error creating the node */
+        ctx->step = PORT_OPEN_STEP_LAST;
+    else
+        /* Go on to next step */
+        ctx->step++;
+    port_open_step (task);
+}
+
+static void
+qrtr_bus_ready (GObject *unused,
+                GAsyncResult *res,
+                GTask *task)
+{
+    PortOpenContext *ctx;
+
+    ctx = g_task_get_task_data (task);
+
+    ctx->qrtr_bus = qrtr_bus_new_finish (res, &ctx->error);
+    if (!ctx->qrtr_bus) {
+        /* Error creating the bus */
+        ctx->step = PORT_OPEN_STEP_LAST;
+        port_open_step (task);
+    } else {
+        qrtr_bus_wait_for_node (ctx->qrtr_bus,
+                                0,
+                                20,
+                                g_task_get_cancellable (task),
+                                (GAsyncReadyCallback) qrtr_node_ready,
+                                task);
+    }
+}
+#endif
+
 static void
 port_open_step (GTask *task)
 {
@@ -512,7 +586,57 @@
         ctx->step++;
         /* Fall through */
 
+#if QMI_QRTR_SUPPORTED //TODO(b/118897479): Remove hacks before merging to upstream
+    case PORT_OPEN_STEP_OPEN_QRTR_NODE:
+        self->priv->in_progress = TRUE;
+
+        mm_obj_info (self, "Creating QRTR bus and fetching node 0...");
+        qrtr_bus_new (20000,
+                      g_task_get_cancellable (task),
+                      (GAsyncReadyCallback) qrtr_bus_ready,
+                      task);
+
+        return;
+
+    case PORT_OPEN_STEP_WAIT_FOR_SERVICES: {
+        GArray *services;
+        QmiService required_services[] = {
+            QMI_SERVICE_DMS,
+            QMI_SERVICE_WDA,
+            QMI_SERVICE_WDS,
+            QMI_SERVICE_NAS,
+            QMI_SERVICE_UIM,
+            QMI_SERVICE_PDC,
+            QMI_SERVICE_WMS
+        };
+
+        services = g_array_sized_new (FALSE,
+                                      FALSE,
+                                      sizeof (QmiService),
+                                      G_N_ELEMENTS (required_services));
+        g_array_append_vals (services,
+                             required_services,
+                             G_N_ELEMENTS (required_services));
+
+        mm_obj_info (self,"Waiting for services...");
+        qrtr_node_wait_for_services (ctx->node,
+                                     services,
+                                     10000,
+                                     NULL,
+                                     (GAsyncReadyCallback) qrtr_node_services_ready,
+                                     task);
+        return;
+    }
+#endif
+
     case PORT_OPEN_STEP_DEVICE_NEW: {
+#if QMI_QRTR_SUPPORTED //TODO(b/118897479): Remove hacks before merging to upstream
+        mm_obj_info (self, "Creating QMI device from QRTR node...");
+        qmi_device_new_from_node (ctx->node,
+                                  g_task_get_cancellable (task),
+                                  (GAsyncReadyCallback) qmi_device_new_ready,
+                                  task);
+#else
         GFile *file;
         gchar *fullpath;
 
@@ -532,6 +656,7 @@
 
         g_free (fullpath);
         g_object_unref (file);
+#endif
         return;
     }
 
diff --git a/tools/tests/Makefile.am b/tools/tests/Makefile.am
index c91675b..a93ffa9 100644
--- a/tools/tests/Makefile.am
+++ b/tools/tests/Makefile.am
@@ -31,7 +31,9 @@
 	-DTEST_SERVICES=\""$(abs_top_builddir)/tools/tests/services"\" \
 	$(NULL)
 
-TEST_PROGS += $(noinst_PROGRAMS)
+# Disable the test using python3 pygobject and gobject-introspection
+# as they are not ready (b/178312330).
+# TEST_PROGS += $(noinst_PROGRAMS)
 
 test-wrapper.sh: test-wrapper.sh.in
 	@sed \