diff options
author | Aleksander Morgado <aleksander@aleksander.es> | 2021-03-24 12:03:30 +0100 |
---|---|---|
committer | Aleksander Morgado <aleksander@aleksander.es> | 2021-04-29 10:13:22 +0000 |
commit | 10aa516f075ef8ea08cd045cc397379b50c8782a (patch) | |
tree | 8279135044f98acd30f09ac18243681067804ae7 /src | |
parent | 62cbf7fa86a3f4773184186b7f937a7bee8b0e5a (diff) |
iface-modem-3gpp-profile-manager: new interface for profile management
This interface will provide support for the profile management
operations in different modem types.
This initial commit introduces support for the interface and provides
handlers for the methods that may be called in the interface.
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/mm-broadband-modem.c | 132 | ||||
-rw-r--r-- | src/mm-iface-modem-3gpp-profile-manager.c | 1673 | ||||
-rw-r--r-- | src/mm-iface-modem-3gpp-profile-manager.h | 248 |
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 */ |