Files
ModemManager/src/mm-bearer.c
T
Aleksander Morgado 0ff904b3c5 bearer: set state as DISCONNECTING as soon as we cancel the connection sequence
Just when we request to cancel the connection we need to set the bearer as
being disconnected, so that we do not end up overwriting connection cancellation
requests.

Reported by: Thieu Le <thieule@chromium.org>
2012-07-31 09:18:45 +02:00

885 lines
28 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) 2008 - 2009 Novell, Inc.
* Copyright (C) 2009 - 2011 Red Hat, Inc.
* Copyright (C) 2011 Google, Inc.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <ModemManager.h>
#include <libmm-common.h>
#include "mm-daemon-enums-types.h"
#include "mm-iface-modem.h"
#include "mm-bearer.h"
#include "mm-base-modem-at.h"
#include "mm-base-modem.h"
#include "mm-utils.h"
#include "mm-log.h"
#include "mm-modem-helpers.h"
/* We require up to 20s to get a proper IP when using PPP */
#define MM_BEARER_IP_TIMEOUT_DEFAULT 20
G_DEFINE_TYPE (MMBearer, mm_bearer, MM_GDBUS_TYPE_BEARER_SKELETON);
enum {
PROP_0,
PROP_PATH,
PROP_CONNECTION,
PROP_MODEM,
PROP_STATUS,
PROP_CONFIG,
PROP_LAST
};
static GParamSpec *properties[PROP_LAST];
struct _MMBearerPrivate {
/* The connection to the system bus */
GDBusConnection *connection;
/* The modem which owns this BEARER */
MMBaseModem *modem;
/* The path where the BEARER object is exported */
gchar *path;
/* Status of this bearer */
MMBearerStatus status;
/* Configuration of the bearer */
MMBearerProperties *config;
/* Cancellable for connect() */
GCancellable *connect_cancellable;
/* handler id for the disconnect + cancel connect request */
gulong disconnect_signal_handler;
};
/*****************************************************************************/
void
mm_bearer_export (MMBearer *self)
{
static guint id = 0;
gchar *path;
path = g_strdup_printf (MM_DBUS_BEARER_PREFIX "/%d", id++);
g_object_set (self,
MM_BEARER_PATH, path,
NULL);
g_free (path);
}
/*****************************************************************************/
static void
bearer_reset_interface_status (MMBearer *self)
{
mm_gdbus_bearer_set_connected (MM_GDBUS_BEARER (self), FALSE);
mm_gdbus_bearer_set_suspended (MM_GDBUS_BEARER (self), FALSE);
mm_gdbus_bearer_set_interface (MM_GDBUS_BEARER (self), NULL);
mm_gdbus_bearer_set_ip4_config (
MM_GDBUS_BEARER (self),
mm_bearer_ip_config_get_dictionary (NULL));
mm_gdbus_bearer_set_ip6_config (
MM_GDBUS_BEARER (self),
mm_bearer_ip_config_get_dictionary (NULL));
}
static void
bearer_update_status (MMBearer *self,
MMBearerStatus status)
{
/* NOTE: we do allow status 'CONNECTED' here; it may happen if we go into
* DISCONNECTING and we cannot disconnect */
/* Update the property value */
self->priv->status = status;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_STATUS]);
/* Ensure that we don't expose any connection related data in the
* interface when going into disconnected state. */
if (self->priv->status == MM_BEARER_STATUS_DISCONNECTED)
bearer_reset_interface_status (self);
}
static void
bearer_update_status_connected (MMBearer *self,
const gchar *interface,
MMBearerIpConfig *ipv4_config,
MMBearerIpConfig *ipv6_config)
{
mm_gdbus_bearer_set_connected (MM_GDBUS_BEARER (self), TRUE);
mm_gdbus_bearer_set_suspended (MM_GDBUS_BEARER (self), FALSE);
mm_gdbus_bearer_set_interface (MM_GDBUS_BEARER (self), interface);
mm_gdbus_bearer_set_ip4_config (
MM_GDBUS_BEARER (self),
mm_bearer_ip_config_get_dictionary (ipv4_config));
mm_gdbus_bearer_set_ip6_config (
MM_GDBUS_BEARER (self),
mm_bearer_ip_config_get_dictionary (ipv6_config));
/* Update the property value */
self->priv->status = MM_BEARER_STATUS_CONNECTED;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_STATUS]);
}
/*****************************************************************************/
/* CONNECT */
gboolean
mm_bearer_connect_finish (MMBearer *self,
GAsyncResult *res,
GError **error)
{
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
}
static void
disconnect_after_cancel_ready (MMBearer *self,
GAsyncResult *res)
{
GError *error = NULL;
if (!MM_BEARER_GET_CLASS (self)->disconnect_finish (self, res, &error)) {
mm_warn ("Error disconnecting bearer '%s': '%s'. "
"Will assume disconnected anyway.",
self->priv->path,
error->message);
g_error_free (error);
}
else
mm_dbg ("Disconnected bearer '%s'", self->priv->path);
bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTED);
}
static void
connect_ready (MMBearer *self,
GAsyncResult *res,
GSimpleAsyncResult *simple)
{
GError *error = NULL;
gboolean launch_disconnect = FALSE;
MMPort *data = NULL;
MMBearerIpConfig *ipv4_config = NULL;
MMBearerIpConfig *ipv6_config = NULL;
/* NOTE: connect() implementations *MUST* handle cancellations themselves */
if (!MM_BEARER_GET_CLASS (self)->connect_finish (self,
res,
&data,
&ipv4_config,
&ipv6_config,
&error)) {
mm_dbg ("Couldn't connect bearer '%s': '%s'",
self->priv->path,
error->message);
if (g_error_matches (error,
MM_CORE_ERROR,
MM_CORE_ERROR_CANCELLED)) {
/* Will launch disconnection */
launch_disconnect = TRUE;
} else
bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTED);
g_simple_async_result_take_error (simple, error);
}
/* Handle cancellations detected after successful connection */
else if (g_cancellable_is_cancelled (self->priv->connect_cancellable)) {
mm_dbg ("Connected bearer '%s', but need to disconnect", self->priv->path);
g_clear_object (&data);
g_clear_object (&ipv4_config);
g_clear_object (&ipv6_config);
g_simple_async_result_set_error (
simple,
MM_CORE_ERROR,
MM_CORE_ERROR_CANCELLED,
"Bearer got connected, but had to disconnect after cancellation request");
launch_disconnect = TRUE;
}
else {
mm_dbg ("Connected bearer '%s'", self->priv->path);
/* Update bearer and interface status */
bearer_update_status_connected (self,
mm_port_get_device (data),
ipv4_config,
ipv6_config);
g_clear_object (&data);
g_clear_object (&ipv4_config);
g_clear_object (&ipv6_config);
g_simple_async_result_set_op_res_gboolean (simple, TRUE);
}
if (launch_disconnect) {
bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTING);
MM_BEARER_GET_CLASS (self)->disconnect (
self,
(GAsyncReadyCallback)disconnect_after_cancel_ready,
NULL);
}
g_simple_async_result_complete (simple);
g_object_unref (simple);
}
void
mm_bearer_connect (MMBearer *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *result;
g_assert (MM_BEARER_GET_CLASS (self)->connect != NULL);
g_assert (MM_BEARER_GET_CLASS (self)->connect_finish != NULL);
/* If already connecting, return error, don't allow a second request. */
if (self->priv->status == MM_BEARER_STATUS_CONNECTING) {
g_simple_async_report_error_in_idle (
G_OBJECT (self),
callback,
user_data,
MM_CORE_ERROR,
MM_CORE_ERROR_IN_PROGRESS,
"Bearer already being connected");
return;
}
/* If currently disconnecting, return error, previous operation should
* finish before allowing to connect again. */
if (self->priv->status == MM_BEARER_STATUS_DISCONNECTING) {
g_simple_async_report_error_in_idle (
G_OBJECT (self),
callback,
user_data,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Bearer currently being disconnected");
return;
}
result = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
mm_bearer_connect);
/* If already connected, done */
if (self->priv->status == MM_BEARER_STATUS_CONNECTED) {
g_simple_async_result_set_op_res_gboolean (result, TRUE);
g_simple_async_result_complete_in_idle (result);
g_object_unref (result);
return;
}
/* Connecting! */
mm_dbg ("Connecting bearer '%s'", self->priv->path);
self->priv->connect_cancellable = g_cancellable_new ();
bearer_update_status (self, MM_BEARER_STATUS_CONNECTING);
MM_BEARER_GET_CLASS (self)->connect (
self,
self->priv->connect_cancellable,
(GAsyncReadyCallback)connect_ready,
result);
}
typedef struct {
MMBearer *self;
MMBaseModem *modem;
GDBusMethodInvocation *invocation;
} HandleConnectContext;
static void
handle_connect_context_free (HandleConnectContext *ctx)
{
g_object_unref (ctx->invocation);
g_object_unref (ctx->modem);
g_object_unref (ctx->self);
g_free (ctx);
}
static void
handle_connect_ready (MMBearer *self,
GAsyncResult *res,
HandleConnectContext *ctx)
{
GError *error = NULL;
if (!mm_bearer_connect_finish (self, res, &error))
g_dbus_method_invocation_take_error (ctx->invocation, error);
else
mm_gdbus_bearer_complete_connect (MM_GDBUS_BEARER (self), ctx->invocation);
handle_connect_context_free (ctx);
}
static void
handle_connect_auth_ready (MMBaseModem *modem,
GAsyncResult *res,
HandleConnectContext *ctx)
{
GError *error = NULL;
if (!mm_base_modem_authorize_finish (modem, res, &error)) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
handle_connect_context_free (ctx);
return;
}
mm_bearer_connect (ctx->self,
(GAsyncReadyCallback)handle_connect_ready,
ctx);
}
static gboolean
handle_connect (MMBearer *self,
GDBusMethodInvocation *invocation)
{
HandleConnectContext *ctx;
ctx = g_new0 (HandleConnectContext, 1);
ctx->self = g_object_ref (self);
ctx->invocation = g_object_ref (invocation);
g_object_get (self,
MM_BEARER_MODEM, &ctx->modem,
NULL);
mm_base_modem_authorize (ctx->modem,
invocation,
MM_AUTHORIZATION_DEVICE_CONTROL,
(GAsyncReadyCallback)handle_connect_auth_ready,
ctx);
return TRUE;
}
/*****************************************************************************/
/* DISCONNECT */
gboolean
mm_bearer_disconnect_finish (MMBearer *self,
GAsyncResult *res,
GError **error)
{
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
}
static void
disconnect_ready (MMBearer *self,
GAsyncResult *res,
GSimpleAsyncResult *simple)
{
GError *error = NULL;
if (!MM_BEARER_GET_CLASS (self)->disconnect_finish (self, res, &error)) {
mm_dbg ("Couldn't disconnect bearer '%s'", self->priv->path);
bearer_update_status (self, MM_BEARER_STATUS_CONNECTED);
g_simple_async_result_take_error (simple, error);
}
else {
mm_dbg ("Disconnected bearer '%s'", self->priv->path);
bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTED);
g_simple_async_result_set_op_res_gboolean (simple, TRUE);
}
g_simple_async_result_complete (simple);
g_object_unref (simple);
}
static void
status_changed_complete_disconnect (MMBearer *self,
GParamSpec *pspec,
GSimpleAsyncResult *simple)
{
/* We may get other states here before DISCONNECTED, like DISCONNECTING or
* even CONNECTED. */
if (self->priv->status != MM_BEARER_STATUS_DISCONNECTED)
return;
mm_dbg ("Disconnected bearer '%s' after cancelling previous connect request",
self->priv->path);
g_signal_handler_disconnect (self,
self->priv->disconnect_signal_handler);
self->priv->disconnect_signal_handler = 0;
/* Note: interface state is updated when the DISCONNECTED state is set */
g_simple_async_result_set_op_res_gboolean (simple, TRUE);
g_simple_async_result_complete (simple);
g_object_unref (simple);
}
void
mm_bearer_disconnect (MMBearer *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *simple;
g_assert (MM_BEARER_GET_CLASS (self)->disconnect != NULL);
g_assert (MM_BEARER_GET_CLASS (self)->disconnect_finish != NULL);
simple = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
mm_bearer_disconnect);
/* If already disconnected, done */
if (self->priv->status == MM_BEARER_STATUS_DISCONNECTED) {
g_simple_async_result_set_op_res_gboolean (simple, TRUE);
g_simple_async_result_complete_in_idle (simple);
g_object_unref (simple);
return;
}
/* If already disconnecting, return error, don't allow a second request. */
if (self->priv->status == MM_BEARER_STATUS_DISCONNECTING) {
g_simple_async_result_set_error (
simple,
MM_CORE_ERROR,
MM_CORE_ERROR_IN_PROGRESS,
"Bearer already being disconnected");
g_simple_async_result_complete_in_idle (simple);
g_object_unref (simple);
return;
}
mm_dbg ("Disconnecting bearer '%s'", self->priv->path);
/* If currently connecting, try to cancel that operation, and wait to get
* disconnected. */
if (self->priv->status == MM_BEARER_STATUS_CONNECTING) {
/* Set ourselves as disconnecting */
bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTING);
/* We MUST ensure that we get to DISCONNECTED */
g_cancellable_cancel (self->priv->connect_cancellable);
/* Note that we only allow to remove disconnected bearers, so should
* be safe to assume that we'll get the signal handler called properly
*/
self->priv->disconnect_signal_handler =
g_signal_connect (self,
"notify::" MM_BEARER_STATUS,
(GCallback)status_changed_complete_disconnect,
simple); /* takes ownership */
return;
}
/* Disconnecting! */
bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTING);
MM_BEARER_GET_CLASS (self)->disconnect (
self,
(GAsyncReadyCallback)disconnect_ready,
simple); /* takes ownership */
}
typedef struct {
MMBearer *self;
MMBaseModem *modem;
GDBusMethodInvocation *invocation;
} HandleDisconnectContext;
static void
handle_disconnect_context_free (HandleDisconnectContext *ctx)
{
g_object_unref (ctx->invocation);
g_object_unref (ctx->modem);
g_object_unref (ctx->self);
g_free (ctx);
}
static void
handle_disconnect_ready (MMBearer *self,
GAsyncResult *res,
HandleDisconnectContext *ctx)
{
GError *error = NULL;
if (!mm_bearer_disconnect_finish (self, res, &error))
g_dbus_method_invocation_take_error (ctx->invocation, error);
else
mm_gdbus_bearer_complete_disconnect (MM_GDBUS_BEARER (self), ctx->invocation);
handle_disconnect_context_free (ctx);
}
static void
handle_disconnect_auth_ready (MMBaseModem *modem,
GAsyncResult *res,
HandleDisconnectContext *ctx)
{
GError *error = NULL;
if (!mm_base_modem_authorize_finish (modem, res, &error)) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
handle_disconnect_context_free (ctx);
return;
}
mm_bearer_disconnect (ctx->self,
(GAsyncReadyCallback)handle_disconnect_ready,
ctx);
}
static gboolean
handle_disconnect (MMBearer *self,
GDBusMethodInvocation *invocation)
{
HandleDisconnectContext *ctx;
ctx = g_new0 (HandleDisconnectContext, 1);
ctx->self = g_object_ref (self);
ctx->invocation = g_object_ref (invocation);
g_object_get (self,
MM_BEARER_MODEM, &ctx->modem,
NULL);
mm_base_modem_authorize (ctx->modem,
invocation,
MM_AUTHORIZATION_DEVICE_CONTROL,
(GAsyncReadyCallback)handle_disconnect_auth_ready,
ctx);
return TRUE;
}
/*****************************************************************************/
static void
mm_bearer_dbus_export (MMBearer *self)
{
GError *error = NULL;
/* Handle method invocations */
g_signal_connect (self,
"handle-connect",
G_CALLBACK (handle_connect),
NULL);
g_signal_connect (self,
"handle-disconnect",
G_CALLBACK (handle_disconnect),
NULL);
if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (self),
self->priv->connection,
self->priv->path,
&error)) {
mm_warn ("couldn't export BEARER at '%s': '%s'",
self->priv->path,
error->message);
g_error_free (error);
}
}
static void
mm_bearer_dbus_unexport (MMBearer *self)
{
const gchar *path;
path = g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (self));
/* Only unexport if currently exported */
if (path) {
mm_dbg ("Removing from DBus bearer at '%s'", path);
g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (self));
}
}
/*****************************************************************************/
MMBearerStatus
mm_bearer_get_status (MMBearer *self)
{
return self->priv->status;
}
const gchar *
mm_bearer_get_path (MMBearer *self)
{
return self->priv->path;
}
MMBearerProperties *
mm_bearer_peek_config (MMBearer *self)
{
return self->priv->config;
}
MMBearerProperties *
mm_bearer_get_config (MMBearer *self)
{
return (self->priv->config ?
g_object_ref (self->priv->config) :
NULL);
}
/*****************************************************************************/
static void
disconnect_force_ready (MMBearer *self,
GAsyncResult *res)
{
GError *error = NULL;
if (!MM_BEARER_GET_CLASS (self)->disconnect_finish (self, res, &error)) {
mm_warn ("Error disconnecting bearer '%s': '%s'. "
"Will assume disconnected anyway.",
self->priv->path,
error->message);
g_error_free (error);
}
else
mm_dbg ("Disconnected bearer '%s'", self->priv->path);
bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTED);
}
void
mm_bearer_disconnect_force (MMBearer *self)
{
if (self->priv->status == MM_BEARER_STATUS_DISCONNECTING ||
self->priv->status == MM_BEARER_STATUS_DISCONNECTED)
return;
mm_dbg ("Forcing disconnection of bearer '%s'", self->priv->path);
/* If currently connecting, try to cancel that operation. */
if (self->priv->status == MM_BEARER_STATUS_CONNECTING) {
g_cancellable_cancel (self->priv->connect_cancellable);
return;
}
/* Disconnecting! */
bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTING);
MM_BEARER_GET_CLASS (self)->disconnect (
self,
(GAsyncReadyCallback)disconnect_force_ready,
NULL);
}
/*****************************************************************************/
static void
report_disconnection (MMBearer *self)
{
/* In the generic bearer implementation we just need to reset the
* interface status */
bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTED);
}
void
mm_bearer_report_disconnection (MMBearer *self)
{
return MM_BEARER_GET_CLASS (self)->report_disconnection (self);
}
static void
set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
MMBearer *self = MM_BEARER (object);
switch (prop_id) {
case PROP_PATH:
g_free (self->priv->path);
self->priv->path = g_value_dup_string (value);
/* Export when we get a DBus connection AND we have a path */
if (self->priv->path &&
self->priv->connection)
mm_bearer_dbus_export (self);
break;
case PROP_CONNECTION:
g_clear_object (&self->priv->connection);
self->priv->connection = g_value_dup_object (value);
/* Export when we get a DBus connection AND we have a path */
if (!self->priv->connection)
mm_bearer_dbus_unexport (self);
else if (self->priv->path)
mm_bearer_dbus_export (self);
break;
case PROP_MODEM:
g_clear_object (&self->priv->modem);
self->priv->modem = g_value_dup_object (value);
if (self->priv->modem)
/* Bind the modem's connection (which is set when it is exported,
* and unset when unexported) to the BEARER's connection */
g_object_bind_property (self->priv->modem, MM_BASE_MODEM_CONNECTION,
self, MM_BEARER_CONNECTION,
G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
break;
case PROP_STATUS:
/* We don't allow g_object_set()-ing the status property */
g_assert_not_reached ();
break;
case PROP_CONFIG: {
GVariant *dictionary;
g_clear_object (&self->priv->config);
self->priv->config = g_value_dup_object (value);
/* Also expose the properties */
dictionary = mm_bearer_properties_get_dictionary (self->priv->config);
mm_gdbus_bearer_set_properties (MM_GDBUS_BEARER (self), dictionary);
if (dictionary)
g_variant_unref (dictionary);
break;
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
MMBearer *self = MM_BEARER (object);
switch (prop_id) {
case PROP_PATH:
g_value_set_string (value, self->priv->path);
break;
case PROP_CONNECTION:
g_value_set_object (value, self->priv->connection);
break;
case PROP_MODEM:
g_value_set_object (value, self->priv->modem);
break;
case PROP_STATUS:
g_value_set_enum (value, self->priv->status);
break;
case PROP_CONFIG:
g_value_set_object (value, self->priv->config);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
mm_bearer_init (MMBearer *self)
{
/* Initialize private data */
self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self),
MM_TYPE_BEARER,
MMBearerPrivate);
self->priv->status = MM_BEARER_STATUS_DISCONNECTED;
/* Set defaults */
mm_gdbus_bearer_set_interface (MM_GDBUS_BEARER (self), NULL);
mm_gdbus_bearer_set_connected (MM_GDBUS_BEARER (self), FALSE);
mm_gdbus_bearer_set_suspended (MM_GDBUS_BEARER (self), FALSE);
mm_gdbus_bearer_set_properties (MM_GDBUS_BEARER (self), NULL);
mm_gdbus_bearer_set_ip_timeout (MM_GDBUS_BEARER (self), MM_BEARER_IP_TIMEOUT_DEFAULT);
mm_gdbus_bearer_set_ip4_config (MM_GDBUS_BEARER (self),
mm_bearer_ip_config_get_dictionary (NULL));
mm_gdbus_bearer_set_ip6_config (MM_GDBUS_BEARER (self),
mm_bearer_ip_config_get_dictionary (NULL));
}
static void
finalize (GObject *object)
{
MMBearer *self = MM_BEARER (object);
g_free (self->priv->path);
G_OBJECT_CLASS (mm_bearer_parent_class)->finalize (object);
}
static void
dispose (GObject *object)
{
MMBearer *self = MM_BEARER (object);
if (self->priv->connection) {
mm_bearer_dbus_unexport (self);
g_clear_object (&self->priv->connection);
}
g_clear_object (&self->priv->modem);
g_clear_object (&self->priv->config);
G_OBJECT_CLASS (mm_bearer_parent_class)->dispose (object);
}
static void
mm_bearer_class_init (MMBearerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (object_class, sizeof (MMBearerPrivate));
/* Virtual methods */
object_class->get_property = get_property;
object_class->set_property = set_property;
object_class->finalize = finalize;
object_class->dispose = dispose;
klass->report_disconnection = report_disconnection;
properties[PROP_CONNECTION] =
g_param_spec_object (MM_BEARER_CONNECTION,
"Connection",
"GDBus connection to the system bus.",
G_TYPE_DBUS_CONNECTION,
G_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_CONNECTION, properties[PROP_CONNECTION]);
properties[PROP_PATH] =
g_param_spec_string (MM_BEARER_PATH,
"Path",
"DBus path of the Bearer",
NULL,
G_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_PATH, properties[PROP_PATH]);
properties[PROP_MODEM] =
g_param_spec_object (MM_BEARER_MODEM,
"Modem",
"The Modem which owns this Bearer",
MM_TYPE_BASE_MODEM,
G_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_MODEM, properties[PROP_MODEM]);
properties[PROP_STATUS] =
g_param_spec_enum (MM_BEARER_STATUS,
"Bearer status",
"Status of the bearer",
MM_TYPE_BEARER_STATUS,
MM_BEARER_STATUS_DISCONNECTED,
G_PARAM_READABLE);
g_object_class_install_property (object_class, PROP_STATUS, properties[PROP_STATUS]);
properties[PROP_CONFIG] =
g_param_spec_object (MM_BEARER_CONFIG,
"Bearer configuration",
"List of user provided properties",
MM_TYPE_BEARER_PROPERTIES,
G_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_CONFIG, properties[PROP_CONFIG]);
}