diff options
-rw-r--r-- | data/org.freedesktop.ModemManager1.conf.polkit | 15 | ||||
-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 |
5 files changed, 2040 insertions, 30 deletions
diff --git a/data/org.freedesktop.ModemManager1.conf.polkit b/data/org.freedesktop.ModemManager1.conf.polkit index 8087da6f..a5e43600 100644 --- a/data/org.freedesktop.ModemManager1.conf.polkit +++ b/data/org.freedesktop.ModemManager1.conf.polkit @@ -184,6 +184,21 @@ send_interface="org.freedesktop.ModemManager1.Bearer" send_member="Disconnect"/> + <!-- org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager.xml --> + + <!-- Protected by the Device.Control policy rule --> + <allow send_destination="org.freedesktop.ModemManager1" + send_interface="org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager" + send_member="List"/> + + <allow send_destination="org.freedesktop.ModemManager1" + send_interface="org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager" + send_member="Set"/> + + <allow send_destination="org.freedesktop.ModemManager1" + send_interface="org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager" + send_member="Delete"/> + <!-- org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd.xml --> <!-- Protected by the USSD policy rule --> 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 */ |