aboutsummaryrefslogtreecommitdiff
path: root/src/mm-iface-modem-3gpp-profile-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mm-iface-modem-3gpp-profile-manager.c')
-rw-r--r--src/mm-iface-modem-3gpp-profile-manager.c1673
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;
+}