aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am2
-rw-r--r--src/mm-broadband-modem.c132
-rw-r--r--src/mm-iface-modem-3gpp-profile-manager.c1673
-rw-r--r--src/mm-iface-modem-3gpp-profile-manager.h248
4 files changed, 2025 insertions, 30 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 37b0932a..9ac762c5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -346,6 +346,8 @@ ModemManager_SOURCES = \
mm-iface-modem.c \
mm-iface-modem-3gpp.h \
mm-iface-modem-3gpp.c \
+ mm-iface-modem-3gpp-profile-manager.h \
+ mm-iface-modem-3gpp-profile-manager.c \
mm-iface-modem-3gpp-ussd.h \
mm-iface-modem-3gpp-ussd.c \
mm-iface-modem-cdma.h \
diff --git a/src/mm-broadband-modem.c b/src/mm-broadband-modem.c
index 869f47c3..6f5be885 100644
--- a/src/mm-broadband-modem.c
+++ b/src/mm-broadband-modem.c
@@ -32,6 +32,7 @@
#include "mm-broadband-modem.h"
#include "mm-iface-modem.h"
#include "mm-iface-modem-3gpp.h"
+#include "mm-iface-modem-3gpp-profile-manager.h"
#include "mm-iface-modem-3gpp-ussd.h"
#include "mm-iface-modem-cdma.h"
#include "mm-iface-modem-simple.h"
@@ -60,6 +61,7 @@
static void iface_modem_init (MMIfaceModem *iface);
static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
+static void iface_modem_3gpp_profile_manager_init (MMIfaceModem3gppProfileManager *iface);
static void iface_modem_3gpp_ussd_init (MMIfaceModem3gppUssd *iface);
static void iface_modem_cdma_init (MMIfaceModemCdma *iface);
static void iface_modem_simple_init (MMIfaceModemSimple *iface);
@@ -74,6 +76,7 @@ static void iface_modem_firmware_init (MMIfaceModemFirmware *iface);
G_DEFINE_TYPE_EXTENDED (MMBroadbandModem, mm_broadband_modem, MM_TYPE_BASE_MODEM, 0,
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP_PROFILE_MANAGER, iface_modem_3gpp_profile_manager_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP_USSD, iface_modem_3gpp_ussd_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_CDMA, iface_modem_cdma_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_SIMPLE, iface_modem_simple_init)
@@ -89,6 +92,7 @@ enum {
PROP_0,
PROP_MODEM_DBUS_SKELETON,
PROP_MODEM_3GPP_DBUS_SKELETON,
+ PROP_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON,
PROP_MODEM_3GPP_USSD_DBUS_SKELETON,
PROP_MODEM_CDMA_DBUS_SKELETON,
PROP_MODEM_SIMPLE_DBUS_SKELETON,
@@ -189,6 +193,10 @@ struct _MMBroadbandModemPrivate {
MMModem3gppFacility modem_3gpp_ignored_facility_locks;
MMBaseBearer *modem_3gpp_initial_eps_bearer;
+ /*<--- Modem 3GPP Profile Manager interface --->*/
+ /* Properties */
+ GObject *modem_3gpp_profile_manager_dbus_skeleton;
+
/*<--- Modem 3GPP USSD interface --->*/
/* Properties */
GObject *modem_3gpp_ussd_dbus_skeleton;
@@ -10510,6 +10518,7 @@ typedef enum {
DISABLING_STEP_IFACE_LOCATION,
DISABLING_STEP_IFACE_CDMA,
DISABLING_STEP_IFACE_3GPP_USSD,
+ DISABLING_STEP_IFACE_3GPP_PROFILE_MANAGER,
DISABLING_STEP_IFACE_3GPP,
DISABLING_STEP_IFACE_MODEM,
DISABLING_STEP_LAST,
@@ -10585,16 +10594,17 @@ common_disable_finish (MMBroadbandModem *self,
disabling_step (task); \
}
-INTERFACE_DISABLE_READY_FN (iface_modem, MM_IFACE_MODEM, TRUE)
-INTERFACE_DISABLE_READY_FN (iface_modem_3gpp, MM_IFACE_MODEM_3GPP, TRUE)
-INTERFACE_DISABLE_READY_FN (iface_modem_3gpp_ussd, MM_IFACE_MODEM_3GPP_USSD, FALSE)
-INTERFACE_DISABLE_READY_FN (iface_modem_cdma, MM_IFACE_MODEM_CDMA, TRUE)
-INTERFACE_DISABLE_READY_FN (iface_modem_location, MM_IFACE_MODEM_LOCATION, FALSE)
-INTERFACE_DISABLE_READY_FN (iface_modem_messaging, MM_IFACE_MODEM_MESSAGING, FALSE)
-INTERFACE_DISABLE_READY_FN (iface_modem_voice, MM_IFACE_MODEM_VOICE, FALSE)
-INTERFACE_DISABLE_READY_FN (iface_modem_signal, MM_IFACE_MODEM_SIGNAL, FALSE)
-INTERFACE_DISABLE_READY_FN (iface_modem_time, MM_IFACE_MODEM_TIME, FALSE)
-INTERFACE_DISABLE_READY_FN (iface_modem_oma, MM_IFACE_MODEM_OMA, FALSE)
+INTERFACE_DISABLE_READY_FN (iface_modem, MM_IFACE_MODEM, TRUE)
+INTERFACE_DISABLE_READY_FN (iface_modem_3gpp, MM_IFACE_MODEM_3GPP, TRUE)
+INTERFACE_DISABLE_READY_FN (iface_modem_3gpp_ussd, MM_IFACE_MODEM_3GPP_USSD, FALSE)
+INTERFACE_DISABLE_READY_FN (iface_modem_3gpp_profile_manager, MM_IFACE_MODEM_3GPP_PROFILE_MANAGER, FALSE)
+INTERFACE_DISABLE_READY_FN (iface_modem_cdma, MM_IFACE_MODEM_CDMA, TRUE)
+INTERFACE_DISABLE_READY_FN (iface_modem_location, MM_IFACE_MODEM_LOCATION, FALSE)
+INTERFACE_DISABLE_READY_FN (iface_modem_messaging, MM_IFACE_MODEM_MESSAGING, FALSE)
+INTERFACE_DISABLE_READY_FN (iface_modem_voice, MM_IFACE_MODEM_VOICE, FALSE)
+INTERFACE_DISABLE_READY_FN (iface_modem_signal, MM_IFACE_MODEM_SIGNAL, FALSE)
+INTERFACE_DISABLE_READY_FN (iface_modem_time, MM_IFACE_MODEM_TIME, FALSE)
+INTERFACE_DISABLE_READY_FN (iface_modem_oma, MM_IFACE_MODEM_OMA, FALSE)
static void
bearer_list_disconnect_all_bearers_ready (MMBearerList *list,
@@ -10813,6 +10823,17 @@ disabling_step (GTask *task)
ctx->step++;
/* fall through */
+ case DISABLING_STEP_IFACE_3GPP_PROFILE_MANAGER:
+ if (ctx->self->priv->modem_3gpp_profile_manager_dbus_skeleton) {
+ mm_obj_dbg (ctx->self, "modem has 3GPP profile management capabilities, disabling the Modem 3GPP Profile Manager interface...");
+ mm_iface_modem_3gpp_profile_manager_disable (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (ctx->self),
+ (GAsyncReadyCallback)iface_modem_3gpp_profile_manager_disable_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
case DISABLING_STEP_IFACE_3GPP:
if (ctx->self->priv->modem_3gpp_dbus_skeleton) {
mm_obj_dbg (ctx->self, "modem has 3GPP capabilities, disabling the Modem 3GPP interface...");
@@ -10930,6 +10951,7 @@ typedef enum {
ENABLING_STEP_STARTED,
ENABLING_STEP_IFACE_MODEM,
ENABLING_STEP_IFACE_3GPP,
+ ENABLING_STEP_IFACE_3GPP_PROFILE_MANAGER,
ENABLING_STEP_IFACE_3GPP_USSD,
ENABLING_STEP_IFACE_CDMA,
ENABLING_STEP_IFACE_LOCATION,
@@ -11028,16 +11050,17 @@ enable_failed_ready (MMBroadbandModem *self,
enabling_step (task); \
}
-INTERFACE_ENABLE_READY_FN (iface_modem, MM_IFACE_MODEM, TRUE)
-INTERFACE_ENABLE_READY_FN (iface_modem_3gpp, MM_IFACE_MODEM_3GPP, TRUE)
-INTERFACE_ENABLE_READY_FN (iface_modem_3gpp_ussd, MM_IFACE_MODEM_3GPP_USSD, FALSE)
-INTERFACE_ENABLE_READY_FN (iface_modem_cdma, MM_IFACE_MODEM_CDMA, TRUE)
-INTERFACE_ENABLE_READY_FN (iface_modem_location, MM_IFACE_MODEM_LOCATION, FALSE)
-INTERFACE_ENABLE_READY_FN (iface_modem_messaging, MM_IFACE_MODEM_MESSAGING, FALSE)
-INTERFACE_ENABLE_READY_FN (iface_modem_voice, MM_IFACE_MODEM_VOICE, FALSE)
-INTERFACE_ENABLE_READY_FN (iface_modem_signal, MM_IFACE_MODEM_SIGNAL, FALSE)
-INTERFACE_ENABLE_READY_FN (iface_modem_time, MM_IFACE_MODEM_TIME, FALSE)
-INTERFACE_ENABLE_READY_FN (iface_modem_oma, MM_IFACE_MODEM_OMA, FALSE)
+INTERFACE_ENABLE_READY_FN (iface_modem, MM_IFACE_MODEM, TRUE)
+INTERFACE_ENABLE_READY_FN (iface_modem_3gpp, MM_IFACE_MODEM_3GPP, TRUE)
+INTERFACE_ENABLE_READY_FN (iface_modem_3gpp_profile_manager, MM_IFACE_MODEM_3GPP_PROFILE_MANAGER, FALSE)
+INTERFACE_ENABLE_READY_FN (iface_modem_3gpp_ussd, MM_IFACE_MODEM_3GPP_USSD, FALSE)
+INTERFACE_ENABLE_READY_FN (iface_modem_cdma, MM_IFACE_MODEM_CDMA, TRUE)
+INTERFACE_ENABLE_READY_FN (iface_modem_location, MM_IFACE_MODEM_LOCATION, FALSE)
+INTERFACE_ENABLE_READY_FN (iface_modem_messaging, MM_IFACE_MODEM_MESSAGING, FALSE)
+INTERFACE_ENABLE_READY_FN (iface_modem_voice, MM_IFACE_MODEM_VOICE, FALSE)
+INTERFACE_ENABLE_READY_FN (iface_modem_signal, MM_IFACE_MODEM_SIGNAL, FALSE)
+INTERFACE_ENABLE_READY_FN (iface_modem_time, MM_IFACE_MODEM_TIME, FALSE)
+INTERFACE_ENABLE_READY_FN (iface_modem_oma, MM_IFACE_MODEM_OMA, FALSE)
static void
enabling_started_ready (MMBroadbandModem *self,
@@ -11154,6 +11177,17 @@ enabling_step (GTask *task)
ctx->step++;
/* fall through */
+ case ENABLING_STEP_IFACE_3GPP_PROFILE_MANAGER:
+ if (ctx->self->priv->modem_3gpp_profile_manager_dbus_skeleton) {
+ mm_obj_dbg (ctx->self, "modem has 3GPP profile management capabilities, enabling the Modem 3GPP Profile Manager interface...");
+ mm_iface_modem_3gpp_profile_manager_enable (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (ctx->self),
+ (GAsyncReadyCallback)iface_modem_3gpp_profile_manager_enable_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
case ENABLING_STEP_IFACE_3GPP_USSD:
if (ctx->self->priv->modem_3gpp_ussd_dbus_skeleton) {
mm_obj_dbg (ctx->self, "modem has 3GPP/USSD capabilities, enabling the Modem 3GPP/USSD interface...");
@@ -11364,6 +11398,7 @@ typedef enum {
INITIALIZE_STEP_SETUP_SIMPLE_STATUS,
INITIALIZE_STEP_IFACE_MODEM,
INITIALIZE_STEP_IFACE_3GPP,
+ INITIALIZE_STEP_IFACE_3GPP_PROFILE_MANAGER,
INITIALIZE_STEP_IFACE_3GPP_USSD,
INITIALIZE_STEP_IFACE_CDMA,
INITIALIZE_STEP_IFACE_LOCATION,
@@ -11549,16 +11584,17 @@ iface_modem_initialize_ready (MMBroadbandModem *self,
initialize_step (task); \
}
-INTERFACE_INIT_READY_FN (iface_modem_3gpp, MM_IFACE_MODEM_3GPP, TRUE)
-INTERFACE_INIT_READY_FN (iface_modem_3gpp_ussd, MM_IFACE_MODEM_3GPP_USSD, FALSE)
-INTERFACE_INIT_READY_FN (iface_modem_cdma, MM_IFACE_MODEM_CDMA, TRUE)
-INTERFACE_INIT_READY_FN (iface_modem_location, MM_IFACE_MODEM_LOCATION, FALSE)
-INTERFACE_INIT_READY_FN (iface_modem_messaging, MM_IFACE_MODEM_MESSAGING, FALSE)
-INTERFACE_INIT_READY_FN (iface_modem_voice, MM_IFACE_MODEM_VOICE, FALSE)
-INTERFACE_INIT_READY_FN (iface_modem_time, MM_IFACE_MODEM_TIME, FALSE)
-INTERFACE_INIT_READY_FN (iface_modem_signal, MM_IFACE_MODEM_SIGNAL, FALSE)
-INTERFACE_INIT_READY_FN (iface_modem_oma, MM_IFACE_MODEM_OMA, FALSE)
-INTERFACE_INIT_READY_FN (iface_modem_firmware, MM_IFACE_MODEM_FIRMWARE, FALSE)
+INTERFACE_INIT_READY_FN (iface_modem_3gpp, MM_IFACE_MODEM_3GPP, TRUE)
+INTERFACE_INIT_READY_FN (iface_modem_3gpp_profile_manager, MM_IFACE_MODEM_3GPP_PROFILE_MANAGER, FALSE)
+INTERFACE_INIT_READY_FN (iface_modem_3gpp_ussd, MM_IFACE_MODEM_3GPP_USSD, FALSE)
+INTERFACE_INIT_READY_FN (iface_modem_cdma, MM_IFACE_MODEM_CDMA, TRUE)
+INTERFACE_INIT_READY_FN (iface_modem_location, MM_IFACE_MODEM_LOCATION, FALSE)
+INTERFACE_INIT_READY_FN (iface_modem_messaging, MM_IFACE_MODEM_MESSAGING, FALSE)
+INTERFACE_INIT_READY_FN (iface_modem_voice, MM_IFACE_MODEM_VOICE, FALSE)
+INTERFACE_INIT_READY_FN (iface_modem_time, MM_IFACE_MODEM_TIME, FALSE)
+INTERFACE_INIT_READY_FN (iface_modem_signal, MM_IFACE_MODEM_SIGNAL, FALSE)
+INTERFACE_INIT_READY_FN (iface_modem_oma, MM_IFACE_MODEM_OMA, FALSE)
+INTERFACE_INIT_READY_FN (iface_modem_firmware, MM_IFACE_MODEM_FIRMWARE, FALSE)
static void
initialize_step (GTask *task)
@@ -11625,6 +11661,17 @@ initialize_step (GTask *task)
ctx->step++;
/* fall through */
+ case INITIALIZE_STEP_IFACE_3GPP_PROFILE_MANAGER:
+ if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (ctx->self))) {
+ /* Initialize the 3GPP Profile Manager interface */
+ mm_iface_modem_3gpp_profile_manager_initialize (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (ctx->self),
+ (GAsyncReadyCallback)iface_modem_3gpp_profile_manager_initialize_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
case INITIALIZE_STEP_IFACE_3GPP_USSD:
if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (ctx->self))) {
/* Initialize the 3GPP/USSD interface */
@@ -11809,6 +11856,7 @@ sim_hot_swap_enabled:
* emergency voice calls.
*/
mm_iface_modem_3gpp_shutdown (MM_IFACE_MODEM_3GPP (ctx->self));
+ mm_iface_modem_3gpp_profile_manager_shutdown (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (ctx->self));
mm_iface_modem_3gpp_ussd_shutdown (MM_IFACE_MODEM_3GPP_USSD (ctx->self));
mm_iface_modem_cdma_shutdown (MM_IFACE_MODEM_CDMA (ctx->self));
mm_iface_modem_location_shutdown (MM_IFACE_MODEM_LOCATION (ctx->self));
@@ -12002,6 +12050,10 @@ set_property (GObject *object,
g_clear_object (&self->priv->modem_3gpp_dbus_skeleton);
self->priv->modem_3gpp_dbus_skeleton = g_value_dup_object (value);
break;
+ case PROP_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON:
+ g_clear_object (&self->priv->modem_3gpp_profile_manager_dbus_skeleton);
+ self->priv->modem_3gpp_profile_manager_dbus_skeleton = g_value_dup_object (value);
+ break;
case PROP_MODEM_3GPP_USSD_DBUS_SKELETON:
g_clear_object (&self->priv->modem_3gpp_ussd_dbus_skeleton);
self->priv->modem_3gpp_ussd_dbus_skeleton = g_value_dup_object (value);
@@ -12160,6 +12212,9 @@ get_property (GObject *object,
case PROP_MODEM_3GPP_DBUS_SKELETON:
g_value_set_object (value, self->priv->modem_3gpp_dbus_skeleton);
break;
+ case PROP_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON:
+ g_value_set_object (value, self->priv->modem_3gpp_profile_manager_dbus_skeleton);
+ break;
case PROP_MODEM_3GPP_USSD_DBUS_SKELETON:
g_value_set_object (value, self->priv->modem_3gpp_ussd_dbus_skeleton);
break;
@@ -12356,6 +12411,11 @@ dispose (GObject *object)
g_clear_object (&self->priv->modem_3gpp_dbus_skeleton);
}
+ if (self->priv->modem_3gpp_profile_manager_dbus_skeleton) {
+ mm_iface_modem_3gpp_profile_manager_shutdown (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (object));
+ g_clear_object (&self->priv->modem_3gpp_profile_manager_dbus_skeleton);
+ }
+
if (self->priv->modem_3gpp_ussd_dbus_skeleton) {
mm_iface_modem_3gpp_ussd_shutdown (MM_IFACE_MODEM_3GPP_USSD (object));
g_clear_object (&self->priv->modem_3gpp_ussd_dbus_skeleton);
@@ -12516,6 +12576,14 @@ iface_modem_3gpp_init (MMIfaceModem3gpp *iface)
}
static void
+iface_modem_3gpp_profile_manager_init (MMIfaceModem3gppProfileManager *iface)
+{
+ /* Initialization steps */
+ iface->check_support = NULL;
+ iface->check_support_finish = NULL;
+}
+
+static void
iface_modem_3gpp_ussd_init (MMIfaceModem3gppUssd *iface)
{
/* Initialization steps */
@@ -12723,6 +12791,10 @@ mm_broadband_modem_class_init (MMBroadbandModemClass *klass)
MM_IFACE_MODEM_3GPP_DBUS_SKELETON);
g_object_class_override_property (object_class,
+ PROP_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON,
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON);
+
+ g_object_class_override_property (object_class,
PROP_MODEM_3GPP_USSD_DBUS_SKELETON,
MM_IFACE_MODEM_3GPP_USSD_DBUS_SKELETON);
diff --git a/src/mm-iface-modem-3gpp-profile-manager.c b/src/mm-iface-modem-3gpp-profile-manager.c
new file mode 100644
index 00000000..d6eb81c6
--- /dev/null
+++ b/src/mm-iface-modem-3gpp-profile-manager.c
@@ -0,0 +1,1673 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2021 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2021 Google, Inc.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-3gpp.h"
+#include "mm-iface-modem-3gpp-profile-manager.h"
+#include "mm-base-modem.h"
+#include "mm-log-object.h"
+
+#define SUPPORT_CHECKED_TAG "3gpp-ussd-support-checked-tag"
+#define SUPPORTED_TAG "3gpp-ussd-supported-tag"
+
+static GQuark support_checked_quark;
+static GQuark supported_quark;
+
+/*****************************************************************************/
+
+void
+mm_iface_modem_3gpp_profile_manager_bind_simple_status (MMIfaceModem3gppProfileManager *self,
+ MMSimpleStatus *status)
+{
+ /* Nothing shown in simple status */
+}
+
+/*****************************************************************************/
+
+void
+mm_iface_modem_3gpp_profile_manager_updated (MMIfaceModem3gppProfileManager *self)
+{
+ g_autoptr(MmGdbusModem3gppProfileManagerSkeleton) skeleton = NULL;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON, &skeleton,
+ NULL);
+
+ if (skeleton)
+ mm_gdbus_modem3gpp_profile_manager_emit_updated (MM_GDBUS_MODEM3GPP_PROFILE_MANAGER (skeleton));
+}
+
+static gboolean
+profile_manager_fail_if_connected_bearer (MMIfaceModem3gppProfileManager *self,
+ gint profile_id,
+ GError **error)
+{
+ g_autoptr(MMBearerList) bearer_list = NULL;
+ g_autoptr(MMBaseBearer) bearer = NULL;
+
+ g_assert (profile_id != MM_3GPP_PROFILE_ID_UNKNOWN);
+
+ g_object_get (self, MM_IFACE_MODEM_BEARER_LIST, &bearer_list, NULL);
+ if (bearer_list)
+ bearer = mm_bearer_list_find_by_profile_id (bearer_list, profile_id);
+
+ /* If a bearer is found reporting the profile id we're targeting to use,
+ * it means we have a known connected bearer, and we must abort the
+ * operation right away. */
+ if (bearer) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_CONNECTED,
+ "Cannot use profile %d: found an already connected bearer", profile_id);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* Set profile (3GPP profile management interface) */
+
+typedef enum {
+ SET_PROFILE_STEP_FIRST,
+ SET_PROFILE_STEP_CHECK_FORMAT,
+ SET_PROFILE_STEP_LIST_BEFORE,
+ SET_PROFILE_STEP_SELECT_PROFILE,
+ SET_PROFILE_STEP_CHECK_ACTIVATED_PROFILE,
+ SET_PROFILE_STEP_DEACTIVATE_PROFILE,
+ SET_PROFILE_STEP_STORE_PROFILE,
+ SET_PROFILE_STEP_LIST_AFTER,
+ SET_PROFILE_STEP_LAST,
+} SetProfileStep;
+
+typedef struct {
+ SetProfileStep step;
+ MM3gppProfile *requested;
+ gboolean strict;
+ gboolean new_id;
+ gint min_profile_id;
+ gint max_profile_id;
+ GEqualFunc profile_apn_cmp;
+ MM3gppProfileCmpFlags profile_cmp_flags;
+ gint profile_id;
+ GList *before_list;
+ MM3gppProfile *stored;
+} SetProfileContext;
+
+static void
+set_profile_context_free (SetProfileContext *ctx)
+{
+ mm_3gpp_profile_list_free (ctx->before_list);
+ g_clear_object (&ctx->requested);
+ g_clear_object (&ctx->stored);
+ g_slice_free (SetProfileContext, ctx);
+}
+
+MM3gppProfile *
+mm_iface_modem_3gpp_profile_manager_set_profile_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return MM_3GPP_PROFILE (g_task_propagate_pointer (G_TASK (res), error));
+}
+
+static void set_profile_step (GTask *task);
+
+static void
+profile_manager_get_profile_after_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetProfileContext *ctx;
+ g_autoptr(GError) error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ ctx->stored = mm_iface_modem_3gpp_profile_manager_get_profile_finish (self, res, &error);
+ if (!ctx->stored) {
+ g_prefix_error (&error, "Couldn't validate update of profile '%d': ", ctx->profile_id);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx->step++;
+ set_profile_step (task);
+}
+
+static void
+set_profile_step_list_after (GTask *task)
+{
+ MMIfaceModem3gppProfileManager *self;
+ SetProfileContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ mm_iface_modem_3gpp_profile_manager_get_profile (
+ self,
+ ctx->profile_id,
+ (GAsyncReadyCallback)profile_manager_get_profile_after_ready,
+ task);
+}
+
+static void
+profile_manager_store_profile_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetProfileContext *ctx;
+ GError *error = NULL;
+ gint profile_id;
+
+ ctx = g_task_get_task_data (task);
+
+ profile_id = MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->store_profile_finish (self, res, &error);
+ if (profile_id == MM_3GPP_PROFILE_ID_UNKNOWN) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* when creating a new profile with an unbound input profile id, store the
+ * one received after store */
+ if (ctx->profile_id == MM_3GPP_PROFILE_ID_UNKNOWN)
+ ctx->profile_id = profile_id;
+
+ g_assert (ctx->profile_id == profile_id);
+ mm_obj_dbg (self, "stored profile with id '%d'", ctx->profile_id);
+
+ ctx->step++;
+ set_profile_step (task);
+}
+
+static void
+set_profile_step_store_profile (GTask *task)
+{
+ MMIfaceModem3gppProfileManager *self;
+ SetProfileContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ g_assert (!ctx->stored);
+
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->store_profile (
+ self,
+ ctx->requested,
+ (GAsyncReadyCallback) profile_manager_store_profile_ready,
+ task);
+}
+
+static void
+profile_manager_deactivate_profile_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetProfileContext *ctx;
+ g_autoptr(GError) error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ /* profile deactivation errors aren't fatal per se */
+ if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->deactivate_profile_finish (self, res, &error))
+ mm_obj_dbg (self, "couldn't deactivate profile with id '%d': %s", ctx->profile_id, error->message);
+ else
+ mm_obj_dbg (self, "deactivated profile with id '%d'", ctx->profile_id);
+
+ ctx->step++;
+ set_profile_step (task);
+}
+
+static void
+set_profile_step_deactivate_profile (GTask *task)
+{
+ MMIfaceModem3gppProfileManager *self;
+ SetProfileContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->deactivate_profile ||
+ !MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->deactivate_profile_finish) {
+ mm_obj_dbg (self, "skipping profile deactivation");
+ ctx->step++;
+ set_profile_step (task);
+ return;
+ }
+
+ /* This profile deactivation is EXCLUSIVELY done for those profiles that
+ * are connected in the modem but for which we don't have any connected
+ * bearer tracked. This covers e.g. a clean recovery of a previous daemon
+ * crash, and is now defined as a supported step in the core logic, instead
+ * of doing it differently in the different plugins and protocols. */
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->deactivate_profile (
+ self,
+ ctx->requested,
+ (GAsyncReadyCallback) profile_manager_deactivate_profile_ready,
+ task);
+}
+
+static void
+profile_manager_check_activated_profile_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetProfileContext *ctx;
+ gboolean activated = TRUE;
+ g_autoptr(GError) error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->check_activated_profile_finish (self, res, &activated, &error)) {
+ mm_obj_dbg (self, "couldn't check if profile '%d' is activated: %s", ctx->profile_id, error->message);
+ ctx->step = SET_PROFILE_STEP_DEACTIVATE_PROFILE;
+ }
+ else if (activated) {
+ mm_obj_dbg (self, "profile '%d' is activated", ctx->profile_id);
+ ctx->step = SET_PROFILE_STEP_DEACTIVATE_PROFILE;
+ } else {
+ mm_obj_dbg (self, "profile '%d' is not activated", ctx->profile_id);
+ ctx->step = SET_PROFILE_STEP_STORE_PROFILE;
+ }
+ set_profile_step (task);
+}
+
+static void
+set_profile_step_check_activated_profile (GTask *task)
+{
+ MMIfaceModem3gppProfileManager *self;
+ SetProfileContext *ctx;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ g_assert (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN);
+
+ /* First, a quick check on our own bearer list. If we have a known bearer
+ * connected using the same profile id, we fail the operation right away. */
+ if (!profile_manager_fail_if_connected_bearer (self, ctx->profile_id, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->check_activated_profile ||
+ !MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->check_activated_profile_finish) {
+ ctx->step = SET_PROFILE_STEP_DEACTIVATE_PROFILE;
+ set_profile_step (task);
+ return;
+ }
+
+ /* Second, an actual query to the modem, in order to trigger the profile
+ * deactivation before we attempt to activate it again */
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->check_activated_profile (
+ self,
+ ctx->requested,
+ (GAsyncReadyCallback) profile_manager_check_activated_profile_ready,
+ task);
+}
+
+static void
+set_profile_step_select_profile_exact (GTask *task)
+{
+ MMIfaceModem3gppProfileManager *self;
+ SetProfileContext *ctx;
+ GError *error = NULL;
+ g_autoptr(MM3gppProfile) existing = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ g_assert (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN);
+
+ /* Look for the exact profile we want to use */
+ existing = mm_3gpp_profile_list_find_by_profile_id (ctx->before_list,
+ ctx->profile_id,
+ &error);
+ if (!existing) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* If the profile is 100% equal to what we require, nothing to do */
+ if (mm_3gpp_profile_cmp (existing, ctx->requested, ctx->profile_apn_cmp, ctx->profile_cmp_flags)) {
+ ctx->stored = g_object_ref (existing);
+ mm_obj_dbg (self, "reusing profile '%d'", ctx->profile_id);
+ } else
+ mm_obj_dbg (self, "overwriting profile '%d'", ctx->profile_id);
+
+ ctx->step++;
+ set_profile_step (task);
+}
+
+static void
+set_profile_step_select_profile_new (GTask *task)
+{
+ MMIfaceModem3gppProfileManager *self;
+ SetProfileContext *ctx;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ g_assert (ctx->profile_id == MM_3GPP_PROFILE_ID_UNKNOWN);
+ g_assert (ctx->strict);
+
+ /* If strict set required, fail if we cannot find an empty profile id */
+ ctx->profile_id = mm_3gpp_profile_list_find_empty (ctx->before_list,
+ ctx->min_profile_id,
+ ctx->max_profile_id,
+ &error);
+ if (ctx->profile_id == MM_3GPP_PROFILE_ID_UNKNOWN) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* store profile id in the requested profile */
+ mm_3gpp_profile_set_profile_id (ctx->requested, ctx->profile_id);
+
+ mm_obj_dbg (self, "creating profile '%d'", ctx->profile_id);
+
+ ctx->step++;
+ set_profile_step (task);
+}
+
+static void
+set_profile_step_select_profile_best (GTask *task)
+{
+ MMIfaceModem3gppProfileManager *self;
+ SetProfileContext *ctx;
+ gboolean overwritten = FALSE;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ g_assert (ctx->profile_id == MM_3GPP_PROFILE_ID_UNKNOWN);
+ g_assert (!ctx->strict);
+
+ ctx->profile_id = mm_3gpp_profile_list_find_best (ctx->before_list,
+ ctx->requested,
+ ctx->profile_apn_cmp,
+ ctx->profile_cmp_flags,
+ ctx->min_profile_id,
+ ctx->max_profile_id,
+ self,
+ &ctx->stored,
+ &overwritten);
+
+ /* store profile id in the requested profile */
+ mm_3gpp_profile_set_profile_id (ctx->requested, ctx->profile_id);
+
+ /* If we're reusing an already existing profile, we're done at this
+ * point, no need to create a new one */
+ if (ctx->stored)
+ mm_obj_dbg (self, "reusing profile '%d'", ctx->profile_id);
+ else if (overwritten)
+ mm_obj_dbg (self, "overwriting profile '%d'", ctx->profile_id);
+
+ ctx->step++;
+ set_profile_step (task);
+}
+
+static void
+profile_manager_list_profiles_before_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetProfileContext *ctx;
+ g_autoptr(GError) error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_iface_modem_3gpp_profile_manager_list_profiles_finish (self, res, &ctx->before_list, &error))
+ mm_obj_dbg (self, "failed checking currently defined contexts: %s", error->message);
+
+ ctx->step++;
+ set_profile_step (task);
+}
+
+static void
+set_profile_step_list_before (GTask *task)
+{
+ MMIfaceModem3gppProfileManager *self;
+
+ self = g_task_get_source_object (task);
+ mm_iface_modem_3gpp_profile_manager_list_profiles (
+ self,
+ (GAsyncReadyCallback)profile_manager_list_profiles_before_ready,
+ task);
+}
+
+static void
+set_profile_check_format_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetProfileContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->check_format_finish (
+ self, res,
+ &ctx->new_id,
+ &ctx->min_profile_id,
+ &ctx->max_profile_id,
+ &ctx->profile_apn_cmp,
+ &ctx->profile_cmp_flags,
+ NULL)) {
+ ctx->min_profile_id = 1;
+ ctx->max_profile_id = G_MAXINT-1;
+ mm_obj_dbg (self, "unknown context definition format; using defaults: minimum %d, maximum %d",
+ ctx->min_profile_id, ctx->max_profile_id);
+ } else
+ mm_obj_dbg (self, "context definition format: minimum %d, maximum %d",
+ ctx->min_profile_id, ctx->max_profile_id);
+
+ ctx->step++;
+ set_profile_step (task);
+}
+
+static void
+set_profile_step_check_format (GTask *task)
+{
+ MMIfaceModem3gppProfileManager *self;
+ SetProfileContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->check_format (
+ self,
+ mm_3gpp_profile_get_ip_type (ctx->requested),
+ (GAsyncReadyCallback)set_profile_check_format_ready,
+ task);
+}
+
+static void
+set_profile_step (GTask *task)
+{
+ MMIfaceModem3gppProfileManager *self;
+ SetProfileContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case SET_PROFILE_STEP_FIRST:
+ ctx->step++;
+ /* Fall through */
+
+ case SET_PROFILE_STEP_CHECK_FORMAT:
+ mm_obj_dbg (self, "set profile state (%d/%d): check format",
+ ctx->step, SET_PROFILE_STEP_LAST);
+ set_profile_step_check_format (task);
+ return;
+
+ case SET_PROFILE_STEP_LIST_BEFORE:
+ mm_obj_dbg (self, "set profile state (%d/%d): list before",
+ ctx->step, SET_PROFILE_STEP_LAST);
+ set_profile_step_list_before (task);
+ return;
+
+ case SET_PROFILE_STEP_SELECT_PROFILE:
+ if (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN) {
+ mm_obj_dbg (self, "set profile state (%d/%d): select profile (exact)",
+ ctx->step, SET_PROFILE_STEP_LAST);
+ set_profile_step_select_profile_exact (task);
+ return;
+ }
+ if (!ctx->strict) {
+ mm_obj_dbg (self, "set profile state (%d/%d): select profile (best)",
+ ctx->step, SET_PROFILE_STEP_LAST);
+ set_profile_step_select_profile_best (task);
+ return;
+ }
+ if (ctx->new_id) {
+ mm_obj_dbg (self, "set profile state (%d/%d): select profile (new)",
+ ctx->step, SET_PROFILE_STEP_LAST);
+ set_profile_step_select_profile_new (task);
+ return;
+ }
+
+ mm_obj_dbg (self, "set profile state (%d/%d): select profile (none)",
+ ctx->step, SET_PROFILE_STEP_LAST);
+ ctx->step++;
+ /* Fall through */
+
+ case SET_PROFILE_STEP_CHECK_ACTIVATED_PROFILE:
+ /* If the modem/protocol doesn't allow preselecting the profile id of
+ * a new profile we're going to create, then we won't have a profile id
+ * set at this point. If so, just skip this step. */
+ if (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN) {
+ mm_obj_dbg (self, "set profile state (%d/%d): check activated profile",
+ ctx->step, SET_PROFILE_STEP_LAST);
+ set_profile_step_check_activated_profile (task);
+ return;
+ }
+ ctx->step++;
+ /* Fall through */
+
+ case SET_PROFILE_STEP_DEACTIVATE_PROFILE:
+ /* If the modem/protocol doesn't allow preselecting the profile id of
+ * a new profile we're going to create, then we won't have a profile id
+ * set at this point. If so, just skip this step. */
+ if (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN) {
+ mm_obj_dbg (self, "set profile state (%d/%d): deactivate profile",
+ ctx->step, SET_PROFILE_STEP_LAST);
+ set_profile_step_deactivate_profile (task);
+ return;
+ }
+ ctx->step++;
+ /* Fall through */
+
+ case SET_PROFILE_STEP_STORE_PROFILE:
+ /* if we're reusing an already existing profile, we can jump
+ * to the last step now, there is no need to store any update */
+ if (ctx->stored) {
+ g_assert (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN);
+ mm_obj_dbg (self, "set profile state (%d/%d): profile already stored",
+ ctx->step, SET_PROFILE_STEP_LAST);
+ ctx->step = SET_PROFILE_STEP_LAST;
+ set_profile_step (task);
+ return;
+ }
+ mm_obj_dbg (self, "set profile state (%d/%d): store profile",
+ ctx->step, SET_PROFILE_STEP_LAST);
+ set_profile_step_store_profile (task);
+ return;
+
+ case SET_PROFILE_STEP_LIST_AFTER:
+ mm_obj_dbg (self, "set profile state (%d/%d): list after",
+ ctx->step, SET_PROFILE_STEP_LAST);
+ set_profile_step_list_after (task);
+ return;
+
+ case SET_PROFILE_STEP_LAST:
+ mm_obj_dbg (self, "set profile state (%d/%d): all done",
+ ctx->step, SET_PROFILE_STEP_LAST);
+ g_assert (ctx->stored);
+ g_task_return_pointer (task, g_steal_pointer (&ctx->stored), g_object_unref);
+ g_object_unref (task);
+ return;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+void
+mm_iface_modem_3gpp_profile_manager_set_profile (MMIfaceModem3gppProfileManager *self,
+ MM3gppProfile *requested,
+ gboolean strict,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ SetProfileContext *ctx;
+ MMBearerIpFamily ip_family;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ ctx = g_slice_new0 (SetProfileContext);
+ ctx->step = SET_PROFILE_STEP_FIRST;
+ ctx->requested = g_object_ref (requested);
+ ctx->strict = strict;
+ ctx->profile_id = mm_3gpp_profile_get_profile_id (requested);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)set_profile_context_free);
+
+ /* normalize IP family right away */
+ ip_family = mm_3gpp_profile_get_ip_type (requested);
+ mm_3gpp_normalize_ip_family (&ip_family);
+ mm_3gpp_profile_set_ip_type (requested, ip_family);
+
+ set_profile_step (task);
+}
+
+/*****************************************************************************/
+
+MM3gppProfile *
+mm_iface_modem_3gpp_profile_manager_get_profile_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return MM_3GPP_PROFILE (g_task_propagate_pointer (G_TASK (res), error));
+}
+
+static void
+get_profile_list_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ GList *profiles = NULL;
+ gint profile_id;
+ MM3gppProfile *profile;
+
+ profile_id = GPOINTER_TO_INT (g_task_get_task_data (task));
+
+ if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->list_profiles_finish (self, res, &profiles, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ profile = mm_3gpp_profile_list_find_by_profile_id (profiles, profile_id, &error);
+ mm_3gpp_profile_list_free (profiles);
+
+ if (!profile) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ g_task_return_pointer (task, profile, g_object_unref);
+ g_object_unref (task);
+}
+
+static void
+get_profile_single_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ MM3gppProfile *profile;
+
+ profile = MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->get_profile_finish (self, res, &error);
+ if (!profile)
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, profile, g_object_unref);
+ g_object_unref (task);
+}
+
+void
+mm_iface_modem_3gpp_profile_manager_get_profile (MMIfaceModem3gppProfileManager *self,
+ gint profile_id,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->get_profile &&
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->get_profile_finish) {
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->get_profile (self,
+ profile_id,
+ (GAsyncReadyCallback)get_profile_single_ready,
+ task);
+ return;
+ }
+
+ /* If there is no way to query one single profile, query all and filter */
+ g_task_set_task_data (task, GINT_TO_POINTER (profile_id), NULL);
+
+ mm_iface_modem_3gpp_profile_manager_list_profiles (self,
+ (GAsyncReadyCallback)get_profile_list_ready,
+ task);
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ GList *profiles;
+} ListProfilesContext;
+
+static void
+list_profiles_context_free (ListProfilesContext *ctx)
+{
+ mm_3gpp_profile_list_free (ctx->profiles);
+ g_slice_free (ListProfilesContext, ctx);
+}
+
+gboolean
+mm_iface_modem_3gpp_profile_manager_list_profiles_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GList **out_profiles,
+ GError **error)
+{
+ ListProfilesContext *ctx;
+
+ if (!g_task_propagate_boolean (G_TASK (res), error))
+ return FALSE;
+
+ ctx = g_task_get_task_data (G_TASK (res));
+ if (out_profiles)
+ *out_profiles = g_steal_pointer (&ctx->profiles);
+ return TRUE;
+}
+
+static void
+internal_list_profiles_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ ListProfilesContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_slice_new0 (ListProfilesContext);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) list_profiles_context_free);
+
+ if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->list_profiles_finish (self, res, &ctx->profiles, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_iface_modem_3gpp_profile_manager_list_profiles (MMIfaceModem3gppProfileManager *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Internal calls to the list profile logic may be performed even if the 3GPP Profile Manager
+ * interface is not exposed in DBus, therefore, make sure this logic exits cleanly if there
+ * is no support for listing profiles */
+ if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->list_profiles ||
+ !MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->list_profiles_finish) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Listing profiles is unsupported");
+ g_object_unref (task);
+ return;
+ }
+
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->list_profiles (
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self),
+ (GAsyncReadyCallback)internal_list_profiles_ready,
+ task);
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ MmGdbusModem3gppProfileManager *skeleton;
+ GDBusMethodInvocation *invocation;
+ MMIfaceModem3gppProfileManager *self;
+} HandleListContext;
+
+static void
+handle_list_context_free (HandleListContext *ctx)
+{
+ g_object_unref (ctx->skeleton);
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->self);
+ g_slice_free (HandleListContext, ctx);
+}
+
+static void
+list_profiles_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ HandleListContext *ctx)
+{
+ GVariantBuilder builder;
+ GError *error = NULL;
+ GList *profiles = NULL;
+ GList *l;
+
+ if (!mm_iface_modem_3gpp_profile_manager_list_profiles_finish (self, res, &profiles, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_list_context_free (ctx);
+ return;
+ }
+
+ /* Build array of dicts */
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}"));
+ for (l = profiles; l; l = g_list_next (l)) {
+ g_autoptr(GVariant) dict = NULL;
+
+ dict = mm_3gpp_profile_get_dictionary (MM_3GPP_PROFILE (l->data));
+ g_variant_builder_add_value (&builder, dict);
+ }
+
+ mm_gdbus_modem3gpp_profile_manager_complete_list (ctx->skeleton,
+ ctx->invocation,
+ g_variant_builder_end (&builder));
+ handle_list_context_free (ctx);
+ mm_3gpp_profile_list_free (profiles);
+}
+
+static void
+handle_list_auth_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ HandleListContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_authorize_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_list_context_free (ctx);
+ return;
+ }
+
+ if (mm_iface_modem_abort_invocation_if_state_not_reached (MM_IFACE_MODEM (self),
+ ctx->invocation,
+ MM_MODEM_STATE_ENABLED)) {
+ handle_list_context_free (ctx);
+ return;
+ }
+
+ /* Don't call the class callback directly, use the common helper method
+ * that is also used by other internal operations. */
+ mm_iface_modem_3gpp_profile_manager_list_profiles (
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self),
+ (GAsyncReadyCallback)list_profiles_ready,
+ ctx);
+}
+
+static gboolean
+handle_list (MmGdbusModem3gppProfileManager *skeleton,
+ GDBusMethodInvocation *invocation,
+ MMIfaceModem3gppProfileManager *self)
+{
+ HandleListContext *ctx;
+
+ ctx = g_slice_new0 (HandleListContext);
+ ctx->skeleton = g_object_ref (skeleton);
+ ctx->invocation = g_object_ref (invocation);
+ ctx->self = g_object_ref (self);
+
+ mm_base_modem_authorize (MM_BASE_MODEM (self),
+ invocation,
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ (GAsyncReadyCallback)handle_list_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ MmGdbusModem3gppProfileManager *skeleton;
+ GDBusMethodInvocation *invocation;
+ GVariant *requested_dictionary;
+ MMIfaceModem3gppProfileManager *self;
+} HandleSetContext;
+
+static void
+handle_set_context_free (HandleSetContext *ctx)
+{
+ g_clear_pointer (&ctx->requested_dictionary, g_variant_unref);
+ g_object_unref (ctx->skeleton);
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->self);
+ g_slice_free (HandleSetContext, ctx);
+}
+
+static void
+set_profile_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ HandleSetContext *ctx)
+{
+ GError *error = NULL;
+ g_autoptr(MM3gppProfile) profile_stored = NULL;
+
+ profile_stored = mm_iface_modem_3gpp_profile_manager_set_profile_finish (self, res, &error);
+ if (!profile_stored)
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ else {
+ g_autoptr(GVariant) profile_dictionary = NULL;
+
+ profile_dictionary = mm_3gpp_profile_get_dictionary (profile_stored);
+ mm_gdbus_modem3gpp_profile_manager_complete_set (ctx->skeleton, ctx->invocation, profile_dictionary);
+ }
+ handle_set_context_free (ctx);
+}
+
+static void
+handle_set_auth_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ HandleSetContext *ctx)
+{
+ GError *error = NULL;
+ g_autoptr(MM3gppProfile) profile_requested = NULL;
+
+ if (!mm_base_modem_authorize_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_set_context_free (ctx);
+ return;
+ }
+
+ if (mm_iface_modem_abort_invocation_if_state_not_reached (MM_IFACE_MODEM (self),
+ ctx->invocation,
+ MM_MODEM_STATE_ENABLED)) {
+ handle_set_context_free (ctx);
+ return;
+ }
+
+ if (!ctx->requested_dictionary) {
+ g_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Missing requested profile settings");
+ handle_set_context_free (ctx);
+ return;
+ }
+
+ profile_requested = mm_3gpp_profile_new_from_dictionary (ctx->requested_dictionary, &error);
+ if (!profile_requested) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_set_context_free (ctx);
+ return;
+ }
+
+ /* Don't call the class callback directly, use the common helper method
+ * that is also used by other internal operations. */
+ mm_iface_modem_3gpp_profile_manager_set_profile (
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self),
+ profile_requested,
+ TRUE, /* strict always! */
+ (GAsyncReadyCallback)set_profile_ready,
+ ctx);
+}
+
+static gboolean
+handle_set (MmGdbusModem3gppProfileManager *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *requested_dictionary,
+ MMIfaceModem3gppProfileManager *self)
+{
+ HandleSetContext *ctx;
+
+ ctx = g_slice_new0 (HandleSetContext);
+ ctx->skeleton = g_object_ref (skeleton);
+ ctx->invocation = g_object_ref (invocation);
+ ctx->self = g_object_ref (self);
+ ctx->requested_dictionary = requested_dictionary ? g_variant_ref (requested_dictionary) : NULL;
+
+ mm_base_modem_authorize (MM_BASE_MODEM (self),
+ invocation,
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ (GAsyncReadyCallback)handle_set_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ MmGdbusModem3gppProfileManager *skeleton;
+ GDBusMethodInvocation *invocation;
+ GVariant *dictionary;
+ MMIfaceModem3gppProfileManager *self;
+} HandleDeleteContext;
+
+static void
+handle_delete_context_free (HandleDeleteContext *ctx)
+{
+ g_clear_pointer (&ctx->dictionary, g_variant_unref);
+ g_object_unref (ctx->skeleton);
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->self);
+ g_slice_free (HandleDeleteContext, ctx);
+}
+
+static void
+delete_profile_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ HandleDeleteContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->delete_profile_finish (self, res, &error))
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ else
+ mm_gdbus_modem3gpp_profile_manager_complete_delete (ctx->skeleton, ctx->invocation);
+ handle_delete_context_free (ctx);
+}
+
+static void
+handle_delete_auth_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ HandleDeleteContext *ctx)
+{
+ gint profile_id;
+ GError *error = NULL;
+ g_autoptr(MM3gppProfile) profile = NULL;
+
+ if (!mm_base_modem_authorize_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_delete_context_free (ctx);
+ return;
+ }
+
+ if (mm_iface_modem_abort_invocation_if_state_not_reached (MM_IFACE_MODEM (self),
+ ctx->invocation,
+ MM_MODEM_STATE_ENABLED)) {
+ handle_delete_context_free (ctx);
+ return;
+ }
+
+ if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->delete_profile ||
+ !MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->delete_profile_finish) {
+ g_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Deleting profiles is not supported");
+ handle_delete_context_free (ctx);
+ return;
+ }
+
+ if (!ctx->dictionary) {
+ g_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Missing profile settings");
+ handle_delete_context_free (ctx);
+ return;
+ }
+
+ profile = mm_3gpp_profile_new_from_dictionary (ctx->dictionary, &error);
+ if (!profile) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_delete_context_free (ctx);
+ return;
+ }
+
+ profile_id = mm_3gpp_profile_get_profile_id (profile);
+ if (profile_id == MM_3GPP_PROFILE_ID_UNKNOWN) {
+ g_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Missing 'profile-id' in profile settings");
+ handle_delete_context_free (ctx);
+ return;
+ }
+
+ if (!profile_manager_fail_if_connected_bearer (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self), profile_id, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_delete_context_free (ctx);
+ return;
+ }
+
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->delete_profile (
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self),
+ profile,
+ (GAsyncReadyCallback)delete_profile_ready,
+ ctx);
+}
+
+static gboolean
+handle_delete (MmGdbusModem3gppProfileManager *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *dictionary,
+ MMIfaceModem3gppProfileManager *self)
+{
+ HandleDeleteContext *ctx;
+
+ ctx = g_slice_new0 (HandleDeleteContext);
+ ctx->skeleton = g_object_ref (skeleton);
+ ctx->invocation = g_object_ref (invocation);
+ ctx->self = g_object_ref (self);
+ ctx->dictionary = g_variant_ref (dictionary);
+
+ mm_base_modem_authorize (MM_BASE_MODEM (self),
+ invocation,
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ (GAsyncReadyCallback)handle_delete_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+typedef struct _DisablingContext DisablingContext;
+static void interface_disabling_step (GTask *task);
+
+typedef enum {
+ DISABLING_STEP_FIRST,
+ DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS,
+ DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS,
+ DISABLING_STEP_LAST
+} DisablingStep;
+
+struct _DisablingContext {
+ DisablingStep step;
+ MmGdbusModem3gppProfileManager *skeleton;
+};
+
+static void
+disabling_context_free (DisablingContext *ctx)
+{
+ g_clear_object (&ctx->skeleton);
+ g_slice_free (DisablingContext, ctx);
+}
+
+gboolean
+mm_iface_modem_3gpp_profile_manager_disable_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+disable_unsolicited_events_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ DisablingContext *ctx;
+ g_autoptr(GError) error = NULL;
+
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->disable_unsolicited_events_finish (self, res, &error);
+ if (error) {
+ /* This error shouldn't be treated as critical */
+ mm_obj_dbg (self, "couldn't disable unsolicited profile management events: %s", error->message);
+ }
+
+ /* Go on to next step */
+ ctx = g_task_get_task_data (task);
+ ctx->step++;
+ interface_disabling_step (task);
+}
+
+static void
+cleanup_unsolicited_events_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ DisablingContext *ctx;
+ g_autoptr(GError) error = NULL;
+
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->cleanup_unsolicited_events_finish (self, res, &error);
+ if (error) {
+ /* This error shouldn't be treated as critical */
+ mm_obj_dbg (self, "couldn't cleanup unsolicited profile management events: %s", error->message);
+ }
+
+ /* Go on to next step */
+ ctx = g_task_get_task_data (task);
+ ctx->step++;
+ interface_disabling_step (task);
+}
+
+static void
+interface_disabling_step (GTask *task)
+{
+ MMIfaceModem3gppProfileManager *self;
+ DisablingContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case DISABLING_STEP_FIRST:
+ ctx->step++;
+ /* fall through */
+
+ case DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS:
+ if (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->disable_unsolicited_events &&
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->disable_unsolicited_events_finish) {
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->disable_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)disable_unsolicited_events_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS:
+ if (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->cleanup_unsolicited_events &&
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->cleanup_unsolicited_events_finish) {
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->cleanup_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)cleanup_unsolicited_events_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case DISABLING_STEP_LAST:
+ /* We are done without errors! */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+
+ default:
+ break;
+ }
+
+ g_assert_not_reached ();
+}
+
+void
+mm_iface_modem_3gpp_profile_manager_disable (MMIfaceModem3gppProfileManager *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ DisablingContext *ctx;
+ GTask *task;
+
+ ctx = g_slice_new0 (DisablingContext);
+ ctx->step = DISABLING_STEP_FIRST;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)disabling_context_free);
+
+ g_object_get (self,
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON, &ctx->skeleton,
+ NULL);
+ if (!ctx->skeleton) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't get interface skeleton");
+ g_object_unref (task);
+ return;
+ }
+
+ interface_disabling_step (task);
+}
+
+/*****************************************************************************/
+
+typedef struct _EnablingContext EnablingContext;
+static void interface_enabling_step (GTask *task);
+
+typedef enum {
+ ENABLING_STEP_FIRST,
+ ENABLING_STEP_SETUP_UNSOLICITED_EVENTS,
+ ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS,
+ ENABLING_STEP_LAST
+} EnablingStep;
+
+struct _EnablingContext {
+ EnablingStep step;
+ MmGdbusModem3gppProfileManager *skeleton;
+};
+
+static void
+enabling_context_free (EnablingContext *ctx)
+{
+ g_clear_object (&ctx->skeleton);
+ g_slice_free (EnablingContext, ctx);
+}
+
+gboolean
+mm_iface_modem_3gpp_profile_manager_enable_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+setup_unsolicited_events_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ EnablingContext *ctx;
+ g_autoptr(GError) error = NULL;
+
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->setup_unsolicited_events_finish (self, res, &error);
+ if (error) {
+ /* This error shouldn't be treated as critical */
+ mm_obj_dbg (self, "couldn't setup unsolicited profile management events: %s", error->message);
+ }
+
+ /* Go on to next step */
+ ctx = g_task_get_task_data (task);
+ ctx->step++;
+ interface_enabling_step (task);
+}
+
+static void
+enable_unsolicited_events_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ EnablingContext *ctx;
+ g_autoptr(GError) error = NULL;
+
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->enable_unsolicited_events_finish (self, res, &error);
+ if (error) {
+ /* This error shouldn't be treated as critical */
+ mm_obj_dbg (self, "couldn't enable unsolicited profile management events: %s", error->message);
+ }
+
+ /* Go on to next step */
+ ctx = g_task_get_task_data (task);
+ ctx->step++;
+ interface_enabling_step (task);
+}
+
+static void
+interface_enabling_step (GTask *task)
+{
+ MMIfaceModem3gppProfileManager *self;
+ EnablingContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case ENABLING_STEP_FIRST:
+ ctx->step++;
+ /* fall through */
+
+ case ENABLING_STEP_SETUP_UNSOLICITED_EVENTS:
+ if (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->setup_unsolicited_events &&
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->setup_unsolicited_events_finish) {
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->setup_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)setup_unsolicited_events_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS:
+ if (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->enable_unsolicited_events &&
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->enable_unsolicited_events_finish) {
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->enable_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)enable_unsolicited_events_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case ENABLING_STEP_LAST:
+ /* We are done without errors! */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+
+ default:
+ break;
+ }
+
+ g_assert_not_reached ();
+}
+
+void
+mm_iface_modem_3gpp_profile_manager_enable (MMIfaceModem3gppProfileManager *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ EnablingContext *ctx;
+ GTask *task;
+
+ ctx = g_slice_new0 (EnablingContext);
+ ctx->step = ENABLING_STEP_FIRST;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)enabling_context_free);
+
+ g_object_get (self,
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON, &ctx->skeleton,
+ NULL);
+ if (!ctx->skeleton) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't get interface skeleton");
+ g_object_unref (task);
+ return;
+ }
+
+ interface_enabling_step (task);
+}
+
+/*****************************************************************************/
+
+typedef struct _InitializationContext InitializationContext;
+static void interface_initialization_step (GTask *task);
+
+typedef enum {
+ INITIALIZATION_STEP_FIRST,
+ INITIALIZATION_STEP_CHECK_SUPPORT,
+ INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED,
+ INITIALIZATION_STEP_LAST
+} InitializationStep;
+
+struct _InitializationContext {
+ MmGdbusModem3gppProfileManager *skeleton;
+ InitializationStep step;
+};
+
+static void
+initialization_context_free (InitializationContext *ctx)
+{
+ g_clear_object (&ctx->skeleton);
+ g_slice_free (InitializationContext, ctx);
+}
+
+gboolean
+mm_iface_modem_3gpp_profile_manager_initialize_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+check_support_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ InitializationContext *ctx;
+ g_autoptr(GError) error = NULL;
+
+ if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->check_support_finish (self, res, &error)) {
+ if (error) {
+ /* This error shouldn't be treated as critical */
+ mm_obj_dbg (self, "profile management support check failed: %s", error->message);
+ }
+ } else {
+ /* profile management is supported! */
+ g_object_set_qdata (G_OBJECT (self), supported_quark, GUINT_TO_POINTER (TRUE));
+ }
+
+ /* Go on to next step */
+ ctx = g_task_get_task_data (task);
+ ctx->step++;
+ interface_initialization_step (task);
+}
+
+static void
+profile_manager_list_profiles_check_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ InitializationContext *ctx;
+ g_autoptr(GError) error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_iface_modem_3gpp_profile_manager_list_profiles_finish (self, res, NULL, &error))
+ mm_obj_dbg (self, "profile management support check failed: couldn't load profile list: %s", error->message);
+ else {
+ /* profile management is supported! */
+ g_object_set_qdata (G_OBJECT (self), supported_quark, GUINT_TO_POINTER (TRUE));
+ }
+
+ ctx->step++;
+ interface_initialization_step (task);
+}
+
+static void
+interface_initialization_step (GTask *task)
+{
+ MMIfaceModem3gppProfileManager *self;
+ InitializationContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case INITIALIZATION_STEP_FIRST:
+ /* Setup quarks if we didn't do it before */
+ if (G_UNLIKELY (!support_checked_quark))
+ support_checked_quark = (g_quark_from_static_string (SUPPORT_CHECKED_TAG));
+ if (G_UNLIKELY (!supported_quark))
+ supported_quark = (g_quark_from_static_string (SUPPORTED_TAG));
+ ctx->step++;
+ /* fall through */
+
+ case INITIALIZATION_STEP_CHECK_SUPPORT:
+ if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self), support_checked_quark))) {
+ /* Set the checked flag so that we don't run it again */
+ g_object_set_qdata (G_OBJECT (self), support_checked_quark, GUINT_TO_POINTER (TRUE));
+ /* Initially, assume we don't support it */
+ g_object_set_qdata (G_OBJECT (self), supported_quark, GUINT_TO_POINTER (FALSE));
+ if (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->check_support &&
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->check_support_finish) {
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->check_support (
+ self,
+ (GAsyncReadyCallback)check_support_ready,
+ task);
+ return;
+ }
+
+ /* If there is no implementation to check support, try to query the list
+ * explicitly; it may be the case that there is no other way to check for
+ * support. */
+ mm_iface_modem_3gpp_profile_manager_list_profiles (
+ self,
+ (GAsyncReadyCallback)profile_manager_list_profiles_check_ready,
+ task);
+ return;
+ }
+
+ ctx->step++;
+ /* fall through */
+
+ case INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED:
+ if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self), supported_quark))) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Profile management not supported");
+ g_object_unref (task);
+ return;
+ }
+
+ ctx->step++;
+ /* fall through */
+
+ case INITIALIZATION_STEP_LAST:
+ /* We are done without errors! */
+
+ /* Handle method invocations */
+ g_object_connect (ctx->skeleton,
+ "signal::handle-list", G_CALLBACK (handle_list), self,
+ "signal::handle-set", G_CALLBACK (handle_set), self,
+ "signal::handle-delete", G_CALLBACK (handle_delete), self,
+ NULL);
+
+ /* Finally, export the new interface */
+ mm_gdbus_object_skeleton_set_modem3gpp_profile_manager (MM_GDBUS_OBJECT_SKELETON (self),
+ MM_GDBUS_MODEM3GPP_PROFILE_MANAGER (ctx->skeleton));
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+
+ default:
+ break;
+ }
+
+ g_assert_not_reached ();
+}
+
+void
+mm_iface_modem_3gpp_profile_manager_initialize (MMIfaceModem3gppProfileManager *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ InitializationContext *ctx;
+ MmGdbusModem3gppProfileManager *skeleton = NULL;
+ GTask *task;
+
+ /* Did we already create it? */
+ g_object_get (self,
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON, &skeleton,
+ NULL);
+ if (!skeleton) {
+ skeleton = mm_gdbus_modem3gpp_profile_manager_skeleton_new ();
+ g_object_set (self,
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON, skeleton,
+ NULL);
+ }
+
+ /* Perform async initialization here */
+
+ ctx = g_slice_new0 (InitializationContext);
+ ctx->step = INITIALIZATION_STEP_FIRST;
+ ctx->skeleton = skeleton;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free);
+
+ interface_initialization_step (task);
+}
+
+void
+mm_iface_modem_3gpp_profile_manager_shutdown (MMIfaceModem3gppProfileManager *self)
+{
+ /* Unexport DBus interface and remove the skeleton */
+ mm_gdbus_object_skeleton_set_modem3gpp_profile_manager (MM_GDBUS_OBJECT_SKELETON (self), NULL);
+ g_object_set (self,
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON, NULL,
+ NULL);
+}
+
+/*****************************************************************************/
+
+static void
+iface_modem_3gpp_profile_manager_init (gpointer g_iface)
+{
+ static gboolean initialized = FALSE;
+
+ if (initialized)
+ return;
+
+ /* Properties */
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_object (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON,
+ "3GPP Profile Manager DBus skeleton",
+ "DBus skeleton for the 3GPP Profile Manager interface",
+ MM_GDBUS_TYPE_MODEM3GPP_PROFILE_MANAGER_SKELETON,
+ G_PARAM_READWRITE));
+
+ initialized = TRUE;
+}
+
+GType
+mm_iface_modem_3gpp_profile_manager_get_type (void)
+{
+ static GType iface_modem_3gpp_profile_manager_type = 0;
+
+ if (!G_UNLIKELY (iface_modem_3gpp_profile_manager_type)) {
+ static const GTypeInfo info = {
+ sizeof (MMIfaceModem3gppProfileManager), /* class_size */
+ iface_modem_3gpp_profile_manager_init, /* base_init */
+ NULL, /* base_finalize */
+ };
+
+ iface_modem_3gpp_profile_manager_type = g_type_register_static (G_TYPE_INTERFACE,
+ "MMIfaceModem3gppProfileManager",
+ &info, 0);
+
+ g_type_interface_add_prerequisite (iface_modem_3gpp_profile_manager_type, MM_TYPE_IFACE_MODEM_3GPP);
+ }
+
+ return iface_modem_3gpp_profile_manager_type;
+}
diff --git a/src/mm-iface-modem-3gpp-profile-manager.h b/src/mm-iface-modem-3gpp-profile-manager.h
new file mode 100644
index 00000000..dcf97895
--- /dev/null
+++ b/src/mm-iface-modem-3gpp-profile-manager.h
@@ -0,0 +1,248 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2021 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2021 Google, Inc.
+ */
+
+#ifndef MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_H
+#define MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#define MM_TYPE_IFACE_MODEM_3GPP_PROFILE_MANAGER (mm_iface_modem_3gpp_profile_manager_get_type ())
+#define MM_IFACE_MODEM_3GPP_PROFILE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_IFACE_MODEM_3GPP_PROFILE_MANAGER, MMIfaceModem3gppProfileManager))
+#define MM_IS_IFACE_MODEM_3GPP_PROFILE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_IFACE_MODEM_3GPP_PROFILE_MANAGER))
+#define MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_IFACE_MODEM_3GPP_PROFILE_MANAGER, MMIfaceModem3gppProfileManager))
+
+#define MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON "iface-modem-3gpp-profile-manager-dbus-skeleton"
+
+typedef struct _MMIfaceModem3gppProfileManager MMIfaceModem3gppProfileManager;
+
+struct _MMIfaceModem3gppProfileManager {
+ GTypeInterface g_iface;
+
+ /* Check for profile management support (async) */
+ void (* check_support) (MMIfaceModem3gppProfileManager *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* check_support_finish) (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Asynchronous setup of unsolicited events */
+ void (* setup_unsolicited_events) (MMIfaceModem3gppProfileManager *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* setup_unsolicited_events_finish) (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Asynchronous enabling of unsolicited events */
+ void (* enable_unsolicited_events) (MMIfaceModem3gppProfileManager *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* enable_unsolicited_events_finish) (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Asynchronous disabling of unsolicited events */
+ void (* disable_unsolicited_events) (MMIfaceModem3gppProfileManager *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* disable_unsolicited_events_finish) (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Asynchronous cleaning up of unsolicited events */
+ void (* cleanup_unsolicited_events) (MMIfaceModem3gppProfileManager *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* cleanup_unsolicited_events_finish) (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Get a single profile.
+ * This is completely optional, and should be used by implementations that are able
+ * to query one single profile settings. For all other implementations, it should be
+ * set to NULL so that the generic implementation is used (listing all and lookup by
+ * profile id) */
+ void (* get_profile) (MMIfaceModem3gppProfileManager *self,
+ gint profile_id,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ MM3gppProfile * (* get_profile_finish) (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* List */
+ void (* list_profiles) (MMIfaceModem3gppProfileManager *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* list_profiles_finish) (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GList **profiles,
+ GError **error);
+
+ /* Delete */
+ void (* delete_profile) (MMIfaceModem3gppProfileManager *self,
+ MM3gppProfile *requested,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* delete_profile_finish) (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /*
+ * Check profile format (substep of 'set profiles')
+ *
+ * Before a profile is set, we would like to check the format of the set
+ * operation, if possible.
+ *
+ * Expected outputs:
+ * - new_id: whether new profiles can be created with a specific known id.
+ * - min_profile_id: minimum supported profile id.
+ * - max_profile_id: maximum supported profile id.
+ * - apn_cmp: method to use when comparing APN strings.
+ * - profile_cmp_flags: flags to use when comparing profile objects.
+ *
+ * The check is done per IP family, as the ranges may be different for each.
+ */
+ void (* check_format) (MMIfaceModem3gppProfileManager *self,
+ MMBearerIpFamily family,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* check_format_finish) (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ gboolean *new_id,
+ gint *min_profile_id,
+ gint *max_profile_id,
+ GEqualFunc *apn_cmp,
+ MM3gppProfileCmpFlags *profile_cmp_flags,
+ GError **error);
+
+ /* Check activated profile (substep of 'set profiles')
+ *
+ * Before a profile is set, we may attempt to deactivate it first, but only
+ * if there is no known bearer using it already and only if this check for
+ * activation really reports the profile being already activated.
+ *
+ * The given profile MUST have profile-id set, so the set_profile()
+ * implementation should only use it once the profile-id is known, never
+ * before.
+ *
+ * This step is optional (method pointers can be initialized to NULL), so
+ * that the deactivate profile step is done always.
+ */
+ void (* check_activated_profile) (MMIfaceModem3gppProfileManager *self,
+ MM3gppProfile *requested,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* check_activated_profile_finish) (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ gboolean *out_activated,
+ GError **error);
+
+ /* Deactivate profile (substep of 'set profiles')
+ *
+ * Before a profile is set, we may attempt to deactivate it first, but only
+ * if there is no known bearer using it already.
+ *
+ * The given profile MUST have profile-id set, so the set_profile()
+ * implementation should only use it once the profile-id is known, never
+ * before.
+ */
+ void (* deactivate_profile) (MMIfaceModem3gppProfileManager *self,
+ MM3gppProfile *requested,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* deactivate_profile_finish) (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Store profile (substep of 'set profiles') */
+ void (* store_profile) (MMIfaceModem3gppProfileManager *self,
+ MM3gppProfile *requested,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gint (* store_profile_finish) (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error);
+};
+
+GType mm_iface_modem_3gpp_profile_manager_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMIfaceModem3gppProfileManager, g_object_unref)
+
+/* Initialize profile manager interface (async) */
+void mm_iface_modem_3gpp_profile_manager_initialize (MMIfaceModem3gppProfileManager *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_iface_modem_3gpp_profile_manager_initialize_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error);
+
+/* Enable profile manager interface (async) */
+void mm_iface_modem_3gpp_profile_manager_enable (MMIfaceModem3gppProfileManager *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_iface_modem_3gpp_profile_manager_enable_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error);
+
+/* Disable profile manager interface (async) */
+void mm_iface_modem_3gpp_profile_manager_disable (MMIfaceModem3gppProfileManager *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_iface_modem_3gpp_profile_manager_disable_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error);
+
+/* Shutdown profile manager interface */
+void mm_iface_modem_3gpp_profile_manager_shutdown (MMIfaceModem3gppProfileManager *self);
+
+/* Bind properties for simple GetStatus() */
+void mm_iface_modem_3gpp_profile_manager_bind_simple_status (MMIfaceModem3gppProfileManager *self,
+ MMSimpleStatus *status);
+
+/* Helper to emit the Updated signal by implementations */
+void mm_iface_modem_3gpp_profile_manager_updated (MMIfaceModem3gppProfileManager *self);
+
+/* Internal list profile management */
+void mm_iface_modem_3gpp_profile_manager_get_profile (MMIfaceModem3gppProfileManager *self,
+ gint profile_id,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+MM3gppProfile *mm_iface_modem_3gpp_profile_manager_get_profile_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_iface_modem_3gpp_profile_manager_list_profiles (MMIfaceModem3gppProfileManager *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_iface_modem_3gpp_profile_manager_list_profiles_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GList **profiles,
+ GError **error);
+void mm_iface_modem_3gpp_profile_manager_set_profile (MMIfaceModem3gppProfileManager *self,
+ MM3gppProfile *requested,
+ gboolean strict,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+MM3gppProfile *mm_iface_modem_3gpp_profile_manager_set_profile_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error);
+
+
+#endif /* MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_H */