Merge remote-tracking branch 'cros/upstream' into 'cros/master'

Contains the following commits:
fd052c8e base-sim: don't allow sending PIN/PUK if not required (Aleksander Morgado)
9f192be6 iface-modem: allow loading current required lock info (Aleksander Morgado)
634bb1ca base-sim: avoid using 'self' to refer to the modem (Aleksander Morgado)
b6add181 iface-modem-simple: don't abort connection attempt if SIM-PUK2 locked (Aleksander Morgado)
94025aad base-sim: refactor common PIN/PUK unlock operations (Aleksander Morgado)
[...287 other commits...]
c523d5bd bearer-qmi: plug memleaks when connection attempt fails early (Aleksander Morgado)
260d34be bearer-qmi: plug memleaks during network disconnection (Aleksander Morgado)
7297b3fc mbim: release all allocated CIDs during shutdown (Amol Lad)
913f0d63 ci: require autoconf-archive (Aleksander Morgado)
8f4b5b52 sierra: add port hints for EM7565 modem (Amol Lad)

Cq-Depend: chromium:2063833
Change-Id: I62b8d9392c3da4f72da5076ceab272e75168d19f
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..6f7554b
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+akhouderchah@chromium.org
+ejcaruso@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 b9e9b19..ea05feb 100644
--- a/introspection/org.freedesktop.ModemManager1.Modem.Modem3gpp.xml
+++ b/introspection/org.freedesktop.ModemManager1.Modem.Modem3gpp.xml
@@ -228,5 +228,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/novatel/mm-broadband-modem-novatel-lte.c b/plugins/novatel/mm-broadband-modem-novatel-lte.c
index 6b5d049..17c557b 100644
--- a/plugins/novatel/mm-broadband-modem-novatel-lte.c
+++ b/plugins/novatel/mm-broadband-modem-novatel-lte.c
@@ -166,6 +166,43 @@
 }
 
 /*****************************************************************************/
+/* Load current capabilities (Modem interface) */
+
+static MMModemCapability
+load_current_capabilities_finish (MMIfaceModem *self,
+                                  GAsyncResult *res,
+                                  GError **error)
+{
+    MMModemCapability caps;
+    gchar *caps_str;
+
+    /* Constrain the modem capabilities to LTE only.
+     * TODO(benchan): Remove this constraint. */
+    caps = MM_MODEM_CAPABILITY_LTE;
+    caps_str = mm_modem_capability_build_string_from_mask (caps);
+    mm_dbg ("loaded current capabilities: %s", caps_str);
+    g_free (caps_str);
+    return caps;
+}
+
+static void
+load_current_capabilities (MMIfaceModem *self,
+                           GAsyncReadyCallback callback,
+                           gpointer user_data)
+{
+    GSimpleAsyncResult *result;
+
+    mm_dbg ("loading (Novatel LTE) current capabilities...");
+
+    result = g_simple_async_result_new (G_OBJECT (self),
+                                        callback,
+                                        user_data,
+                                        load_current_capabilities);
+    g_simple_async_result_complete_in_idle (result);
+    g_object_unref (result);
+}
+
+/*****************************************************************************/
 /* Load own numbers (Modem interface) */
 
 static GStrv
@@ -629,6 +666,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,
@@ -643,6 +723,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);
 }
 
@@ -662,6 +747,8 @@
     iface->create_sim_finish = modem_create_sim_finish;
     iface->modem_after_sim_unlock = modem_after_sim_unlock;
     iface->modem_after_sim_unlock_finish = modem_after_sim_unlock_finish;
+    iface->load_current_capabilities = load_current_capabilities;
+    iface->load_current_capabilities_finish = load_current_capabilities_finish;
     iface->load_own_numbers = load_own_numbers;
     iface->load_own_numbers_finish = load_own_numbers_finish;
     iface->load_supported_bands = load_supported_bands;
@@ -689,4 +776,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-broadband-modem-mbim.c b/src/mm-broadband-modem-mbim.c
index a6b6ec5..ba129dc 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;
 
 struct _MMBroadbandModemMbimPrivate {
@@ -3134,6 +3135,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);
 
@@ -3189,6 +3214,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;
@@ -3451,7 +3480,7 @@
     if (!device)
         return;
 
-    mm_dbg ("Supported notifications: signal (%s), registration (%s), sms (%s), connect (%s), subscriber (%s), packet (%s), pco (%s), ussd (%s), lte attach status (%s)",
+    mm_dbg ("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",
@@ -3460,7 +3489,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 */
@@ -3536,6 +3566,7 @@
     if (is_sim_hot_swap_configured)
         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)
@@ -3554,6 +3585,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)
@@ -3632,7 +3664,7 @@
     if (!peek_device (self, &device, callback, user_data))
         return;
 
-    mm_dbg ("Enabled notifications: signal (%s), registration (%s), sms (%s), connect (%s), subscriber (%s), packet (%s), pco (%s), ussd (%s), lte attach status (%s)",
+    mm_dbg ("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",
@@ -3641,7 +3673,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);
 
@@ -3650,11 +3683,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)
@@ -3665,6 +3699,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++;
     }
 
@@ -3853,6 +3889,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)
@@ -3871,6 +3908,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)
@@ -4260,6 +4298,76 @@
 }
 
 /*****************************************************************************/
+/* Load profiles (3GPP interface) */
+
+static GList *
+modem_3gpp_load_profiles_finish (MMIfaceModem3gpp *self,
+                                 GAsyncResult *res,
+                                 GError **error)
+{
+    return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+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_return_pointer (task, profiles, (GDestroyNotify)mm_3gpp_profile_list_free);
+    } 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_dbg ("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
@@ -5505,6 +5613,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 7c6f4eb..b6ffb7d 100644
--- a/src/mm-broadband-modem-qmi.c
+++ b/src/mm-broadband-modem-qmi.c
@@ -2547,6 +2547,213 @@
 }
 
 /*****************************************************************************/
+/* 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 GList *
+modem_3gpp_load_profiles_finish (MMIfaceModem3gpp *self,
+                                 GAsyncResult *res,
+                                 GError **error)
+{
+    return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+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_return_pointer (task,
+                               mm_3gpp_profile_list_from_qmi_profile_settings (ctx->profiles),
+                               (GDestroyNotify) mm_3gpp_profile_list_free);
+        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,
+                                     NULL);
+    qmi_message_wds_get_profile_list_input_unref (input);
+}
+
+/*****************************************************************************/
 /* Registration checks (3GPP interface) */
 
 static gboolean
@@ -8848,6 +9055,8 @@
     iface->load_operator_code_finish = modem_3gpp_load_operator_code_finish;
     iface->load_operator_name = modem_3gpp_load_operator_name;
     iface->load_operator_name_finish = modem_3gpp_load_operator_name_finish;
+    iface->load_profiles = modem_3gpp_load_profiles;
+    iface->load_profiles_finish = modem_3gpp_load_profiles_finish;
 }
 
 static void
diff --git a/src/mm-iface-modem-3gpp.c b/src/mm-iface-modem-3gpp.c
index 3e04f95..0d07b89 100644
--- a/src/mm-iface-modem-3gpp.c
+++ b/src/mm-iface-modem-3gpp.c
@@ -1728,6 +1728,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);
 
@@ -1942,6 +1998,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;
 
@@ -2096,6 +2153,33 @@
 }
 
 static void
+load_profiles_ready (MMIfaceModem3gpp *self,
+                     GAsyncResult     *res,
+                     GTask            *task)
+{
+    GList           *profiles;
+    EnablingContext *ctx;
+    GError          *error = NULL;
+
+    ctx = g_task_get_task_data (task);
+
+    profiles = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_profiles_finish (self, res, &error);
+    if (error) {
+        mm_dbg ("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;
@@ -2195,6 +2279,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);
@@ -2539,6 +2635,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 91976fd..9cb1f4d 100644
--- a/src/mm-iface-modem-3gpp.h
+++ b/src/mm-iface-modem-3gpp.h
@@ -231,6 +231,14 @@
     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);
+    GList *   (* load_profiles_finish) (MMIfaceModem3gpp  *self,
+                                        GAsyncResult      *res,
+                                        GError           **error);
 };
 
 GType mm_iface_modem_3gpp_get_type (void);
@@ -286,6 +294,8 @@
                                                      const GList *pco_list);
 void mm_iface_modem_3gpp_update_initial_eps_bearer  (MMIfaceModem3gpp *self,
                                                      MMBearerProperties *properties);
+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 eb68467..e1adcca 100644
--- a/src/mm-modem-helpers-mbim.c
+++ b/src/mm-modem-helpers-mbim.c
@@ -198,6 +198,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 a648cac..090b764 100644
--- a/src/mm-modem-helpers-mbim.h
+++ b/src/mm-modem-helpers-mbim.h
@@ -37,6 +37,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 baf6f3d..e4a3c58 100644
--- a/src/mm-modem-helpers-qmi.c
+++ b/src/mm-modem-helpers-qmi.c
@@ -1438,6 +1438,54 @@
     return out;
 }
 
+MMBearerAllowedAuth
+mm_bearer_allowed_auth_from_qmi_authentication (QmiWdsAuthentication auth)
+{
+    MMBearerAllowedAuth out;
+
+    out = MM_BEARER_ALLOWED_AUTH_NONE;
+    if (auth & QMI_WDS_AUTHENTICATION_PAP)
+        out |= MM_BEARER_ALLOWED_AUTH_PAP;
+    if (auth & QMI_WDS_AUTHENTICATION_CHAP)
+        out |= MM_BEARER_ALLOWED_AUTH_CHAP;
+
+    return out;
+}
+
+/*****************************************************************************/
+
+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;
+}
+
 /*****************************************************************************/
 
 /**
diff --git a/src/mm-modem-helpers-qmi.h b/src/mm-modem-helpers-qmi.h
index 5c0200e..29b61af 100644
--- a/src/mm-modem-helpers-qmi.h
+++ b/src/mm-modem-helpers-qmi.h
@@ -107,6 +107,10 @@
 /* QMI/WDS to MM translations */
 
 QmiWdsAuthentication mm_bearer_allowed_auth_to_qmi_authentication (MMBearerAllowedAuth auth);
+MMBearerAllowedAuth mm_bearer_allowed_auth_from_qmi_authentication (QmiWdsAuthentication auth);
+
+/* 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 15fba35..7761d01 100644
--- a/src/mm-modem-helpers.c
+++ b/src/mm-modem-helpers.c
@@ -1846,6 +1846,21 @@
     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 void
diff --git a/src/mm-modem-helpers.h b/src/mm-modem-helpers.h
index 9c1be81..2ad24fa 100644
--- a/src/mm-modem-helpers.h
+++ b/src/mm-modem-helpers.h
@@ -201,6 +201,15 @@
                                gboolean         *cid_reused,
                                gboolean         *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);
+
 /* AT+CGACT? (active PDP context query) response parser */
 typedef struct {
     guint cid;