blob: 3cce9483d7c0589542c5210ec5b526dea023bf12 [file] [log] [blame]
/* -*- 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) {
/* 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]);
}