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/mm-iface-modem-3gpp-profile-manager.c | |
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/mm-iface-modem-3gpp-profile-manager.c')
-rw-r--r-- | src/mm-iface-modem-3gpp-profile-manager.c | 1673 |
1 files changed, 1673 insertions, 0 deletions
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; +} |