diff options
Diffstat (limited to 'src/mm-iface-modem-oma.c')
-rw-r--r-- | src/mm-iface-modem-oma.c | 1252 |
1 files changed, 1252 insertions, 0 deletions
diff --git a/src/mm-iface-modem-oma.c b/src/mm-iface-modem-oma.c new file mode 100644 index 00000000..deb12ea5 --- /dev/null +++ b/src/mm-iface-modem-oma.c @@ -0,0 +1,1252 @@ +/* -*- 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) 2013 Google, Inc. + */ + +#include <ModemManager.h> +#define _LIBMM_INSIDE_MM +#include <libmm-glib.h> + +#include "mm-iface-modem.h" +#include "mm-iface-modem-oma.h" +#include "mm-log.h" + +#define SUPPORT_CHECKED_TAG "oma-support-checked-tag" +#define SUPPORTED_TAG "oma-supported-tag" + +static GQuark support_checked_quark; +static GQuark supported_quark; + +/*****************************************************************************/ + +void +mm_iface_modem_oma_bind_simple_status (MMIfaceModemOma *self, + MMSimpleStatus *status) +{ +} + +/*****************************************************************************/ +/* Manage the list of pending network-initiated sessions */ + +static void +add_or_remove_pending_network_initiated_session (MMIfaceModemOma *self, + gboolean add, + MMOmaSessionType session_type, + guint session_id) +{ + MmGdbusModemOma *skeleton; + GVariant *variant; + GArray *array; + guint i; + + g_object_get (self, + MM_IFACE_MODEM_OMA_DBUS_SKELETON, &skeleton, + NULL); + if (!skeleton) + return; + + variant = mm_gdbus_modem_oma_get_pending_network_initiated_sessions (skeleton); + array = mm_common_oma_pending_network_initiated_sessions_variant_to_garray (variant); + + for (i = 0; i < array->len; i++) { + MMOmaPendingNetworkInitiatedSession *session; + + session = &g_array_index (array, MMOmaPendingNetworkInitiatedSession, i); + if (session->session_id == session_id) + break; + } + + /* If not in the array, and we want to add it, add it */ + if (add && i == array->len) { + MMOmaPendingNetworkInitiatedSession session; + + session.session_type = session_type; + session.session_id = session_id; + g_array_append_val (array, session); + + mm_gdbus_modem_oma_set_pending_network_initiated_sessions ( + skeleton, + mm_common_oma_pending_network_initiated_sessions_garray_to_variant (array)); + } + /* If found in the array, and we want to remove it, remove it */ + else if (!add && i < array->len) { + g_array_remove_index (array, i); + mm_gdbus_modem_oma_set_pending_network_initiated_sessions ( + skeleton, + mm_common_oma_pending_network_initiated_sessions_garray_to_variant (array)); + } + + g_object_unref (skeleton); + g_array_unref (array); +} + +void +mm_iface_modem_oma_add_pending_network_initiated_session (MMIfaceModemOma *self, + MMOmaSessionType session_type, + guint session_id) +{ + add_or_remove_pending_network_initiated_session (self, TRUE, session_type, session_id); +} + +/*****************************************************************************/ +/* New session state reported */ + +void +mm_iface_modem_oma_update_session_state (MMIfaceModemOma *self, + MMOmaSessionState new_session_state, + MMOmaSessionStateFailedReason session_state_failed_reason) +{ + MmGdbusModemOma *skeleton; + MMOmaSessionState old_session_state; + + /* Make sure proper state vs failed reasons are given */ + g_return_if_fail ((new_session_state != MM_OMA_SESSION_STATE_FAILED && + session_state_failed_reason == MM_OMA_SESSION_STATE_FAILED_REASON_UNKNOWN) || + (new_session_state == MM_OMA_SESSION_STATE_FAILED)); + + g_object_get (self, + MM_IFACE_MODEM_OMA_DBUS_SKELETON, &skeleton, + NULL); + if (!skeleton) + return; + + old_session_state = mm_gdbus_modem_oma_get_session_state (skeleton); + if (old_session_state != new_session_state) { + mm_info ("Modem %s: OMA session state changed (%s -> %s)", + g_dbus_object_get_object_path (G_DBUS_OBJECT (self)), + mm_oma_session_state_get_string (old_session_state), + mm_oma_session_state_get_string (new_session_state)); + + /* Flush current change before signaling the state change, + * so that clients get the proper state already in the + * state-changed callback */ + g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (skeleton)); + mm_gdbus_modem_oma_emit_session_state_changed ( + skeleton, + old_session_state, + new_session_state, + session_state_failed_reason); + } +} + +/*****************************************************************************/ +/* Handle Setup() */ + +typedef struct { + MmGdbusModemOma *skeleton; + GDBusMethodInvocation *invocation; + MMIfaceModemOma *self; + guint32 features; +} HandleSetupContext; + +static void +handle_setup_context_free (HandleSetupContext *ctx) +{ + g_object_unref (ctx->skeleton); + g_object_unref (ctx->invocation); + g_object_unref (ctx->self); + g_slice_free (HandleSetupContext, ctx); +} + +static void +setup_ready (MMIfaceModemOma *self, + GAsyncResult *res, + HandleSetupContext *ctx) +{ + GError *error = NULL; + + if (!MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->setup_finish (self, res, &error)) + g_dbus_method_invocation_take_error (ctx->invocation, error); + else { + /* Update current features in the interface */ + mm_gdbus_modem_oma_set_features (ctx->skeleton, ctx->features); + mm_gdbus_modem_oma_complete_setup (ctx->skeleton, ctx->invocation); + } + + handle_setup_context_free (ctx); +} + +static void +handle_setup_auth_ready (MMBaseModem *self, + GAsyncResult *res, + HandleSetupContext *ctx) +{ + GError *error = NULL; + MMModemState modem_state; + gchar *str; + + if (!mm_base_modem_authorize_finish (self, res, &error)) { + g_dbus_method_invocation_take_error (ctx->invocation, error); + handle_setup_context_free (ctx); + return; + } + + modem_state = MM_MODEM_STATE_UNKNOWN; + g_object_get (self, + MM_IFACE_MODEM_STATE, &modem_state, + NULL); + if (modem_state < MM_MODEM_STATE_ENABLED) { + g_dbus_method_invocation_return_error (ctx->invocation, + MM_CORE_ERROR, + MM_CORE_ERROR_WRONG_STATE, + "Cannot setup OMA: " + "device not yet enabled"); + handle_setup_context_free (ctx); + return; + } + + if (!MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->setup || + !MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->setup_finish) { + g_dbus_method_invocation_return_error (ctx->invocation, + MM_CORE_ERROR, + MM_CORE_ERROR_UNSUPPORTED, + "Cannot setup OMA: " + "operation not supported"); + handle_setup_context_free (ctx); + return; + } + + str = mm_oma_feature_build_string_from_mask (ctx->features); + mm_dbg ("Setting up OMA features: '%s'", str); + g_free (str); + + MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->setup ( + ctx->self, + ctx->features, + (GAsyncReadyCallback)setup_ready, + ctx); +} + +static gboolean +handle_setup (MmGdbusModemOma *skeleton, + GDBusMethodInvocation *invocation, + guint32 features, + MMIfaceModemOma *self) +{ + HandleSetupContext *ctx; + + ctx = g_slice_new0 (HandleSetupContext); + ctx->skeleton = g_object_ref (skeleton); + ctx->invocation = g_object_ref (invocation); + ctx->self = g_object_ref (self); + ctx->features = features; + + mm_base_modem_authorize (MM_BASE_MODEM (self), + invocation, + MM_AUTHORIZATION_DEVICE_CONTROL, + (GAsyncReadyCallback)handle_setup_auth_ready, + ctx); + return TRUE; +} + +/*****************************************************************************/ +/* Handle StartClientInitiatedSession() */ + +typedef struct { + MmGdbusModemOma *skeleton; + GDBusMethodInvocation *invocation; + MMIfaceModemOma *self; + MMOmaSessionType session_type; +} HandleStartClientInitiatedSessionContext; + +static void +handle_start_client_initiated_session_context_free (HandleStartClientInitiatedSessionContext *ctx) +{ + g_object_unref (ctx->skeleton); + g_object_unref (ctx->invocation); + g_object_unref (ctx->self); + g_slice_free (HandleStartClientInitiatedSessionContext, ctx); +} + +static void +start_client_initiated_session_ready (MMIfaceModemOma *self, + GAsyncResult *res, + HandleStartClientInitiatedSessionContext *ctx) +{ + GError *error = NULL; + + if (!MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->start_client_initiated_session_finish (self, res, &error)) + g_dbus_method_invocation_take_error (ctx->invocation, error); + else { + /* Update interface info */ + mm_gdbus_modem_oma_set_session_type (ctx->skeleton, ctx->session_type); + mm_iface_modem_oma_update_session_state (self, MM_OMA_SESSION_STATE_STARTED, MM_OMA_SESSION_STATE_FAILED_REASON_UNKNOWN); + mm_gdbus_modem_oma_complete_start_client_initiated_session (ctx->skeleton, ctx->invocation); + } + + handle_start_client_initiated_session_context_free (ctx); +} + +static void +handle_start_client_initiated_session_auth_ready (MMBaseModem *self, + GAsyncResult *res, + HandleStartClientInitiatedSessionContext *ctx) +{ + GError *error = NULL; + MMModemState modem_state; + + if (!mm_base_modem_authorize_finish (self, res, &error)) { + g_dbus_method_invocation_take_error (ctx->invocation, error); + handle_start_client_initiated_session_context_free (ctx); + return; + } + + modem_state = MM_MODEM_STATE_UNKNOWN; + g_object_get (self, + MM_IFACE_MODEM_STATE, &modem_state, + NULL); + if (modem_state < MM_MODEM_STATE_ENABLED) { + g_dbus_method_invocation_return_error (ctx->invocation, + MM_CORE_ERROR, + MM_CORE_ERROR_WRONG_STATE, + "Cannot start client-initiated OMA session: " + "device not yet enabled"); + handle_start_client_initiated_session_context_free (ctx); + return; + } + + if (!MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->start_client_initiated_session || + !MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->start_client_initiated_session_finish) { + g_dbus_method_invocation_return_error (ctx->invocation, + MM_CORE_ERROR, + MM_CORE_ERROR_UNSUPPORTED, + "Cannot start client-initiated OMA session: " + "operation not supported"); + handle_start_client_initiated_session_context_free (ctx); + return; + } + + if (ctx->session_type != MM_OMA_SESSION_TYPE_CLIENT_INITIATED_DEVICE_CONFIGURE && + ctx->session_type != MM_OMA_SESSION_TYPE_CLIENT_INITIATED_PRL_UPDATE && + ctx->session_type != MM_OMA_SESSION_TYPE_CLIENT_INITIATED_HANDS_FREE_ACTIVATION) { + g_dbus_method_invocation_return_error (ctx->invocation, + MM_CORE_ERROR, + MM_CORE_ERROR_UNSUPPORTED, + "Cannot start client-initiated OMA session: " + "invalid session type specified (%s)", + mm_oma_session_type_get_string (ctx->session_type)); + handle_start_client_initiated_session_context_free (ctx); + return; + } + + mm_dbg ("Starting client-initiated OMA session (%s)", + mm_oma_session_type_get_string (ctx->session_type)); + MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->start_client_initiated_session ( + ctx->self, + ctx->session_type, + (GAsyncReadyCallback)start_client_initiated_session_ready, + ctx); +} + +static gboolean +handle_start_client_initiated_session (MmGdbusModemOma *skeleton, + GDBusMethodInvocation *invocation, + guint32 session_type, + MMIfaceModemOma *self) +{ + HandleStartClientInitiatedSessionContext *ctx; + + ctx = g_slice_new0 (HandleStartClientInitiatedSessionContext); + ctx->skeleton = g_object_ref (skeleton); + ctx->invocation = g_object_ref (invocation); + ctx->self = g_object_ref (self); + ctx->session_type = session_type; + + mm_base_modem_authorize (MM_BASE_MODEM (self), + invocation, + MM_AUTHORIZATION_DEVICE_CONTROL, + (GAsyncReadyCallback)handle_start_client_initiated_session_auth_ready, + ctx); + return TRUE; +} + +/*****************************************************************************/ +/* Handle AcceptNetworkInitiatedSession() */ + +typedef struct { + MmGdbusModemOma *skeleton; + GDBusMethodInvocation *invocation; + MMIfaceModemOma *self; + guint session_id; + gboolean accept; +} HandleAcceptNetworkInitiatedSessionContext; + +static void +handle_accept_network_initiated_session_context_free (HandleAcceptNetworkInitiatedSessionContext *ctx) +{ + g_object_unref (ctx->skeleton); + g_object_unref (ctx->invocation); + g_object_unref (ctx->self); + g_slice_free (HandleAcceptNetworkInitiatedSessionContext, ctx); +} + +static MMOmaSessionType +get_pending_network_initiated_session_type (MMIfaceModemOma *self, + guint session_id) +{ + MMOmaSessionType session_type = MM_OMA_SESSION_TYPE_UNKNOWN; + MmGdbusModemOma *skeleton; + + g_object_get (self, + MM_IFACE_MODEM_OMA_DBUS_SKELETON, &skeleton, + NULL); + if (skeleton) { + GArray *array; + guint i; + + array = (mm_common_oma_pending_network_initiated_sessions_variant_to_garray ( + mm_gdbus_modem_oma_get_pending_network_initiated_sessions (skeleton))); + for (i = 0; i < array->len && session_type == MM_OMA_SESSION_TYPE_UNKNOWN; i++) { + MMOmaPendingNetworkInitiatedSession *session; + + session = &g_array_index (array, MMOmaPendingNetworkInitiatedSession, i); + if (session->session_id == session_id) + session_type = session->session_type; + } + + g_array_unref (array); + g_object_unref (skeleton); + } + + return session_type; +} + +static void +accept_network_initiated_session_ready (MMIfaceModemOma *self, + GAsyncResult *res, + HandleAcceptNetworkInitiatedSessionContext *ctx) +{ + GError *error = NULL; + + if (!MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->accept_network_initiated_session_finish (self, res, &error)) + g_dbus_method_invocation_take_error (ctx->invocation, error); + else { + MMOmaSessionType session_type; + + /* Get session type */ + session_type = get_pending_network_initiated_session_type (self, ctx->session_id); + + /* If accepted or rejected, remove from pending */ + add_or_remove_pending_network_initiated_session (self, FALSE, session_type, ctx->session_id); + + /* If accepted, set as current */ + if (ctx->accept) { + mm_gdbus_modem_oma_set_session_type (ctx->skeleton, session_type); + mm_iface_modem_oma_update_session_state (self, MM_OMA_SESSION_STATE_STARTED, MM_OMA_SESSION_STATE_FAILED_REASON_UNKNOWN); + } + + mm_gdbus_modem_oma_complete_accept_network_initiated_session (ctx->skeleton, ctx->invocation); + } + + handle_accept_network_initiated_session_context_free (ctx); +} + +static void +handle_accept_network_initiated_session_auth_ready (MMBaseModem *self, + GAsyncResult *res, + HandleAcceptNetworkInitiatedSessionContext *ctx) +{ + GError *error = NULL; + MMModemState modem_state; + + if (!mm_base_modem_authorize_finish (self, res, &error)) { + g_dbus_method_invocation_take_error (ctx->invocation, error); + handle_accept_network_initiated_session_context_free (ctx); + return; + } + + modem_state = MM_MODEM_STATE_UNKNOWN; + g_object_get (self, + MM_IFACE_MODEM_STATE, &modem_state, + NULL); + if (modem_state < MM_MODEM_STATE_ENABLED) { + g_dbus_method_invocation_return_error (ctx->invocation, + MM_CORE_ERROR, + MM_CORE_ERROR_WRONG_STATE, + "Cannot accept network-initiated OMA session: " + "device not yet enabled"); + handle_accept_network_initiated_session_context_free (ctx); + return; + } + + if (!MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->accept_network_initiated_session || + !MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->accept_network_initiated_session_finish) { + g_dbus_method_invocation_return_error (ctx->invocation, + MM_CORE_ERROR, + MM_CORE_ERROR_UNSUPPORTED, + "Cannot accept network-initiated OMA session: " + "operation not supported"); + handle_accept_network_initiated_session_context_free (ctx); + return; + } + + mm_dbg ("%s network-initiated OMA session (%u)", + ctx->accept ? "Accepting" : "Rejecting", + ctx->session_id); + MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->accept_network_initiated_session ( + ctx->self, + ctx->session_id, + ctx->accept, + (GAsyncReadyCallback)accept_network_initiated_session_ready, + ctx); +} + +static gboolean +handle_accept_network_initiated_session (MmGdbusModemOma *skeleton, + GDBusMethodInvocation *invocation, + guint32 session_id, + gboolean accept, + MMIfaceModemOma *self) +{ + HandleAcceptNetworkInitiatedSessionContext *ctx; + + ctx = g_slice_new0 (HandleAcceptNetworkInitiatedSessionContext); + ctx->skeleton = g_object_ref (skeleton); + ctx->invocation = g_object_ref (invocation); + ctx->self = g_object_ref (self); + ctx->session_id = session_id; + ctx->accept = accept; + + mm_base_modem_authorize (MM_BASE_MODEM (self), + invocation, + MM_AUTHORIZATION_DEVICE_CONTROL, + (GAsyncReadyCallback)handle_accept_network_initiated_session_auth_ready, + ctx); + return TRUE; +} + +/*****************************************************************************/ +/* Handle CancelSession() */ + +typedef struct { + MmGdbusModemOma *skeleton; + GDBusMethodInvocation *invocation; + MMIfaceModemOma *self; +} HandleCancelSessionContext; + +static void +handle_cancel_session_context_free (HandleCancelSessionContext *ctx) +{ + g_object_unref (ctx->skeleton); + g_object_unref (ctx->invocation); + g_object_unref (ctx->self); + g_slice_free (HandleCancelSessionContext, ctx); +} + +static void +cancel_session_ready (MMIfaceModemOma *self, + GAsyncResult *res, + HandleCancelSessionContext *ctx) +{ + GError *error = NULL; + + if (!MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->cancel_session_finish (self, res, &error)) + g_dbus_method_invocation_take_error (ctx->invocation, error); + else { + /* Clear interface info when cancelled */ + mm_gdbus_modem_oma_set_session_type (ctx->skeleton, MM_OMA_SESSION_TYPE_UNKNOWN); + mm_iface_modem_oma_update_session_state (self, MM_OMA_SESSION_STATE_UNKNOWN, MM_OMA_SESSION_STATE_FAILED_REASON_UNKNOWN); + + mm_gdbus_modem_oma_complete_cancel_session (ctx->skeleton, ctx->invocation); + } + + handle_cancel_session_context_free (ctx); +} + +static void +handle_cancel_session_auth_ready (MMBaseModem *self, + GAsyncResult *res, + HandleCancelSessionContext *ctx) +{ + GError *error = NULL; + MMModemState modem_state; + + if (!mm_base_modem_authorize_finish (self, res, &error)) { + g_dbus_method_invocation_take_error (ctx->invocation, error); + handle_cancel_session_context_free (ctx); + return; + } + + modem_state = MM_MODEM_STATE_UNKNOWN; + g_object_get (self, + MM_IFACE_MODEM_STATE, &modem_state, + NULL); + if (modem_state < MM_MODEM_STATE_ENABLED) { + g_dbus_method_invocation_return_error (ctx->invocation, + MM_CORE_ERROR, + MM_CORE_ERROR_WRONG_STATE, + "Cannot cancel OMA session: " + "device not yet enabled"); + handle_cancel_session_context_free (ctx); + return; + } + + if (!MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->cancel_session || + !MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->cancel_session_finish) { + g_dbus_method_invocation_return_error (ctx->invocation, + MM_CORE_ERROR, + MM_CORE_ERROR_UNSUPPORTED, + "Cannot cancel OMA session: " + "operation not supported"); + handle_cancel_session_context_free (ctx); + return; + } + + mm_dbg ("Cancelling OMA session"); + MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->cancel_session ( + ctx->self, + (GAsyncReadyCallback)cancel_session_ready, + ctx); +} + +static gboolean +handle_cancel_session (MmGdbusModemOma *skeleton, + GDBusMethodInvocation *invocation, + MMIfaceModemOma *self) +{ + HandleCancelSessionContext *ctx; + + ctx = g_slice_new0 (HandleCancelSessionContext); + 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_cancel_session_auth_ready, + ctx); + return TRUE; +} + +/*****************************************************************************/ + +typedef struct _DisablingContext DisablingContext; +static void interface_disabling_step (DisablingContext *ctx); + +typedef enum { + DISABLING_STEP_FIRST, + DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS, + DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS, + DISABLING_STEP_LAST +} DisablingStep; + +struct _DisablingContext { + MMIfaceModemOma *self; + DisablingStep step; + GSimpleAsyncResult *result; + MmGdbusModemOma *skeleton; +}; + +static void +disabling_context_complete_and_free (DisablingContext *ctx) +{ + g_simple_async_result_complete_in_idle (ctx->result); + g_object_unref (ctx->self); + g_object_unref (ctx->result); + if (ctx->skeleton) + g_object_unref (ctx->skeleton); + g_free (ctx); +} + +gboolean +mm_iface_modem_oma_disable_finish (MMIfaceModemOma *self, + GAsyncResult *res, + GError **error) +{ + return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); +} + +static void +disable_unsolicited_events_ready (MMIfaceModemOma *self, + GAsyncResult *res, + DisablingContext *ctx) +{ + GError *error = NULL; + + MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->disable_unsolicited_events_finish (self, res, &error); + if (error) { + g_simple_async_result_take_error (ctx->result, error); + disabling_context_complete_and_free (ctx); + return; + } + + /* Go on to next step */ + ctx->step++; + interface_disabling_step (ctx); +} + +static void +cleanup_unsolicited_events_ready (MMIfaceModemOma *self, + GAsyncResult *res, + DisablingContext *ctx) +{ + GError *error = NULL; + + MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->cleanup_unsolicited_events_finish (self, res, &error); + if (error) { + g_simple_async_result_take_error (ctx->result, error); + disabling_context_complete_and_free (ctx); + return; + } + + /* Go on to next step */ + ctx->step++; + interface_disabling_step (ctx); +} + +static void +interface_disabling_step (DisablingContext *ctx) +{ + switch (ctx->step) { + case DISABLING_STEP_FIRST: + /* Fall down to next step */ + ctx->step++; + + case DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS: + /* Allow cleaning up unsolicited events */ + if (MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->disable_unsolicited_events && + MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->disable_unsolicited_events_finish) { + MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->disable_unsolicited_events ( + ctx->self, + (GAsyncReadyCallback)disable_unsolicited_events_ready, + ctx); + return; + } + /* Fall down to next step */ + ctx->step++; + + case DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS: + /* Allow cleaning up unsolicited events */ + if (MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->cleanup_unsolicited_events && + MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->cleanup_unsolicited_events_finish) { + MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->cleanup_unsolicited_events ( + ctx->self, + (GAsyncReadyCallback)cleanup_unsolicited_events_ready, + ctx); + return; + } + /* Fall down to next step */ + ctx->step++; + + case DISABLING_STEP_LAST: + /* We are done without errors! */ + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + disabling_context_complete_and_free (ctx); + return; + } + + g_assert_not_reached (); +} + +void +mm_iface_modem_oma_disable (MMIfaceModemOma *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + DisablingContext *ctx; + + ctx = g_new0 (DisablingContext, 1); + ctx->self = g_object_ref (self); + ctx->result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + mm_iface_modem_oma_disable); + ctx->step = DISABLING_STEP_FIRST; + g_object_get (ctx->self, + MM_IFACE_MODEM_OMA_DBUS_SKELETON, &ctx->skeleton, + NULL); + if (!ctx->skeleton) { + g_simple_async_result_set_error (ctx->result, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Couldn't get interface skeleton"); + disabling_context_complete_and_free (ctx); + return; + } + + interface_disabling_step (ctx); +} + +/*****************************************************************************/ + +typedef struct _EnablingContext EnablingContext; +static void interface_enabling_step (EnablingContext *ctx); + +typedef enum { + ENABLING_STEP_FIRST, + ENABLING_STEP_LOAD_FEATURES, + ENABLING_STEP_SETUP_UNSOLICITED_EVENTS, + ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS, + ENABLING_STEP_LAST +} EnablingStep; + +struct _EnablingContext { + MMIfaceModemOma *self; + EnablingStep step; + GSimpleAsyncResult *result; + GCancellable *cancellable; + MmGdbusModemOma *skeleton; +}; + +static void +enabling_context_complete_and_free (EnablingContext *ctx) +{ + g_simple_async_result_complete_in_idle (ctx->result); + g_object_unref (ctx->self); + g_object_unref (ctx->result); + g_object_unref (ctx->cancellable); + if (ctx->skeleton) + g_object_unref (ctx->skeleton); + g_free (ctx); +} + +static gboolean +enabling_context_complete_and_free_if_cancelled (EnablingContext *ctx) +{ + if (!g_cancellable_is_cancelled (ctx->cancellable)) + return FALSE; + + g_simple_async_result_set_error (ctx->result, + MM_CORE_ERROR, + MM_CORE_ERROR_CANCELLED, + "Interface enabling cancelled"); + enabling_context_complete_and_free (ctx); + return TRUE; +} + +gboolean +mm_iface_modem_oma_enable_finish (MMIfaceModemOma *self, + GAsyncResult *res, + GError **error) +{ + return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); +} + +static void +load_features_ready (MMIfaceModemOma *self, + GAsyncResult *res, + EnablingContext *ctx) +{ + GError *error = NULL; + MMOmaFeature features; + + features = MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->load_features_finish (self, res, &error); + if (error) { + g_simple_async_result_take_error (ctx->result, error); + enabling_context_complete_and_free (ctx); + return; + } + + /* Update in the interface */ + mm_gdbus_modem_oma_set_features (ctx->skeleton, features); + + /* Go on to next step */ + ctx->step++; + interface_enabling_step (ctx); +} + +static void +setup_unsolicited_events_ready (MMIfaceModemOma *self, + GAsyncResult *res, + EnablingContext *ctx) +{ + GError *error = NULL; + + MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->setup_unsolicited_events_finish (self, res, &error); + if (error) { + g_simple_async_result_take_error (ctx->result, error); + enabling_context_complete_and_free (ctx); + return; + } + + /* Go on to next step */ + ctx->step++; + interface_enabling_step (ctx); +} + +static void +enable_unsolicited_events_ready (MMIfaceModemOma *self, + GAsyncResult *res, + EnablingContext *ctx) +{ + GError *error = NULL; + + /* Not critical! */ + if (!MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->enable_unsolicited_events_finish (self, res, &error)) { + mm_dbg ("Couldn't enable unsolicited events: '%s'", error->message); + g_error_free (error); + } + + /* Go on with next step */ + ctx->step++; + interface_enabling_step (ctx); +} + +static void +interface_enabling_step (EnablingContext *ctx) +{ + /* Don't run new steps if we're cancelled */ + if (enabling_context_complete_and_free_if_cancelled (ctx)) + return; + + switch (ctx->step) { + case ENABLING_STEP_FIRST: + /* Fall down to next step */ + ctx->step++; + + case ENABLING_STEP_LOAD_FEATURES: + if (MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->load_features && + MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->load_features_finish) { + MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->load_features ( + ctx->self, + (GAsyncReadyCallback)load_features_ready, + ctx); + return; + } + /* Fall down to next step */ + ctx->step++; + + case ENABLING_STEP_SETUP_UNSOLICITED_EVENTS: + /* Allow setting up unsolicited events */ + if (MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->setup_unsolicited_events && + MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->setup_unsolicited_events_finish) { + MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->setup_unsolicited_events ( + ctx->self, + (GAsyncReadyCallback)setup_unsolicited_events_ready, + ctx); + return; + } + /* Fall down to next step */ + ctx->step++; + + case ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS: + /* Allow setting up unsolicited events */ + if (MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->enable_unsolicited_events && + MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->enable_unsolicited_events_finish) { + MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->enable_unsolicited_events ( + ctx->self, + (GAsyncReadyCallback)enable_unsolicited_events_ready, + ctx); + return; + } + /* Fall down to next step */ + ctx->step++; + + case ENABLING_STEP_LAST: + /* We are done without errors! */ + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + enabling_context_complete_and_free (ctx); + return; + } + + g_assert_not_reached (); +} + +void +mm_iface_modem_oma_enable (MMIfaceModemOma *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + EnablingContext *ctx; + + ctx = g_new0 (EnablingContext, 1); + ctx->self = g_object_ref (self); + ctx->cancellable = g_object_ref (cancellable); + ctx->result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + mm_iface_modem_oma_enable); + ctx->step = ENABLING_STEP_FIRST; + g_object_get (ctx->self, + MM_IFACE_MODEM_OMA_DBUS_SKELETON, &ctx->skeleton, + NULL); + if (!ctx->skeleton) { + g_simple_async_result_set_error (ctx->result, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Couldn't get interface skeleton"); + enabling_context_complete_and_free (ctx); + return; + } + + interface_enabling_step (ctx); +} + +/*****************************************************************************/ + +typedef struct _InitializationContext InitializationContext; +static void interface_initialization_step (InitializationContext *ctx); + +typedef enum { + INITIALIZATION_STEP_FIRST, + INITIALIZATION_STEP_CHECK_SUPPORT, + INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED, + INITIALIZATION_STEP_LAST +} InitializationStep; + +struct _InitializationContext { + MMIfaceModemOma *self; + MmGdbusModemOma *skeleton; + GSimpleAsyncResult *result; + GCancellable *cancellable; + InitializationStep step; +}; + +static void +initialization_context_complete_and_free (InitializationContext *ctx) +{ + g_simple_async_result_complete_in_idle (ctx->result); + g_object_unref (ctx->self); + g_object_unref (ctx->result); + g_object_unref (ctx->cancellable); + g_object_unref (ctx->skeleton); + g_free (ctx); +} + +static gboolean +initialization_context_complete_and_free_if_cancelled (InitializationContext *ctx) +{ + if (!g_cancellable_is_cancelled (ctx->cancellable)) + return FALSE; + + g_simple_async_result_set_error (ctx->result, + MM_CORE_ERROR, + MM_CORE_ERROR_CANCELLED, + "Interface initialization cancelled"); + initialization_context_complete_and_free (ctx); + return TRUE; +} + +static void +check_support_ready (MMIfaceModemOma *self, + GAsyncResult *res, + InitializationContext *ctx) +{ + GError *error = NULL; + + if (!MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->check_support_finish (self, res, &error)) { + if (error) { + /* This error shouldn't be treated as critical */ + mm_dbg ("OMA support check failed: '%s'", error->message); + g_error_free (error); + } + } else { + /* OMA is supported! */ + g_object_set_qdata (G_OBJECT (self), + supported_quark, + GUINT_TO_POINTER (TRUE)); + } + + /* Go on to next step */ + ctx->step++; + interface_initialization_step (ctx); +} + +static void +interface_initialization_step (InitializationContext *ctx) +{ + /* Don't run new steps if we're cancelled */ + if (initialization_context_complete_and_free_if_cancelled (ctx)) + return; + + 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)); + + /* Fall down to next step */ + ctx->step++; + + case INITIALIZATION_STEP_CHECK_SUPPORT: + if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (ctx->self), + support_checked_quark))) { + /* Set the checked flag so that we don't run it again */ + g_object_set_qdata (G_OBJECT (ctx->self), + support_checked_quark, + GUINT_TO_POINTER (TRUE)); + /* Initially, assume we don't support it */ + g_object_set_qdata (G_OBJECT (ctx->self), + supported_quark, + GUINT_TO_POINTER (FALSE)); + + if (MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->check_support && + MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->check_support_finish) { + MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->check_support ( + ctx->self, + (GAsyncReadyCallback)check_support_ready, + ctx); + return; + } + + /* If there is no implementation to check support, assume we DON'T + * support it. */ + } + /* Fall down to next step */ + ctx->step++; + + case INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED: + if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (ctx->self), + supported_quark))) { + g_simple_async_result_set_error (ctx->result, + MM_CORE_ERROR, + MM_CORE_ERROR_UNSUPPORTED, + "OMA not supported"); + initialization_context_complete_and_free (ctx); + return; + } + /* Fall down to next step */ + ctx->step++; + + case INITIALIZATION_STEP_LAST: + /* We are done without errors! */ + + /* Handle method invocations */ + g_signal_connect (ctx->skeleton, + "handle-setup", + G_CALLBACK (handle_setup), + ctx->self); + g_signal_connect (ctx->skeleton, + "handle-start-client-initiated-session", + G_CALLBACK (handle_start_client_initiated_session), + ctx->self); + g_signal_connect (ctx->skeleton, + "handle-accept-network-initiated-session", + G_CALLBACK (handle_accept_network_initiated_session), + ctx->self); + g_signal_connect (ctx->skeleton, + "handle-cancel-session", + G_CALLBACK (handle_cancel_session), + ctx->self); + + /* Finally, export the new interface */ + mm_gdbus_object_skeleton_set_modem_oma (MM_GDBUS_OBJECT_SKELETON (ctx->self), + MM_GDBUS_MODEM_OMA (ctx->skeleton)); + + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + initialization_context_complete_and_free (ctx); + return; + } + + g_assert_not_reached (); +} + +gboolean +mm_iface_modem_oma_initialize_finish (MMIfaceModemOma *self, + GAsyncResult *res, + GError **error) +{ + return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); +} + +void +mm_iface_modem_oma_initialize (MMIfaceModemOma *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + InitializationContext *ctx; + MmGdbusModemOma *skeleton = NULL; + + /* Did we already create it? */ + g_object_get (self, + MM_IFACE_MODEM_OMA_DBUS_SKELETON, &skeleton, + NULL); + if (!skeleton) { + skeleton = mm_gdbus_modem_oma_skeleton_new (); + g_object_set (self, + MM_IFACE_MODEM_OMA_DBUS_SKELETON, skeleton, + NULL); + + /* Set all initial property defaults */ + mm_gdbus_modem_oma_set_features (skeleton, MM_OMA_FEATURE_NONE); + mm_gdbus_modem_oma_set_session_type (skeleton, MM_OMA_SESSION_TYPE_UNKNOWN); + mm_gdbus_modem_oma_set_session_state (skeleton, MM_OMA_SESSION_STATE_UNKNOWN); + mm_gdbus_modem_oma_set_pending_network_initiated_sessions (skeleton, mm_common_build_oma_pending_network_initiated_sessions_default ()); + } + + /* Perform async initialization here */ + + ctx = g_new0 (InitializationContext, 1); + ctx->self = g_object_ref (self); + ctx->cancellable = g_object_ref (cancellable); + ctx->result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + mm_iface_modem_oma_initialize); + ctx->step = INITIALIZATION_STEP_FIRST; + ctx->skeleton = skeleton; + + interface_initialization_step (ctx); +} + +void +mm_iface_modem_oma_shutdown (MMIfaceModemOma *self) +{ + /* Unexport DBus interface and remove the skeleton */ + mm_gdbus_object_skeleton_set_modem_oma (MM_GDBUS_OBJECT_SKELETON (self), NULL); + g_object_set (self, + MM_IFACE_MODEM_OMA_DBUS_SKELETON, NULL, + NULL); +} + +/*****************************************************************************/ + +static void +iface_modem_oma_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_OMA_DBUS_SKELETON, + "OMA DBus skeleton", + "DBus skeleton for the OMA interface", + MM_GDBUS_TYPE_MODEM_OMA_SKELETON, + G_PARAM_READWRITE)); + + initialized = TRUE; +} + +GType +mm_iface_modem_oma_get_type (void) +{ + static GType iface_modem_oma_type = 0; + + if (!G_UNLIKELY (iface_modem_oma_type)) { + static const GTypeInfo info = { + sizeof (MMIfaceModemOma), /* class_size */ + iface_modem_oma_init, /* base_init */ + NULL, /* base_finalize */ + }; + + iface_modem_oma_type = g_type_register_static (G_TYPE_INTERFACE, + "MMIfaceModemOma", + &info, + 0); + + g_type_interface_add_prerequisite (iface_modem_oma_type, MM_TYPE_IFACE_MODEM); + } + + return iface_modem_oma_type; +} |