Rather than make all of them rely on MMBaseModem for it. This lets us disentangle dependencies for easier unit testing. For interfaces, rather than casting directly to MMBaseModem use intermediate interfaces (MMIfaceAuth and MMIfaceOpLock) that tests can fake out. Signed-off-by: Dan Williams <dan@ioncontrol.co>
1200 lines
41 KiB
C
1200 lines
41 KiB
C
/* -*- 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) 2009 Novell, Inc.
|
|
* Copyright (C) 2009 - 2011 Red Hat, Inc.
|
|
* Copyright (C) 2011 Google, Inc.
|
|
* Copyright (c) 2022 Qualcomm Innovation Center, Inc.
|
|
*/
|
|
|
|
#include <ModemManager.h>
|
|
#define _LIBMM_INSIDE_MM
|
|
#include <libmm-glib.h>
|
|
|
|
#include "mm-bearer-list.h"
|
|
#include "mm-base-sim.h"
|
|
#include "mm-error-helpers.h"
|
|
#include "mm-iface-modem.h"
|
|
#include "mm-iface-modem-3gpp.h"
|
|
#include "mm-iface-modem-cdma.h"
|
|
#include "mm-iface-modem-simple.h"
|
|
#include "mm-log-object.h"
|
|
#include "mm-log-helpers.h"
|
|
|
|
G_DEFINE_INTERFACE (MMIfaceModemSimple, mm_iface_modem_simple, MM_TYPE_IFACE_MODEM)
|
|
|
|
/*****************************************************************************/
|
|
/* Private data context */
|
|
|
|
#define PRIVATE_TAG "iface-modem-simple-private-tag"
|
|
static GQuark private_quark;
|
|
|
|
typedef struct {
|
|
GCancellable *ongoing_connect;
|
|
} Private;
|
|
|
|
static void
|
|
private_free (Private *priv)
|
|
{
|
|
g_clear_object (&priv->ongoing_connect);
|
|
g_slice_free (Private, priv);
|
|
}
|
|
|
|
static Private *
|
|
get_private (MMIfaceModemSimple *self)
|
|
{
|
|
Private *priv;
|
|
|
|
if (G_UNLIKELY (!private_quark))
|
|
private_quark = g_quark_from_static_string (PRIVATE_TAG);
|
|
|
|
priv = g_object_get_qdata (G_OBJECT (self), private_quark);
|
|
if (!priv) {
|
|
priv = g_slice_new0 (Private);
|
|
g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free);
|
|
}
|
|
|
|
return priv;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Abort ongoing requests, if any */
|
|
|
|
void
|
|
mm_iface_modem_simple_abort_ongoing (MMIfaceModemSimple *self)
|
|
{
|
|
Private *priv;
|
|
|
|
priv = get_private (self);
|
|
if (priv->ongoing_connect) {
|
|
g_cancellable_cancel (priv->ongoing_connect);
|
|
g_clear_object (&priv->ongoing_connect);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Register in either a CDMA or a 3GPP network (or both) */
|
|
|
|
typedef struct {
|
|
gchar *operator_id;
|
|
guint remaining_tries_cdma;
|
|
guint remaining_tries_3gpp;
|
|
guint max_try_time;
|
|
} RegisterInNetworkContext;
|
|
|
|
static void
|
|
register_in_network_context_free (RegisterInNetworkContext *ctx)
|
|
{
|
|
g_free (ctx->operator_id);
|
|
g_slice_free (RegisterInNetworkContext, ctx);
|
|
}
|
|
|
|
static gboolean
|
|
register_in_3gpp_or_cdma_network_finish (MMIfaceModemSimple *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_boolean (G_TASK (res), error);
|
|
}
|
|
|
|
static void check_next_registration (GTask *task);
|
|
|
|
static void
|
|
register_in_cdma_network_ready (MMIfaceModemCdma *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
RegisterInNetworkContext *ctx;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
ctx->remaining_tries_cdma--;
|
|
|
|
if (!mm_iface_modem_cdma_register_in_network_finish (self, res, NULL)) {
|
|
/* Retry check */
|
|
check_next_registration (task);
|
|
return;
|
|
}
|
|
|
|
/* Registered we are! */
|
|
mm_obj_dbg (self, "registration in network: successful (cdma)");
|
|
g_task_return_boolean (task, TRUE);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
register_in_3gpp_network_ready (MMIfaceModem3gpp *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
RegisterInNetworkContext *ctx;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
ctx->remaining_tries_3gpp--;
|
|
|
|
if (!mm_iface_modem_3gpp_register_in_network_finish (self, res, NULL)) {
|
|
/* Retry check */
|
|
check_next_registration (task);
|
|
return;
|
|
}
|
|
|
|
/* Registered we are! */
|
|
mm_obj_dbg (self, "registration in network: successful (3gpp)");
|
|
g_task_return_boolean (task, TRUE);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
check_next_registration (GTask *task)
|
|
{
|
|
RegisterInNetworkContext *ctx;
|
|
MMIfaceModemSimple *self;
|
|
|
|
self = MM_IFACE_MODEM_SIMPLE (g_task_get_source_object (task));
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
if (g_task_return_error_if_cancelled (task)) {
|
|
mm_obj_dbg (self, "registration in network: cancelled");
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
if (ctx->remaining_tries_cdma > ctx->remaining_tries_3gpp &&
|
|
ctx->remaining_tries_cdma > 0) {
|
|
mm_iface_modem_cdma_register_in_network (
|
|
MM_IFACE_MODEM_CDMA (self),
|
|
ctx->max_try_time,
|
|
(GAsyncReadyCallback)register_in_cdma_network_ready,
|
|
task);
|
|
return;
|
|
}
|
|
|
|
if (ctx->remaining_tries_3gpp > 0) {
|
|
mm_iface_modem_3gpp_register_in_network (
|
|
MM_IFACE_MODEM_3GPP (self),
|
|
ctx->operator_id,
|
|
FALSE, /* if already registered with same settings, do nothing */
|
|
ctx->max_try_time,
|
|
(GAsyncReadyCallback)register_in_3gpp_network_ready,
|
|
task);
|
|
return;
|
|
}
|
|
|
|
/* No more tries of anything */
|
|
mm_obj_dbg (self, "registration in network: timed out");
|
|
g_task_return_error (
|
|
task,
|
|
mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT, self));
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
register_in_3gpp_or_cdma_network (MMIfaceModemSimple *self,
|
|
const gchar *operator_id,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
RegisterInNetworkContext *ctx;
|
|
GTask *task;
|
|
|
|
ctx = g_slice_new0 (RegisterInNetworkContext);
|
|
ctx->operator_id = g_strdup (operator_id);
|
|
|
|
/* 3GPP-only modems... */
|
|
if (mm_iface_modem_is_3gpp_only (MM_IFACE_MODEM (self))) {
|
|
ctx->max_try_time = 60;
|
|
ctx->remaining_tries_cdma = 0;
|
|
ctx->remaining_tries_3gpp = 1;
|
|
}
|
|
/* CDMA-only modems... */
|
|
else if (mm_iface_modem_is_cdma_only (MM_IFACE_MODEM (self))) {
|
|
ctx->max_try_time = 60;
|
|
ctx->remaining_tries_cdma = 1;
|
|
ctx->remaining_tries_3gpp = 0;
|
|
}
|
|
/* Mixed 3GPP+CDMA modems... */
|
|
else {
|
|
ctx->max_try_time = 10;
|
|
ctx->remaining_tries_cdma = 6;
|
|
ctx->remaining_tries_3gpp = 6;
|
|
}
|
|
|
|
task = g_task_new (self, cancellable, callback, user_data);
|
|
g_task_set_task_data (task, ctx, (GDestroyNotify)register_in_network_context_free);
|
|
|
|
check_next_registration (task);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Packet service attach in 3GPP network */
|
|
|
|
typedef enum {
|
|
PACKET_SERVICE_ATTACH_IN_3GPP_NETWORK_STEP_FIRST,
|
|
PACKET_SERVICE_ATTACH_IN_3GPP_NETWORK_STEP_WAIT_BEFORE,
|
|
PACKET_SERVICE_ATTACH_IN_3GPP_NETWORK_STEP_SET,
|
|
PACKET_SERVICE_ATTACH_IN_3GPP_NETWORK_STEP_WAIT_AFTER,
|
|
PACKET_SERVICE_ATTACH_IN_3GPP_NETWORK_STEP_LAST,
|
|
} PacketServiceAttachIn3gppNetworkStep;
|
|
|
|
typedef struct {
|
|
PacketServiceAttachIn3gppNetworkStep step;
|
|
GError *error;
|
|
} PacketServiceAttachIn3gppNetworkContext;
|
|
|
|
static void
|
|
packet_service_attach_in_3gpp_network_context_free (PacketServiceAttachIn3gppNetworkContext *ctx)
|
|
{
|
|
g_clear_error (&ctx->error);
|
|
g_slice_free (PacketServiceAttachIn3gppNetworkContext, ctx);
|
|
}
|
|
|
|
static gboolean
|
|
packet_service_attach_in_3gpp_network_finish (MMIfaceModemSimple *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_boolean (G_TASK (res), error);
|
|
}
|
|
|
|
static void packet_service_attach_in_3gpp_network_step (GTask *task);
|
|
|
|
static void
|
|
set_packet_service_state_ready (MMIfaceModem3gpp *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
PacketServiceAttachIn3gppNetworkContext *ctx;
|
|
g_autoptr(GError) error = NULL;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
g_assert (ctx->error);
|
|
|
|
if (!mm_iface_modem_3gpp_set_packet_service_state_finish (self, res, &error)) {
|
|
/* On an unsupported error, return the original wait failure; otherwise
|
|
* return the set error. */
|
|
if (!g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) {
|
|
g_error_free (ctx->error);
|
|
ctx->error = g_steal_pointer (&error);
|
|
}
|
|
/* Failures in the set are always fatal right away */
|
|
ctx->step = PACKET_SERVICE_ATTACH_IN_3GPP_NETWORK_STEP_LAST;
|
|
} else {
|
|
/* Retry if possible */
|
|
g_clear_error (&ctx->error);
|
|
ctx->step++;
|
|
}
|
|
packet_service_attach_in_3gpp_network_step (task);
|
|
}
|
|
|
|
static void
|
|
wait_for_packet_service_state_ready (MMIfaceModem3gpp *self,
|
|
GAsyncResult *res,
|
|
GTask *task)
|
|
{
|
|
PacketServiceAttachIn3gppNetworkContext *ctx;
|
|
|
|
ctx = g_task_get_task_data (task);
|
|
g_assert (!ctx->error);
|
|
|
|
mm_iface_modem_3gpp_wait_for_packet_service_state_finish (self, res, &ctx->error);
|
|
if (ctx->error)
|
|
ctx->step++;
|
|
else
|
|
ctx->step = PACKET_SERVICE_ATTACH_IN_3GPP_NETWORK_STEP_LAST;
|
|
packet_service_attach_in_3gpp_network_step (task);
|
|
}
|
|
|
|
static void
|
|
packet_service_attach_in_3gpp_network_step (GTask *task)
|
|
{
|
|
PacketServiceAttachIn3gppNetworkContext *ctx;
|
|
MMIfaceModemSimple *self;
|
|
|
|
self = g_task_get_source_object (task);
|
|
ctx = g_task_get_task_data (task);
|
|
|
|
if (g_task_return_error_if_cancelled (task)) {
|
|
mm_obj_dbg (self, "packet service attach in 3gpp network: cancelled");
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
switch (ctx->step) {
|
|
case PACKET_SERVICE_ATTACH_IN_3GPP_NETWORK_STEP_FIRST:
|
|
ctx->step++;
|
|
/* fall through */
|
|
|
|
case PACKET_SERVICE_ATTACH_IN_3GPP_NETWORK_STEP_WAIT_BEFORE:
|
|
g_assert (!ctx->error);
|
|
mm_iface_modem_3gpp_wait_for_packet_service_state (MM_IFACE_MODEM_3GPP (self),
|
|
MM_MODEM_3GPP_PACKET_SERVICE_STATE_ATTACHED,
|
|
g_task_get_cancellable (task),
|
|
(GAsyncReadyCallback)wait_for_packet_service_state_ready,
|
|
task);
|
|
return;
|
|
|
|
case PACKET_SERVICE_ATTACH_IN_3GPP_NETWORK_STEP_SET:
|
|
/* An explicit set will only be attempted if the packet service state wait failed */
|
|
g_assert (ctx->error);
|
|
mm_iface_modem_3gpp_set_packet_service_state (MM_IFACE_MODEM_3GPP (self),
|
|
MM_MODEM_3GPP_PACKET_SERVICE_STATE_ATTACHED,
|
|
(GAsyncReadyCallback)set_packet_service_state_ready,
|
|
task);
|
|
return;
|
|
|
|
case PACKET_SERVICE_ATTACH_IN_3GPP_NETWORK_STEP_WAIT_AFTER:
|
|
g_assert (!ctx->error);
|
|
mm_iface_modem_3gpp_wait_for_packet_service_state (MM_IFACE_MODEM_3GPP (self),
|
|
MM_MODEM_3GPP_PACKET_SERVICE_STATE_ATTACHED,
|
|
g_task_get_cancellable (task),
|
|
(GAsyncReadyCallback)wait_for_packet_service_state_ready,
|
|
task);
|
|
return;
|
|
|
|
case PACKET_SERVICE_ATTACH_IN_3GPP_NETWORK_STEP_LAST:
|
|
if (ctx->error) {
|
|
mm_obj_dbg (self, "packet service attach in 3gpp network: failed: %s", ctx->error->message);
|
|
g_task_return_error (task, g_steal_pointer (&ctx->error));
|
|
} else {
|
|
mm_obj_dbg (self, "packet service attach in 3gpp network: finished");
|
|
g_task_return_boolean (task, TRUE);
|
|
}
|
|
g_object_unref (task);
|
|
return;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
|
|
static void
|
|
packet_service_attach_in_3gpp_network (MMIfaceModemSimple *self,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
PacketServiceAttachIn3gppNetworkContext *ctx;
|
|
GTask *task;
|
|
|
|
task = g_task_new (self, cancellable, callback, user_data);
|
|
ctx = g_slice_new0 (PacketServiceAttachIn3gppNetworkContext);
|
|
ctx->step = PACKET_SERVICE_ATTACH_IN_3GPP_NETWORK_STEP_FIRST;
|
|
g_task_set_task_data (task, ctx, (GDestroyNotify)packet_service_attach_in_3gpp_network_context_free);
|
|
|
|
packet_service_attach_in_3gpp_network_step (task);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
typedef enum {
|
|
CONNECTION_STEP_FIRST,
|
|
CONNECTION_STEP_UNLOCK_CHECK,
|
|
CONNECTION_STEP_WAIT_FOR_INITIALIZED,
|
|
CONNECTION_STEP_ENABLE,
|
|
CONNECTION_STEP_WAIT_FOR_ENABLED,
|
|
CONNECTION_STEP_WAIT_AFTER_ENABLED,
|
|
CONNECTION_STEP_REGISTER,
|
|
CONNECTION_STEP_PACKET_SERVICE_ATTACH,
|
|
CONNECTION_STEP_BEARER,
|
|
CONNECTION_STEP_CONNECT,
|
|
CONNECTION_STEP_LAST
|
|
} ConnectionStep;
|
|
|
|
typedef struct {
|
|
MmGdbusModemSimple *skeleton;
|
|
GDBusMethodInvocation *invocation;
|
|
MMIfaceModemSimple *self;
|
|
ConnectionStep step;
|
|
MMBearerList *bearer_list;
|
|
|
|
/* Expected input properties */
|
|
GVariant *dictionary;
|
|
MMSimpleConnectProperties *properties;
|
|
|
|
/* Results to set */
|
|
MMBaseBearer *bearer;
|
|
|
|
/* Cancellation */
|
|
GCancellable *cancellable;
|
|
} ConnectionContext;
|
|
|
|
static void
|
|
cleanup_cancellation (ConnectionContext *ctx)
|
|
{
|
|
Private *priv;
|
|
|
|
/* The ongoing connect cancellable and the one in the connection context
|
|
* must be the same, as they're set together, so if the one in the
|
|
* context doesn't exist, do nothing. */
|
|
if (!ctx->cancellable)
|
|
return;
|
|
|
|
/* If the ongoing connect cancellable is cancelled via the Simple.Disconnect
|
|
* method, it won't exist in the private struct, so don't assume they
|
|
* both exist. */
|
|
priv = get_private (ctx->self);
|
|
g_clear_object (&priv->ongoing_connect);
|
|
g_clear_object (&ctx->cancellable);
|
|
}
|
|
|
|
static void
|
|
connection_context_free (ConnectionContext *ctx)
|
|
{
|
|
cleanup_cancellation (ctx);
|
|
g_clear_object (&ctx->properties);
|
|
g_clear_object (&ctx->bearer);
|
|
g_clear_object (&ctx->bearer_list);
|
|
g_variant_unref (ctx->dictionary);
|
|
g_object_unref (ctx->skeleton);
|
|
g_object_unref (ctx->invocation);
|
|
g_object_unref (ctx->self);
|
|
g_slice_free (ConnectionContext, ctx);
|
|
}
|
|
|
|
static void connection_step (ConnectionContext *ctx);
|
|
|
|
static void
|
|
connect_bearer_ready (MMBaseBearer *bearer,
|
|
GAsyncResult *res,
|
|
ConnectionContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!mm_base_bearer_connect_finish (bearer, res, &error)) {
|
|
mm_obj_warn (ctx->self, "couldn't connect bearer: %s", error->message);
|
|
mm_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
connection_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
ctx->step++;
|
|
connection_step (ctx);
|
|
}
|
|
|
|
static void
|
|
create_bearer_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
ConnectionContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
/* ownership for the caller */
|
|
ctx->bearer = mm_iface_modem_create_bearer_finish (self, res, &error);
|
|
if (!ctx->bearer) {
|
|
mm_obj_warn (ctx->self, "couldn't create bearer: %s", error->message);
|
|
mm_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
connection_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
ctx->step++;
|
|
connection_step (ctx);
|
|
}
|
|
|
|
static void
|
|
packet_service_attach_in_3gpp_network_ready (MMIfaceModemSimple *self,
|
|
GAsyncResult *res,
|
|
ConnectionContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!packet_service_attach_in_3gpp_network_finish (self, res, &error)) {
|
|
mm_obj_warn (ctx->self, "packet service attach in 3GPP network failed: %s", error->message);
|
|
mm_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
connection_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
ctx->step++;
|
|
connection_step (ctx);
|
|
}
|
|
|
|
static void
|
|
register_in_3gpp_or_cdma_network_ready (MMIfaceModemSimple *self,
|
|
GAsyncResult *res,
|
|
ConnectionContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!register_in_3gpp_or_cdma_network_finish (self, res, &error)) {
|
|
mm_obj_warn (ctx->self, "registration in network failed: %s", error->message);
|
|
mm_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
connection_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
ctx->step++;
|
|
connection_step (ctx);
|
|
}
|
|
|
|
static gboolean
|
|
wait_after_enabled_ready (ConnectionContext *ctx)
|
|
{
|
|
/* Just go on now */
|
|
ctx->step++;
|
|
connection_step (ctx);
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void
|
|
wait_for_enabled_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
ConnectionContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
MMModemState state;
|
|
|
|
state = mm_iface_modem_wait_for_final_state_finish (self, res, &error);
|
|
if (error) {
|
|
mm_obj_warn (ctx->self, "failed waiting for final state: %s", error->message);
|
|
mm_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
connection_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
if (state < MM_MODEM_STATE_ENABLED) {
|
|
mm_obj_warn (ctx->self, "failed waiting for enabled state: %s", mm_modem_state_get_string (state));
|
|
mm_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
|
|
"Couldn't wait for 'enabled': new state is '%s'",
|
|
mm_modem_state_get_string (state));
|
|
connection_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
ctx->step++;
|
|
connection_step (ctx);
|
|
}
|
|
|
|
static void
|
|
enable_ready (MMBaseModem *self,
|
|
GAsyncResult *res,
|
|
ConnectionContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!mm_base_modem_enable_finish (MM_BASE_MODEM (self), res, &error)) {
|
|
mm_obj_warn (ctx->self, "failed enabling modem: %s", error->message);
|
|
mm_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
connection_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
ctx->step++;
|
|
connection_step (ctx);
|
|
}
|
|
|
|
static void
|
|
wait_for_initialized_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
ConnectionContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
mm_iface_modem_wait_for_final_state_finish (self, res, &error);
|
|
if (error) {
|
|
mm_obj_warn (ctx->self, "failed waiting for final state: %s", error->message);
|
|
mm_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
connection_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
ctx->step++;
|
|
connection_step (ctx);
|
|
}
|
|
|
|
static void
|
|
send_pin_ready (MMBaseSim *sim,
|
|
GAsyncResult *res,
|
|
ConnectionContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!mm_base_sim_send_pin_finish (sim, res, &error)) {
|
|
mm_obj_warn (ctx->self, "failed sending SIM PIN: %s", error->message);
|
|
mm_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
connection_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
ctx->step++;
|
|
connection_step (ctx);
|
|
}
|
|
|
|
static void
|
|
update_lock_info_ready (MMIfaceModem *self,
|
|
GAsyncResult *res,
|
|
ConnectionContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
MMModemLock lock;
|
|
g_autoptr(MMBaseSim) sim = NULL;
|
|
|
|
lock = mm_iface_modem_update_lock_info_finish (self, res, &error);
|
|
if (error) {
|
|
mm_obj_warn (ctx->self, "failed updating lock info: %s", error->message);
|
|
mm_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
connection_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
/* If we are already unlocked, go on to next step. Note that we do also
|
|
* allow SIM-PIN2/SIM-PUK2, as we don't need to unlock that in order to get
|
|
* connected. */
|
|
if (lock == MM_MODEM_LOCK_NONE ||
|
|
lock == MM_MODEM_LOCK_SIM_PIN2 ||
|
|
lock == MM_MODEM_LOCK_SIM_PUK2) {
|
|
ctx->step++;
|
|
connection_step (ctx);
|
|
return;
|
|
}
|
|
|
|
/* During simple connect we are only allowed to use SIM PIN */
|
|
if (lock != MM_MODEM_LOCK_SIM_PIN ||
|
|
!mm_simple_connect_properties_get_pin (ctx->properties)) {
|
|
mm_obj_warn (ctx->self, "modem is locked with '%s'",
|
|
mm_modem_lock_get_string (lock));
|
|
mm_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNAUTHORIZED,
|
|
"Modem is locked with '%s' code; cannot unlock it",
|
|
mm_modem_lock_get_string (lock));
|
|
connection_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
/* Try to unlock the modem providing the PIN */
|
|
g_object_get (ctx->self,
|
|
MM_IFACE_MODEM_SIM, &sim,
|
|
NULL);
|
|
if (!sim) {
|
|
mm_obj_warn (ctx->self, "SIM is not accessible");
|
|
mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"Cannot unlock modem, couldn't get access to the SIM");
|
|
connection_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
mm_base_sim_send_pin (sim,
|
|
mm_simple_connect_properties_get_pin (ctx->properties),
|
|
(GAsyncReadyCallback)send_pin_ready,
|
|
ctx);
|
|
}
|
|
|
|
static gboolean
|
|
completed_if_cancelled (ConnectionContext *ctx)
|
|
{
|
|
/* Do nothing if not cancelled */
|
|
if (!g_cancellable_is_cancelled (ctx->cancellable))
|
|
return FALSE;
|
|
|
|
/* Otherwise cancellable is valid and it's cancelled */
|
|
mm_obj_warn (ctx->self, "connection attempt cancelled");
|
|
mm_dbus_method_invocation_return_error_literal (ctx->invocation, G_IO_ERROR, G_IO_ERROR_CANCELLED,
|
|
"Connection attempt cancelled");
|
|
connection_context_free (ctx);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
setup_cancellation (ConnectionContext *ctx,
|
|
GError **error)
|
|
{
|
|
Private *priv;
|
|
|
|
/* Only one connection attempt by Simple.Connect() may be handled at a time */
|
|
priv = get_private (ctx->self);
|
|
if (priv->ongoing_connect) {
|
|
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS,
|
|
"Connection request forbidden: operation already in progress");
|
|
return FALSE;
|
|
}
|
|
|
|
g_assert (!ctx->cancellable);
|
|
ctx->cancellable = g_cancellable_new ();
|
|
priv->ongoing_connect = g_object_ref (ctx->cancellable);
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
connection_step (ConnectionContext *ctx)
|
|
{
|
|
/* Early abort if operation is cancelled */
|
|
if (completed_if_cancelled (ctx))
|
|
return;
|
|
|
|
switch (ctx->step) {
|
|
case CONNECTION_STEP_FIRST:
|
|
ctx->step++;
|
|
/* fall through */
|
|
|
|
case CONNECTION_STEP_UNLOCK_CHECK:
|
|
mm_obj_msg (ctx->self, "simple connect state (%d/%d): unlock check",
|
|
ctx->step, CONNECTION_STEP_LAST);
|
|
mm_iface_modem_update_lock_info (MM_IFACE_MODEM (ctx->self),
|
|
MM_MODEM_LOCK_UNKNOWN, /* ask */
|
|
(GAsyncReadyCallback)update_lock_info_ready,
|
|
ctx);
|
|
return;
|
|
|
|
case CONNECTION_STEP_WAIT_FOR_INITIALIZED:
|
|
mm_obj_msg (ctx->self, "simple connect state (%d/%d): wait to get fully initialized",
|
|
ctx->step, CONNECTION_STEP_LAST);
|
|
mm_iface_modem_wait_for_final_state (MM_IFACE_MODEM (ctx->self),
|
|
MM_MODEM_STATE_DISABLED, /* disabled == initialized */
|
|
(GAsyncReadyCallback)wait_for_initialized_ready,
|
|
ctx);
|
|
return;
|
|
|
|
case CONNECTION_STEP_ENABLE:
|
|
mm_obj_msg (ctx->self, "simple connect state (%d/%d): enable",
|
|
ctx->step, CONNECTION_STEP_LAST);
|
|
mm_base_modem_enable (MM_BASE_MODEM (ctx->self),
|
|
MM_OPERATION_LOCK_REQUIRED,
|
|
(GAsyncReadyCallback)enable_ready,
|
|
ctx);
|
|
return;
|
|
|
|
case CONNECTION_STEP_WAIT_FOR_ENABLED:
|
|
mm_obj_msg (ctx->self, "simple connect state (%d/%d): wait to get fully enabled",
|
|
ctx->step, CONNECTION_STEP_LAST);
|
|
mm_iface_modem_wait_for_final_state (MM_IFACE_MODEM (ctx->self),
|
|
MM_MODEM_STATE_UNKNOWN, /* just a final state */
|
|
(GAsyncReadyCallback)wait_for_enabled_ready,
|
|
ctx);
|
|
return;
|
|
|
|
case CONNECTION_STEP_WAIT_AFTER_ENABLED:
|
|
mm_obj_msg (ctx->self, "simple connect state (%d/%d): wait after enabled",
|
|
ctx->step, CONNECTION_STEP_LAST);
|
|
/* When we have just enabled, we want to give it some time before starting
|
|
* the registration process, so that any pending registration update that may
|
|
* have been scheduled during the enabling phase is applied. We don't want to
|
|
* trigger a new automatic registration if e.g. we're already registered. */
|
|
g_timeout_add (100, (GSourceFunc)wait_after_enabled_ready, ctx);
|
|
return;
|
|
|
|
|
|
case CONNECTION_STEP_REGISTER:
|
|
mm_obj_msg (ctx->self, "simple connect state (%d/%d): register",
|
|
ctx->step, CONNECTION_STEP_LAST);
|
|
if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (ctx->self)) ||
|
|
mm_iface_modem_is_cdma (MM_IFACE_MODEM (ctx->self))) {
|
|
/* 3GPP or CDMA registration */
|
|
register_in_3gpp_or_cdma_network (
|
|
ctx->self,
|
|
mm_simple_connect_properties_get_operator_id (ctx->properties),
|
|
ctx->cancellable,
|
|
(GAsyncReadyCallback)register_in_3gpp_or_cdma_network_ready,
|
|
ctx);
|
|
return;
|
|
}
|
|
|
|
/* If not 3GPP and not CDMA, this will possibly be a POTS modem,
|
|
* which won't require any specific registration anywhere. */
|
|
ctx->step++;
|
|
/* fall through */
|
|
|
|
case CONNECTION_STEP_PACKET_SERVICE_ATTACH:
|
|
mm_obj_msg (ctx->self, "simple connect state (%d/%d): wait to get packet service state attached",
|
|
ctx->step, CONNECTION_STEP_LAST);
|
|
if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (ctx->self))) {
|
|
packet_service_attach_in_3gpp_network (
|
|
ctx->self,
|
|
ctx->cancellable,
|
|
(GAsyncReadyCallback)packet_service_attach_in_3gpp_network_ready,
|
|
ctx);
|
|
return;
|
|
}
|
|
/* If not 3GPP, just go on */
|
|
ctx->step++;
|
|
/* fall through */
|
|
|
|
case CONNECTION_STEP_BEARER: {
|
|
g_autoptr(MMBearerProperties) bearer_properties = NULL;
|
|
|
|
mm_obj_msg (ctx->self, "simple connect state (%d/%d): bearer",
|
|
ctx->step, CONNECTION_STEP_LAST);
|
|
|
|
bearer_properties = mm_simple_connect_properties_get_bearer_properties (ctx->properties);
|
|
|
|
/* Check if the bearer we want to create is already in the list */
|
|
ctx->bearer = mm_bearer_list_find_by_properties (ctx->bearer_list, bearer_properties);
|
|
if (!ctx->bearer) {
|
|
mm_obj_dbg (ctx->self, "creating new bearer...");
|
|
mm_iface_modem_create_bearer (MM_IFACE_MODEM (ctx->self),
|
|
bearer_properties,
|
|
(GAsyncReadyCallback)create_bearer_ready,
|
|
ctx);
|
|
return;
|
|
}
|
|
|
|
mm_obj_dbg (ctx->self, "Using already existing bearer at '%s'...",
|
|
mm_base_bearer_get_path (ctx->bearer));
|
|
ctx->step++;
|
|
} /* fall through */
|
|
|
|
case CONNECTION_STEP_CONNECT:
|
|
mm_obj_msg (ctx->self, "simple connect state (%d/%d): connect",
|
|
ctx->step, CONNECTION_STEP_LAST);
|
|
|
|
/* At this point, we can cleanup the cancellation point in the Simple interface,
|
|
* because the bearer connection has its own cancellation setup. */
|
|
cleanup_cancellation (ctx);
|
|
|
|
/* Wait... if we're already using an existing bearer, we need to check if it is
|
|
* already connected; and if so, just don't do anything else */
|
|
if (mm_base_bearer_get_status (ctx->bearer) != MM_BEARER_STATUS_CONNECTED) {
|
|
mm_base_bearer_connect (ctx->bearer,
|
|
(GAsyncReadyCallback)connect_bearer_ready,
|
|
ctx);
|
|
return;
|
|
}
|
|
|
|
mm_obj_info (ctx->self, "bearer at '%s' is already connected...", mm_base_bearer_get_path (ctx->bearer));
|
|
|
|
ctx->step++;
|
|
/* fall through */
|
|
|
|
case CONNECTION_STEP_LAST:
|
|
mm_obj_msg (ctx->self, "simple connect state (%d/%d): all done",
|
|
ctx->step, CONNECTION_STEP_LAST);
|
|
/* All done, yey! */
|
|
mm_gdbus_modem_simple_complete_connect (
|
|
ctx->skeleton,
|
|
ctx->invocation,
|
|
mm_base_bearer_get_path (ctx->bearer));
|
|
connection_context_free (ctx);
|
|
return;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
static void
|
|
connect_auth_ready (MMIfaceAuth *_self,
|
|
GAsyncResult *res,
|
|
ConnectionContext *ctx)
|
|
{
|
|
MMIfaceModemSimple *self = MM_IFACE_MODEM_SIMPLE (_self);
|
|
GError *error = NULL;
|
|
MMModemState current = MM_MODEM_STATE_UNKNOWN;
|
|
|
|
if (!mm_iface_auth_authorize_finish (_self, res, &error)) {
|
|
mm_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
connection_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
ctx->properties = mm_simple_connect_properties_new_from_dictionary (ctx->dictionary, &error);
|
|
if (!ctx->properties) {
|
|
mm_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
connection_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
mm_obj_info (self, "processing user request to connect modem...");
|
|
mm_log_simple_connect_properties (self, MM_LOG_LEVEL_INFO, " ", ctx->properties);
|
|
|
|
if (!setup_cancellation (ctx, &error)) {
|
|
mm_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
connection_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
/* We may be able to skip some steps, so check that before doing anything */
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_STATE, ¤t,
|
|
MM_IFACE_MODEM_BEARER_LIST, &ctx->bearer_list,
|
|
NULL);
|
|
if (!ctx->bearer_list) {
|
|
mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"Couldn't get the bearer list");
|
|
connection_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
mm_obj_msg (self, "simple connect started...");
|
|
|
|
switch (current) {
|
|
case MM_MODEM_STATE_FAILED:
|
|
case MM_MODEM_STATE_UNKNOWN:
|
|
case MM_MODEM_STATE_LOCKED:
|
|
/* If we need unlocking, start from the very beginning */
|
|
ctx->step = CONNECTION_STEP_FIRST;
|
|
break;
|
|
|
|
case MM_MODEM_STATE_INITIALIZING:
|
|
case MM_MODEM_STATE_DISABLING:
|
|
/* If we are transitioning to the DISABLED (initialized) state,
|
|
* wait to get there before going on */
|
|
ctx->step = CONNECTION_STEP_WAIT_FOR_INITIALIZED;
|
|
break;
|
|
|
|
case MM_MODEM_STATE_DISABLED:
|
|
ctx->step = CONNECTION_STEP_ENABLE;
|
|
break;
|
|
|
|
case MM_MODEM_STATE_ENABLING:
|
|
case MM_MODEM_STATE_DISCONNECTING:
|
|
case MM_MODEM_STATE_SEARCHING:
|
|
case MM_MODEM_STATE_CONNECTING:
|
|
/* Wait to get to a final state before going on */
|
|
ctx->step = CONNECTION_STEP_WAIT_FOR_ENABLED;
|
|
break;
|
|
|
|
case MM_MODEM_STATE_ENABLED:
|
|
case MM_MODEM_STATE_REGISTERED:
|
|
case MM_MODEM_STATE_CONNECTED:
|
|
/* If we are at least already enabled, start at the registration check
|
|
* right away */
|
|
ctx->step = CONNECTION_STEP_REGISTER;
|
|
break;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
break;
|
|
}
|
|
connection_step (ctx);
|
|
}
|
|
|
|
static gboolean
|
|
handle_connect (MmGdbusModemSimple *skeleton,
|
|
GDBusMethodInvocation *invocation,
|
|
GVariant *dictionary,
|
|
MMIfaceModemSimple *self)
|
|
{
|
|
ConnectionContext *ctx;
|
|
|
|
ctx = g_slice_new0 (ConnectionContext);
|
|
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_iface_auth_authorize (MM_IFACE_AUTH (self),
|
|
invocation,
|
|
MM_AUTHORIZATION_DEVICE_CONTROL,
|
|
(GAsyncReadyCallback)connect_auth_ready,
|
|
ctx);
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
typedef struct {
|
|
MMIfaceModemSimple *self;
|
|
MmGdbusModemSimple *skeleton;
|
|
GDBusMethodInvocation *invocation;
|
|
gchar *bearer_path;
|
|
} DisconnectionContext;
|
|
|
|
static void
|
|
disconnection_context_free (DisconnectionContext *ctx)
|
|
{
|
|
g_object_unref (ctx->skeleton);
|
|
g_object_unref (ctx->invocation);
|
|
g_object_unref (ctx->self);
|
|
g_free (ctx->bearer_path);
|
|
g_slice_free (DisconnectionContext, ctx);
|
|
}
|
|
|
|
static void
|
|
bearer_list_disconnect_bearers_ready (MMBearerList *bearer_list,
|
|
GAsyncResult *res,
|
|
DisconnectionContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!mm_bearer_list_disconnect_bearers_finish (bearer_list, res, &error)) {
|
|
mm_obj_warn (ctx->self, "failed to disconnect bearers: %s", error->message);
|
|
mm_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
} else {
|
|
mm_obj_info (ctx->self, "all requested bearers disconnected");
|
|
mm_gdbus_modem_simple_complete_disconnect (ctx->skeleton, ctx->invocation);
|
|
}
|
|
disconnection_context_free (ctx);
|
|
}
|
|
|
|
static void
|
|
disconnect_auth_ready (MMIfaceAuth *_self,
|
|
GAsyncResult *res,
|
|
DisconnectionContext *ctx)
|
|
{
|
|
MMIfaceModemSimple *self = MM_IFACE_MODEM_SIMPLE (_self);
|
|
g_autoptr(MMBearerList) list = NULL;
|
|
GError *error = NULL;
|
|
|
|
if (!mm_iface_auth_authorize_finish (_self, res, &error)) {
|
|
mm_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
disconnection_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
/* If not disconnecting a specific bearer, also cancel any ongoing
|
|
* connection attempt. */
|
|
if (!ctx->bearer_path)
|
|
mm_iface_modem_simple_abort_ongoing (MM_IFACE_MODEM_SIMPLE (self));
|
|
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_BEARER_LIST, &list,
|
|
NULL);
|
|
if (!list) {
|
|
mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"Couldn't get the bearer list");
|
|
disconnection_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
if (ctx->bearer_path)
|
|
mm_obj_info (self, "processing user request to disconnect modem: bearer '%s'", ctx->bearer_path);
|
|
else
|
|
mm_obj_info (self, "processing user request to disconnect modem: all bearers");
|
|
|
|
mm_bearer_list_disconnect_bearers (list,
|
|
ctx->bearer_path,
|
|
(GAsyncReadyCallback)bearer_list_disconnect_bearers_ready,
|
|
ctx);
|
|
}
|
|
|
|
static gboolean
|
|
handle_disconnect (MmGdbusModemSimple *skeleton,
|
|
GDBusMethodInvocation *invocation,
|
|
const gchar *bearer_path,
|
|
MMIfaceModemSimple *self)
|
|
{
|
|
DisconnectionContext *ctx;
|
|
|
|
ctx = g_slice_new0 (DisconnectionContext);
|
|
ctx->skeleton = g_object_ref (skeleton);
|
|
ctx->self = g_object_ref (self);
|
|
ctx->invocation = g_object_ref (invocation);
|
|
|
|
/* The Disconnect() method expects a valid object path given in bearer path,
|
|
* it cannot be NULL, so we assume we get '/' when we're asked to disconnect
|
|
* all connected bearers, as that is what mm_modem_simple_disconnect() does
|
|
* when a NULL bearer path is given to that method.
|
|
*
|
|
* We will detect the '/' string and set the bearer path as NULL in the
|
|
* context if so, and otherwise use the given input string as path */
|
|
if (g_strcmp0 (bearer_path, "/") != 0)
|
|
ctx->bearer_path = g_strdup (bearer_path);
|
|
|
|
mm_iface_auth_authorize (MM_IFACE_AUTH (self),
|
|
invocation,
|
|
MM_AUTHORIZATION_DEVICE_CONTROL,
|
|
(GAsyncReadyCallback)disconnect_auth_ready,
|
|
ctx);
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static gboolean
|
|
handle_get_status (MmGdbusModemSimple *skeleton,
|
|
GDBusMethodInvocation *invocation,
|
|
MMIfaceModemSimple *self)
|
|
{
|
|
MMSimpleStatus *status = NULL;
|
|
GVariant *dictionary;
|
|
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_SIMPLE_STATUS, &status,
|
|
NULL);
|
|
|
|
dictionary = mm_simple_status_get_dictionary (status);
|
|
mm_gdbus_modem_simple_complete_get_status (skeleton, invocation, dictionary);
|
|
g_variant_unref (dictionary);
|
|
|
|
g_object_unref (status);
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void
|
|
mm_iface_modem_simple_initialize (MMIfaceModemSimple *self)
|
|
{
|
|
MmGdbusModemSimple *skeleton = NULL;
|
|
|
|
/* Did we already create it? */
|
|
g_object_get (self,
|
|
MM_IFACE_MODEM_SIMPLE_DBUS_SKELETON, &skeleton,
|
|
NULL);
|
|
if (!skeleton) {
|
|
skeleton = mm_gdbus_modem_simple_skeleton_new ();
|
|
|
|
g_object_set (self,
|
|
MM_IFACE_MODEM_SIMPLE_DBUS_SKELETON, skeleton,
|
|
NULL);
|
|
|
|
/* Handle method invocations */
|
|
g_signal_connect (skeleton,
|
|
"handle-connect",
|
|
G_CALLBACK (handle_connect),
|
|
self);
|
|
g_signal_connect (skeleton,
|
|
"handle-disconnect",
|
|
G_CALLBACK (handle_disconnect),
|
|
self);
|
|
g_signal_connect (skeleton,
|
|
"handle-get-status",
|
|
G_CALLBACK (handle_get_status),
|
|
self);
|
|
|
|
/* Finally, export the new interface */
|
|
mm_gdbus_object_skeleton_set_modem_simple (MM_GDBUS_OBJECT_SKELETON (self),
|
|
MM_GDBUS_MODEM_SIMPLE (skeleton));
|
|
}
|
|
g_object_unref (skeleton);
|
|
}
|
|
|
|
void
|
|
mm_iface_modem_simple_shutdown (MMIfaceModemSimple *self)
|
|
{
|
|
/* Unexport DBus interface and remove the skeleton */
|
|
mm_gdbus_object_skeleton_set_modem_simple (MM_GDBUS_OBJECT_SKELETON (self), NULL);
|
|
g_object_set (self,
|
|
MM_IFACE_MODEM_SIMPLE_DBUS_SKELETON, NULL,
|
|
NULL);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
mm_iface_modem_simple_default_init (MMIfaceModemSimpleInterface *iface)
|
|
{
|
|
static gsize initialized = 0;
|
|
|
|
if (!g_once_init_enter (&initialized))
|
|
return;
|
|
|
|
/* Properties */
|
|
g_object_interface_install_property (
|
|
iface,
|
|
g_param_spec_object (MM_IFACE_MODEM_SIMPLE_DBUS_SKELETON,
|
|
"Simple DBus skeleton",
|
|
"DBus skeleton for the Simple interface",
|
|
MM_GDBUS_TYPE_MODEM_SIMPLE_SKELETON,
|
|
G_PARAM_READWRITE));
|
|
|
|
g_object_interface_install_property (
|
|
iface,
|
|
g_param_spec_object (MM_IFACE_MODEM_SIMPLE_STATUS,
|
|
"Simple status",
|
|
"Compilation of status values",
|
|
MM_TYPE_SIMPLE_STATUS,
|
|
G_PARAM_READWRITE));
|
|
|
|
g_once_init_leave (&initialized, 1);
|
|
}
|