mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-03-10 05:30:39 +01:00
It should be possible to add a profile with autoconnect blocked form the start. Update2() has a %NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT flag to block autoconnect, and so we need something similar when adding a connection. As the existing AddConnection() and AddConnectionUnsaved() API is not extensible, add AddConnection2() that has flags and room for additional arguments. Then add and implement the new flag %NM_SETTINGS_ADD_CONNECTION2_FLAG_BLOCK_AUTOCONNECT for AddConnection2(). Note that libnm's nm_client_add_connection2() API can completely replace the existing nm_client_add_connection_async() call. In particular, it will automatically prefer to call the D-Bus methods AddConnection() and AddConnectionUnsaved(), in order to work with server versions older than 1.20. The purpose of this is that when upgrading the package, the running NetworkManager might still be older than the installed libnm. Anyway, so since nm_client_add_connection2_finish() also has a result output, the caller needs to decide whether he cares about that result. Hence it has an argument ignore_out_result, which allows to fallback to the old API. One might argue that a caller who doesn't care about the output results while still wanting to be backward compatible, should itself choose to call nm_client_add_connection_async() or nm_client_add_connection2(). But instead, it's more convenient if the new function can fully replace the old one, so that the caller does not need to switch which start/finish method to call. https://bugzilla.redhat.com/show_bug.cgi?id=1677068
1320 lines
40 KiB
C
1320 lines
40 KiB
C
/* NetworkManager -- Network link manager
|
|
*
|
|
* 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Copyright (C) 2009 - 2012 Red Hat, Inc.
|
|
* Copyright (C) 2013 Intel Corporation.
|
|
*/
|
|
|
|
#include "nm-default.h"
|
|
|
|
#include "nm-bluez-device.h"
|
|
|
|
#include "nm-core-internal.h"
|
|
#include "nm-bt-error.h"
|
|
#include "nm-bluez-common.h"
|
|
#include "settings/nm-settings.h"
|
|
#include "settings/nm-settings-connection.h"
|
|
#include "NetworkManagerUtils.h"
|
|
|
|
#if WITH_BLUEZ5_DUN
|
|
#include "nm-bluez5-dun.h"
|
|
#endif
|
|
|
|
/*****************************************************************************/
|
|
|
|
#define VARIANT_IS_OF_TYPE_BOOLEAN(v) ((v) != NULL && ( g_variant_is_of_type ((v), G_VARIANT_TYPE_BOOLEAN) ))
|
|
#define VARIANT_IS_OF_TYPE_STRING(v) ((v) != NULL && ( g_variant_is_of_type ((v), G_VARIANT_TYPE_STRING) ))
|
|
#define VARIANT_IS_OF_TYPE_OBJECT_PATH(v) ((v) != NULL && ( g_variant_is_of_type ((v), G_VARIANT_TYPE_OBJECT_PATH) ))
|
|
#define VARIANT_IS_OF_TYPE_STRING_ARRAY(v) ((v) != NULL && ( g_variant_is_of_type ((v), G_VARIANT_TYPE_STRING_ARRAY) ))
|
|
|
|
/*****************************************************************************/
|
|
|
|
NM_GOBJECT_PROPERTIES_DEFINE (NMBluezDevice,
|
|
PROP_PATH,
|
|
PROP_ADDRESS,
|
|
PROP_NAME,
|
|
PROP_CAPABILITIES,
|
|
PROP_USABLE,
|
|
PROP_CONNECTED,
|
|
);
|
|
|
|
enum {
|
|
INITIALIZED,
|
|
REMOVED,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
static guint signals[LAST_SIGNAL] = { 0 };
|
|
|
|
typedef struct {
|
|
char *path;
|
|
GDBusConnection *dbus_connection;
|
|
|
|
GDBusProxy *proxy;
|
|
|
|
GDBusProxy *adapter5;
|
|
gboolean adapter_powered;
|
|
|
|
int bluez_version;
|
|
|
|
gboolean initialized;
|
|
gboolean usable;
|
|
NMBluetoothCapabilities connection_bt_type;
|
|
|
|
guint check_emit_usable_id;
|
|
|
|
char *adapter_address;
|
|
char *address;
|
|
char *name;
|
|
guint32 capabilities;
|
|
gboolean connected;
|
|
gboolean paired;
|
|
|
|
char *b4_iface;
|
|
#if WITH_BLUEZ5_DUN
|
|
NMBluez5DunContext *b5_dun_context;
|
|
#endif
|
|
|
|
NMSettings *settings;
|
|
GSList *connections;
|
|
|
|
NMSettingsConnection *pan_connection;
|
|
gboolean pan_connection_no_autocreate;
|
|
} NMBluezDevicePrivate;
|
|
|
|
struct _NMBluezDevice {
|
|
GObject parent;
|
|
NMBluezDevicePrivate _priv;
|
|
};
|
|
|
|
struct _NMBluezDeviceClass {
|
|
GObjectClass parent;
|
|
};
|
|
|
|
G_DEFINE_TYPE (NMBluezDevice, nm_bluez_device, G_TYPE_OBJECT)
|
|
|
|
#define NM_BLUEZ_DEVICE_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMBluezDevice, NM_IS_BLUEZ_DEVICE)
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void cp_connection_added (NMSettings *settings,
|
|
NMSettingsConnection *sett_conn,
|
|
NMBluezDevice *self);
|
|
static gboolean connection_compatible (NMBluezDevice *self, NMSettingsConnection *sett_conn);
|
|
|
|
/*****************************************************************************/
|
|
|
|
const char *
|
|
nm_bluez_device_get_path (NMBluezDevice *self)
|
|
{
|
|
g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), NULL);
|
|
|
|
return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->path;
|
|
}
|
|
|
|
const char *
|
|
nm_bluez_device_get_address (NMBluezDevice *self)
|
|
{
|
|
g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), NULL);
|
|
|
|
return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->address;
|
|
}
|
|
|
|
gboolean
|
|
nm_bluez_device_get_initialized (NMBluezDevice *self)
|
|
{
|
|
g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), FALSE);
|
|
|
|
return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->initialized;
|
|
}
|
|
|
|
gboolean
|
|
nm_bluez_device_get_usable (NMBluezDevice *self)
|
|
{
|
|
g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), FALSE);
|
|
|
|
return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->usable;
|
|
}
|
|
|
|
const char *
|
|
nm_bluez_device_get_name (NMBluezDevice *self)
|
|
{
|
|
g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), NULL);
|
|
|
|
return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->name;
|
|
}
|
|
|
|
guint32
|
|
nm_bluez_device_get_capabilities (NMBluezDevice *self)
|
|
{
|
|
g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), 0);
|
|
|
|
return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->capabilities;
|
|
}
|
|
|
|
gboolean
|
|
nm_bluez_device_get_connected (NMBluezDevice *self)
|
|
{
|
|
NMBluezDevicePrivate *priv;
|
|
|
|
g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), FALSE);
|
|
|
|
priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
|
|
return priv->connected;
|
|
}
|
|
|
|
static void
|
|
pan_connection_check_create (NMBluezDevice *self)
|
|
{
|
|
gs_unref_object NMConnection *connection = NULL;
|
|
NMSettingsConnection *added;
|
|
NMSetting *setting;
|
|
gs_free char *id = NULL;
|
|
char uuid[37];
|
|
GError *error = NULL;
|
|
NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
|
|
|
|
g_return_if_fail (priv->capabilities & NM_BT_CAPABILITY_NAP);
|
|
g_return_if_fail (priv->connections == NULL);
|
|
g_return_if_fail (priv->name);
|
|
|
|
if (priv->pan_connection || priv->pan_connection_no_autocreate) {
|
|
/* already have a connection or we don't want to create one, nothing to do. */
|
|
return;
|
|
}
|
|
|
|
/* Only try once to create a connection. If it does not succeed, we do not try again. Also,
|
|
* if the connection gets deleted later, do not create another one for this device. */
|
|
priv->pan_connection_no_autocreate = TRUE;
|
|
|
|
/* create a new connection */
|
|
|
|
connection = nm_simple_connection_new ();
|
|
|
|
/* Setting: Connection */
|
|
nm_utils_uuid_generate_buf (uuid);
|
|
id = g_strdup_printf (_("%s Network"), priv->name);
|
|
setting = nm_setting_connection_new ();
|
|
g_object_set (setting,
|
|
NM_SETTING_CONNECTION_ID, id,
|
|
NM_SETTING_CONNECTION_UUID, uuid,
|
|
NM_SETTING_CONNECTION_AUTOCONNECT, FALSE,
|
|
NM_SETTING_CONNECTION_TYPE, NM_SETTING_BLUETOOTH_SETTING_NAME,
|
|
NULL);
|
|
nm_connection_add_setting (connection, setting);
|
|
|
|
/* Setting: Bluetooth */
|
|
setting = nm_setting_bluetooth_new ();
|
|
g_object_set (G_OBJECT (setting),
|
|
NM_SETTING_BLUETOOTH_BDADDR, priv->address,
|
|
NM_SETTING_BLUETOOTH_TYPE, NM_SETTING_BLUETOOTH_TYPE_PANU,
|
|
NULL);
|
|
nm_connection_add_setting (connection, setting);
|
|
|
|
if (!nm_connection_normalize (connection, NULL, NULL, &error)) {
|
|
nm_log_err (LOGD_BT, "bluez[%s] couldn't generate a connection for NAP device: %s",
|
|
priv->path, error->message);
|
|
g_error_free (error);
|
|
g_return_if_reached ();
|
|
}
|
|
|
|
/* Adding a new connection raises a signal which eventually calls check_emit_usable (again)
|
|
* which then already finds the suitable connection in priv->connections. This is confusing,
|
|
* so block the signal. check_emit_usable will succeed after this function call returns. */
|
|
g_signal_handlers_block_by_func (priv->settings, cp_connection_added, self);
|
|
nm_settings_add_connection (priv->settings,
|
|
connection,
|
|
NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
|
|
NM_SETTINGS_CONNECTION_ADD_REASON_NONE,
|
|
NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED,
|
|
&added,
|
|
&error);
|
|
g_signal_handlers_unblock_by_func (priv->settings, cp_connection_added, self);
|
|
|
|
if (added) {
|
|
nm_assert (!g_slist_find (priv->connections, added));
|
|
nm_assert (connection_compatible (self, added));
|
|
priv->connections = g_slist_prepend (priv->connections, g_object_ref (added));
|
|
priv->pan_connection = added;
|
|
nm_log_dbg (LOGD_BT, "bluez[%s] added new Bluetooth connection for NAP device: '%s' (%s)", priv->path, id, uuid);
|
|
} else {
|
|
nm_log_warn (LOGD_BT, "bluez[%s] couldn't add new Bluetooth connection for NAP device: '%s' (%s): %s",
|
|
priv->path, id, uuid, error->message);
|
|
g_clear_error (&error);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
check_emit_usable (NMBluezDevice *self)
|
|
{
|
|
NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
|
|
gboolean new_usable;
|
|
|
|
/* only expect the supported capabilities set. */
|
|
nm_assert ((priv->capabilities & ~(NM_BT_CAPABILITY_NAP | NM_BT_CAPABILITY_DUN)) == NM_BT_CAPABILITY_NONE );
|
|
|
|
new_usable = ( priv->initialized && priv->capabilities
|
|
&& priv->name && priv->paired
|
|
&& ( (priv->bluez_version == 4)
|
|
|| (priv->bluez_version == 5 && priv->adapter5 && priv->adapter_powered))
|
|
&& priv->dbus_connection && priv->address && priv->adapter_address);
|
|
|
|
if (!new_usable)
|
|
goto END;
|
|
|
|
if (priv->connections)
|
|
goto END;
|
|
|
|
if (!(priv->capabilities & NM_BT_CAPABILITY_NAP)) {
|
|
/* non NAP devices are only usable, if they already have a connection. */
|
|
new_usable = FALSE;
|
|
goto END;
|
|
}
|
|
|
|
pan_connection_check_create (self);
|
|
new_usable = !!priv->pan_connection;
|
|
|
|
END:
|
|
if (new_usable != priv->usable) {
|
|
priv->usable = new_usable;
|
|
_notify (self, PROP_USABLE);
|
|
}
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void
|
|
check_emit_usable_schedule (NMBluezDevice *self)
|
|
{
|
|
NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
|
|
|
|
if (priv->check_emit_usable_id == 0)
|
|
priv->check_emit_usable_id = g_idle_add ((GSourceFunc) check_emit_usable, self);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static gboolean
|
|
connection_compatible (NMBluezDevice *self, NMSettingsConnection *sett_conn)
|
|
{
|
|
NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
|
|
NMConnection *connection = nm_settings_connection_get_connection (sett_conn);
|
|
NMSettingBluetooth *s_bt;
|
|
const char *bt_type;
|
|
const char *bdaddr;
|
|
|
|
if (!nm_connection_is_type (connection, NM_SETTING_BLUETOOTH_SETTING_NAME))
|
|
return FALSE;
|
|
|
|
s_bt = nm_connection_get_setting_bluetooth (connection);
|
|
if (!s_bt)
|
|
return FALSE;
|
|
|
|
if (!priv->address)
|
|
return FALSE;
|
|
|
|
bdaddr = nm_setting_bluetooth_get_bdaddr (s_bt);
|
|
if (!bdaddr)
|
|
return FALSE;
|
|
if (!nm_utils_hwaddr_matches (bdaddr, -1, priv->address, -1))
|
|
return FALSE;
|
|
|
|
bt_type = nm_setting_bluetooth_get_connection_type (s_bt);
|
|
|
|
if (nm_streq (bt_type, NM_SETTING_BLUETOOTH_TYPE_NAP))
|
|
return FALSE;
|
|
|
|
if ( g_str_equal (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN)
|
|
&& !(priv->capabilities & NM_BT_CAPABILITY_DUN))
|
|
return FALSE;
|
|
|
|
if ( g_str_equal (bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU)
|
|
&& !(priv->capabilities & NM_BT_CAPABILITY_NAP))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
_internal_track_connection (NMBluezDevice *self,
|
|
NMSettingsConnection *sett_conn,
|
|
gboolean tracked)
|
|
{
|
|
NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
|
|
gboolean was_tracked;
|
|
|
|
was_tracked = !!g_slist_find (priv->connections, sett_conn);
|
|
if (was_tracked == !!tracked)
|
|
return FALSE;
|
|
|
|
if (tracked)
|
|
priv->connections = g_slist_prepend (priv->connections, g_object_ref (sett_conn));
|
|
else {
|
|
priv->connections = g_slist_remove (priv->connections, sett_conn);
|
|
if (priv->pan_connection == sett_conn)
|
|
priv->pan_connection = NULL;
|
|
g_object_unref (sett_conn);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
cp_connection_added (NMSettings *settings,
|
|
NMSettingsConnection *sett_conn,
|
|
NMBluezDevice *self)
|
|
{
|
|
if (connection_compatible (self, sett_conn)) {
|
|
if (_internal_track_connection (self, sett_conn, TRUE))
|
|
check_emit_usable (self);
|
|
}
|
|
}
|
|
|
|
static void
|
|
cp_connection_removed (NMSettings *settings,
|
|
NMSettingsConnection *sett_conn,
|
|
NMBluezDevice *self)
|
|
{
|
|
if (_internal_track_connection (self, sett_conn, FALSE))
|
|
check_emit_usable (self);
|
|
}
|
|
|
|
static void
|
|
cp_connection_updated (NMSettings *settings,
|
|
NMSettingsConnection *sett_conn,
|
|
guint update_reason_u,
|
|
NMBluezDevice *self)
|
|
{
|
|
if (_internal_track_connection (self, sett_conn,
|
|
connection_compatible (self, sett_conn)))
|
|
check_emit_usable_schedule (self);
|
|
}
|
|
|
|
static void
|
|
load_connections (NMBluezDevice *self)
|
|
{
|
|
NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
|
|
NMSettingsConnection *const*connections;
|
|
guint i;
|
|
gboolean changed = FALSE;
|
|
|
|
connections = nm_settings_get_connections (priv->settings, NULL);
|
|
for (i = 0; connections[i]; i++) {
|
|
if (connection_compatible (self, connections[i]))
|
|
changed |= _internal_track_connection (self, connections[i], TRUE);
|
|
}
|
|
if (changed)
|
|
check_emit_usable (self);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
bluez_disconnect_cb (GDBusConnection *dbus_connection,
|
|
GAsyncResult *res,
|
|
gpointer user_data)
|
|
{
|
|
NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE ((NMBluezDevice *) user_data);
|
|
GError *error = NULL;
|
|
GVariant *variant;
|
|
|
|
variant = g_dbus_connection_call_finish (dbus_connection, res, &error);
|
|
if (!variant) {
|
|
if (!strstr (error->message, "org.bluez.Error.NotConnected"))
|
|
nm_log_warn (LOGD_BT, "bluez[%s]: failed to disconnect: %s", priv->path, error->message);
|
|
g_error_free (error);
|
|
} else
|
|
g_variant_unref (variant);
|
|
|
|
g_object_unref (NM_BLUEZ_DEVICE (user_data));
|
|
}
|
|
|
|
void
|
|
nm_bluez_device_disconnect (NMBluezDevice *self)
|
|
{
|
|
NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
|
|
GVariant *args = NULL;
|
|
const char *dbus_iface = NULL;
|
|
|
|
g_return_if_fail (priv->dbus_connection);
|
|
|
|
/* FIXME: if we are in the process of connecting and cancel the
|
|
* connection attempt, we must complete the pending connect request.
|
|
* However, we must also ensure that we don't leave a connected device. */
|
|
if (priv->connection_bt_type == NM_BT_CAPABILITY_DUN) {
|
|
if (priv->bluez_version == 4) {
|
|
/* Can't pass a NULL interface name through dbus to bluez, so just
|
|
* ignore the disconnect if the interface isn't known.
|
|
*/
|
|
if (!priv->b4_iface)
|
|
goto out;
|
|
args = g_variant_new ("(s)", priv->b4_iface),
|
|
dbus_iface = NM_BLUEZ4_SERIAL_INTERFACE;
|
|
} else if (priv->bluez_version == 5) {
|
|
#if WITH_BLUEZ5_DUN
|
|
nm_bluez5_dun_cleanup (priv->b5_dun_context);
|
|
#endif
|
|
priv->connected = FALSE;
|
|
goto out;
|
|
}
|
|
} else if (priv->connection_bt_type == NM_BT_CAPABILITY_NAP) {
|
|
if (priv->bluez_version == 4)
|
|
dbus_iface = NM_BLUEZ4_NETWORK_INTERFACE;
|
|
else if (priv->bluez_version == 5)
|
|
dbus_iface = NM_BLUEZ5_NETWORK_INTERFACE;
|
|
else
|
|
g_assert_not_reached ();
|
|
} else
|
|
g_assert_not_reached ();
|
|
|
|
g_dbus_connection_call (priv->dbus_connection,
|
|
NM_BLUEZ_SERVICE,
|
|
priv->path,
|
|
dbus_iface,
|
|
"Disconnect",
|
|
args ?: g_variant_new("()"),
|
|
NULL,
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
10000,
|
|
NULL,
|
|
(GAsyncReadyCallback) bluez_disconnect_cb,
|
|
g_object_ref (self));
|
|
|
|
out:
|
|
g_clear_pointer (&priv->b4_iface, g_free);
|
|
priv->connection_bt_type = NM_BT_CAPABILITY_NONE;
|
|
}
|
|
|
|
static void
|
|
_connect_complete (NMBluezDevice *self,
|
|
const char *device,
|
|
NMBluezDeviceConnectCallback callback,
|
|
gpointer callback_user_data,
|
|
GError *error)
|
|
{
|
|
NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
|
|
|
|
nm_assert ((device || error) && !(device && error));
|
|
|
|
if ( device
|
|
&& priv->bluez_version == 5) {
|
|
priv->connected = TRUE;
|
|
_notify (self, PROP_CONNECTED);
|
|
}
|
|
|
|
if (callback)
|
|
callback (self, device, error, callback_user_data);
|
|
}
|
|
|
|
static void
|
|
_connect_cb (GObject *source_object,
|
|
GAsyncResult *res,
|
|
gpointer user_data)
|
|
{
|
|
gs_unref_object NMBluezDevice *self = NULL;
|
|
NMBluezDevicePrivate *priv;
|
|
NMBluezDeviceConnectCallback callback;
|
|
gpointer callback_user_data;
|
|
gs_free_error GError *error = NULL;
|
|
char *device = NULL;
|
|
gs_unref_variant GVariant *variant = NULL;
|
|
|
|
nm_utils_user_data_unpack (user_data, &self, &callback, &callback_user_data);
|
|
|
|
priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
|
|
|
|
variant = _nm_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), res, G_VARIANT_TYPE ("(s)"), &error);
|
|
if (variant) {
|
|
g_variant_get (variant, "(s)", &device);
|
|
priv->b4_iface = device;
|
|
}
|
|
|
|
_connect_complete (self, device, callback, callback_user_data, error);
|
|
}
|
|
|
|
#if WITH_BLUEZ5_DUN
|
|
static void
|
|
_connect_cb_bluez5_dun (NMBluez5DunContext *context,
|
|
const char *device,
|
|
GError *error,
|
|
gpointer user_data)
|
|
{
|
|
gs_unref_object NMBluezDevice *self = NULL;
|
|
gs_unref_object GCancellable *cancellable = NULL;
|
|
NMBluezDeviceConnectCallback callback;
|
|
gpointer callback_user_data;
|
|
gs_free_error GError *cancelled_error = NULL;
|
|
|
|
nm_utils_user_data_unpack (user_data, &self, &cancellable, &callback, &callback_user_data);
|
|
|
|
/* FIXME(shutdown): the async operation nm_bluez5_dun_connect() should be cancellable.
|
|
* Fake it here. */
|
|
if (g_cancellable_set_error_if_cancelled (cancellable, &cancelled_error))
|
|
error = cancelled_error;
|
|
|
|
_connect_complete (self, device, callback, callback_user_data, error);
|
|
}
|
|
#else /* WITH_BLUEZ5_DUN */
|
|
static void
|
|
_connect_cb_bluez5_dun_idle_no_b5 (gpointer user_data,
|
|
GCancellable *cancellable)
|
|
{
|
|
gs_unref_object NMBluezDevice *self = NULL;
|
|
NMBluezDeviceConnectCallback callback;
|
|
gpointer callback_user_data;
|
|
gs_free_error GError *error = NULL;
|
|
|
|
nm_utils_user_data_unpack (user_data, &self, &callback, &callback_user_data);
|
|
|
|
if (!g_cancellable_set_error_if_cancelled (cancellable, &error)) {
|
|
g_set_error (&error,
|
|
NM_BT_ERROR,
|
|
NM_BT_ERROR_DUN_CONNECT_FAILED,
|
|
"NetworkManager built without support for Bluez 5");
|
|
}
|
|
callback (self, NULL, error, callback_user_data);
|
|
}
|
|
#endif /* WITH_BLUEZ5_DUN */
|
|
|
|
void
|
|
nm_bluez_device_connect_async (NMBluezDevice *self,
|
|
NMBluetoothCapabilities connection_bt_type,
|
|
GCancellable *cancellable,
|
|
NMBluezDeviceConnectCallback callback,
|
|
gpointer callback_user_data)
|
|
{
|
|
NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
|
|
const char *dbus_iface = NULL;
|
|
const char *connect_type = NULL;
|
|
|
|
g_return_if_fail (priv->capabilities & connection_bt_type & (NM_BT_CAPABILITY_DUN | NM_BT_CAPABILITY_NAP));
|
|
|
|
priv->connection_bt_type = connection_bt_type;
|
|
|
|
if (connection_bt_type == NM_BT_CAPABILITY_NAP) {
|
|
connect_type = BLUETOOTH_CONNECT_NAP;
|
|
if (priv->bluez_version == 4)
|
|
dbus_iface = NM_BLUEZ4_NETWORK_INTERFACE;
|
|
else if (priv->bluez_version == 5)
|
|
dbus_iface = NM_BLUEZ5_NETWORK_INTERFACE;
|
|
} else if (connection_bt_type == NM_BT_CAPABILITY_DUN) {
|
|
connect_type = BLUETOOTH_CONNECT_DUN;
|
|
if (priv->bluez_version == 4)
|
|
dbus_iface = NM_BLUEZ4_SERIAL_INTERFACE;
|
|
else if (priv->bluez_version == 5) {
|
|
#if WITH_BLUEZ5_DUN
|
|
if (priv->b5_dun_context == NULL)
|
|
priv->b5_dun_context = nm_bluez5_dun_new (priv->adapter_address, priv->address);
|
|
nm_bluez5_dun_connect (priv->b5_dun_context,
|
|
_connect_cb_bluez5_dun,
|
|
nm_utils_user_data_pack (g_object_ref (self),
|
|
nm_g_object_ref (cancellable),
|
|
callback,
|
|
callback_user_data));
|
|
#else
|
|
if (callback) {
|
|
nm_utils_invoke_on_idle (_connect_cb_bluez5_dun_idle_no_b5,
|
|
nm_utils_user_data_pack (g_object_ref (self),
|
|
callback,
|
|
callback_user_data),
|
|
cancellable);
|
|
}
|
|
#endif
|
|
return;
|
|
}
|
|
} else
|
|
g_return_if_reached ();
|
|
|
|
/* FIXME: we need to remember that a connect is in progress.
|
|
* So, if the request gets cancelled, that we disconnect the
|
|
* connection that was established in the meantime. */
|
|
g_dbus_connection_call (priv->dbus_connection,
|
|
NM_BLUEZ_SERVICE,
|
|
priv->path,
|
|
dbus_iface,
|
|
"Connect",
|
|
g_variant_new ("(s)", connect_type),
|
|
NULL,
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
20000,
|
|
cancellable,
|
|
_connect_cb,
|
|
nm_utils_user_data_pack (g_object_ref (self),
|
|
callback,
|
|
callback_user_data));
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
set_adapter_address (NMBluezDevice *self, const char *address)
|
|
{
|
|
NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
|
|
|
|
g_return_if_fail (address);
|
|
|
|
if (priv->adapter_address)
|
|
g_free (priv->adapter_address);
|
|
priv->adapter_address = g_strdup (address);
|
|
}
|
|
|
|
static guint32
|
|
convert_uuids_to_capabilities (const char **strings)
|
|
{
|
|
const char **iter;
|
|
guint32 capabilities = 0;
|
|
|
|
for (iter = strings; iter && *iter; iter++) {
|
|
char **parts;
|
|
|
|
parts = g_strsplit (*iter, "-", -1);
|
|
if (parts && parts[0]) {
|
|
switch (g_ascii_strtoull (parts[0], NULL, 16)) {
|
|
case 0x1103:
|
|
capabilities |= NM_BT_CAPABILITY_DUN;
|
|
break;
|
|
case 0x1116:
|
|
capabilities |= NM_BT_CAPABILITY_NAP;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
g_strfreev (parts);
|
|
}
|
|
|
|
return capabilities;
|
|
}
|
|
|
|
static void
|
|
_set_property_capabilities (NMBluezDevice *self, const char **uuids)
|
|
{
|
|
guint32 uint_val;
|
|
NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
|
|
|
|
uint_val = convert_uuids_to_capabilities (uuids);
|
|
if (priv->capabilities != uint_val) {
|
|
if (priv->capabilities) {
|
|
/* changing (relevant) capabilities is not supported and ignored -- except setting initially */
|
|
nm_log_warn (LOGD_BT, "bluez[%s] ignore change of capabilities for Bluetooth device from %u to %u",
|
|
priv->path, priv->capabilities, uint_val);
|
|
return;
|
|
}
|
|
nm_log_dbg (LOGD_BT, "bluez[%s] set capabilities for Bluetooth device: %s%s%s", priv->path,
|
|
uint_val & NM_BT_CAPABILITY_NAP ? "NAP" : "",
|
|
((uint_val & NM_BT_CAPABILITY_DUN) && (uint_val &NM_BT_CAPABILITY_NAP)) ? " | " : "",
|
|
uint_val & NM_BT_CAPABILITY_DUN ? "DUN" : "");
|
|
priv->capabilities = uint_val;
|
|
_notify (self, PROP_CAPABILITIES);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* priv->address can only be set one to a certain (non NULL) value. Every later attempt
|
|
* to reset it to another value will be ignored and a warning will be logged.
|
|
**/
|
|
static void
|
|
_set_property_address (NMBluezDevice *self, const char *addr)
|
|
{
|
|
NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
|
|
|
|
if (g_strcmp0 (priv->address, addr) == 0)
|
|
return;
|
|
|
|
if (!addr) {
|
|
nm_log_warn (LOGD_BT, "bluez[%s] cannot reset address from '%s' to NULL", priv->path, priv->address);
|
|
return;
|
|
}
|
|
|
|
if (priv->address != NULL) {
|
|
nm_log_warn (LOGD_BT, "bluez[%s] cannot reset address from '%s' to '%s'", priv->path, priv->address, addr);
|
|
return;
|
|
}
|
|
|
|
if (!nm_utils_hwaddr_valid (addr, ETH_ALEN)) {
|
|
nm_log_warn (LOGD_BT, "bluez[%s] cannot set address to '%s' (invalid value)", priv->path, addr);
|
|
return;
|
|
}
|
|
|
|
priv->address = g_strdup (addr);
|
|
_notify (self, PROP_ADDRESS);
|
|
}
|
|
|
|
static void
|
|
_take_variant_property_address (NMBluezDevice *self, GVariant *v)
|
|
{
|
|
_set_property_address (self, VARIANT_IS_OF_TYPE_STRING (v) ? g_variant_get_string (v, NULL) : NULL);
|
|
if (v)
|
|
g_variant_unref (v);
|
|
}
|
|
|
|
static void
|
|
_take_variant_property_name (NMBluezDevice *self, GVariant *v)
|
|
{
|
|
NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
|
|
const char *str;
|
|
|
|
if (VARIANT_IS_OF_TYPE_STRING (v)) {
|
|
str = g_variant_get_string (v, NULL);
|
|
if (g_strcmp0 (priv->name, str)) {
|
|
g_free (priv->name);
|
|
priv->name = g_strdup (str);
|
|
_notify (self, PROP_NAME);
|
|
}
|
|
}
|
|
if (v)
|
|
g_variant_unref (v);
|
|
}
|
|
|
|
static void
|
|
_take_variant_property_uuids (NMBluezDevice *self, GVariant *v)
|
|
{
|
|
if (VARIANT_IS_OF_TYPE_STRING_ARRAY (v)) {
|
|
const char **uuids = g_variant_get_strv (v, NULL);
|
|
|
|
_set_property_capabilities (self, uuids);
|
|
g_free (uuids);
|
|
}
|
|
if (v)
|
|
g_variant_unref (v);
|
|
}
|
|
|
|
static void
|
|
_take_variant_property_connected (NMBluezDevice *self, GVariant *v)
|
|
{
|
|
NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
|
|
|
|
if (VARIANT_IS_OF_TYPE_BOOLEAN (v)) {
|
|
gboolean connected = g_variant_get_boolean (v);
|
|
|
|
if (priv->connected != connected) {
|
|
priv->connected = connected;
|
|
_notify (self, PROP_CONNECTED);
|
|
}
|
|
}
|
|
if (v)
|
|
g_variant_unref (v);
|
|
}
|
|
|
|
static void
|
|
_take_variant_property_paired (NMBluezDevice *self, GVariant *v)
|
|
{
|
|
NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
|
|
|
|
if (VARIANT_IS_OF_TYPE_BOOLEAN (v))
|
|
priv->paired = g_variant_get_boolean (v);
|
|
|
|
if (v)
|
|
g_variant_unref (v);
|
|
}
|
|
|
|
static void
|
|
adapter5_on_properties_changed (GDBusProxy *proxy,
|
|
GVariant *changed_properties,
|
|
GStrv invalidated_properties,
|
|
gpointer user_data)
|
|
{
|
|
NMBluezDevice *self = NM_BLUEZ_DEVICE (user_data);
|
|
NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
|
|
GVariantIter i;
|
|
const char *property;
|
|
GVariant *v;
|
|
|
|
g_variant_iter_init (&i, changed_properties);
|
|
while (g_variant_iter_next (&i, "{&sv}", &property, &v)) {
|
|
if (!strcmp (property, "Powered") && VARIANT_IS_OF_TYPE_BOOLEAN (v)) {
|
|
gboolean powered = g_variant_get_boolean (v);
|
|
if (priv->adapter_powered != powered)
|
|
priv->adapter_powered = powered;
|
|
}
|
|
g_variant_unref (v);
|
|
}
|
|
|
|
check_emit_usable (self);
|
|
}
|
|
|
|
static void
|
|
adapter5_on_acquired (GObject *object, GAsyncResult *res, NMBluezDevice *self)
|
|
{
|
|
NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
|
|
GError *error = NULL;
|
|
GVariant *v;
|
|
|
|
priv->adapter5 = g_dbus_proxy_new_for_bus_finish (res, &error);
|
|
if (!priv->adapter5) {
|
|
nm_log_warn (LOGD_BT, "bluez[%s] failed to acquire adapter proxy: %s.", priv->path, error->message);
|
|
g_clear_error (&error);
|
|
g_signal_emit (self, signals[INITIALIZED], 0, FALSE);
|
|
} else {
|
|
g_signal_connect (priv->adapter5, "g-properties-changed",
|
|
G_CALLBACK (adapter5_on_properties_changed), self);
|
|
|
|
/* Check adapter's powered state */
|
|
v = g_dbus_proxy_get_cached_property (priv->adapter5, "Powered");
|
|
priv->adapter_powered = VARIANT_IS_OF_TYPE_BOOLEAN (v) ? g_variant_get_boolean (v) : FALSE;
|
|
if (v)
|
|
g_variant_unref (v);
|
|
|
|
v = g_dbus_proxy_get_cached_property (priv->adapter5, "Address");
|
|
if (VARIANT_IS_OF_TYPE_STRING (v))
|
|
set_adapter_address (self, g_variant_get_string (v, NULL));
|
|
|
|
priv->initialized = TRUE;
|
|
g_signal_emit (self, signals[INITIALIZED], 0, TRUE);
|
|
|
|
check_emit_usable (self);
|
|
}
|
|
|
|
g_object_unref (self);
|
|
}
|
|
|
|
static void
|
|
_take_one_variant_property (NMBluezDevice *self, const char *property, GVariant *v)
|
|
{
|
|
if (v) {
|
|
if (!g_strcmp0 (property, "Address"))
|
|
_take_variant_property_address (self, v);
|
|
else if (!g_strcmp0 (property, "Connected"))
|
|
_take_variant_property_connected (self, v);
|
|
else if (!g_strcmp0 (property, "Paired"))
|
|
_take_variant_property_paired (self, v);
|
|
else if (!g_strcmp0 (property, "Name"))
|
|
_take_variant_property_name (self, v);
|
|
else if (!g_strcmp0 (property, "UUIDs"))
|
|
_take_variant_property_uuids (self, v);
|
|
else
|
|
g_variant_unref (v);
|
|
}
|
|
}
|
|
|
|
static void
|
|
_set_properties (NMBluezDevice *self, GVariant *properties)
|
|
{
|
|
GVariantIter i;
|
|
const char *property;
|
|
GVariant *v;
|
|
|
|
g_object_freeze_notify (G_OBJECT (self));
|
|
g_variant_iter_init (&i, properties);
|
|
while (g_variant_iter_next (&i, "{&sv}", &property, &v))
|
|
_take_one_variant_property (self, property, v);
|
|
g_object_thaw_notify (G_OBJECT (self));
|
|
}
|
|
|
|
static void
|
|
properties_changed (GDBusProxy *proxy,
|
|
GVariant *changed_properties,
|
|
GStrv invalidated_properties,
|
|
gpointer user_data)
|
|
{
|
|
NMBluezDevice *self = NM_BLUEZ_DEVICE (user_data);
|
|
|
|
_set_properties (self, changed_properties);
|
|
check_emit_usable (self);
|
|
}
|
|
|
|
static void
|
|
bluez4_property_changed (GDBusProxy *proxy,
|
|
const char *property,
|
|
GVariant *v,
|
|
gpointer user_data)
|
|
{
|
|
NMBluezDevice *self = NM_BLUEZ_DEVICE (user_data);
|
|
|
|
_take_one_variant_property (self, property, v);
|
|
check_emit_usable (self);
|
|
}
|
|
|
|
static void
|
|
get_properties_cb_4 (GObject *source_object, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
NMBluezDevice *self = NM_BLUEZ_DEVICE (user_data);
|
|
NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
|
|
GError *err = NULL;
|
|
GVariant *v_properties, *v_dict;
|
|
|
|
v_properties = _nm_dbus_proxy_call_finish (priv->proxy, res,
|
|
G_VARIANT_TYPE ("(a{sv})"),
|
|
&err);
|
|
if (!v_properties) {
|
|
g_dbus_error_strip_remote_error (err);
|
|
nm_log_warn (LOGD_BT, "bluez[%s] error getting device properties: %s",
|
|
priv->path, err->message);
|
|
g_error_free (err);
|
|
g_signal_emit (self, signals[INITIALIZED], 0, FALSE);
|
|
goto END;
|
|
}
|
|
|
|
v_dict = g_variant_get_child_value (v_properties, 0);
|
|
_set_properties (self, v_dict);
|
|
g_variant_unref (v_dict);
|
|
g_variant_unref (v_properties);
|
|
|
|
/* Check if any connections match this device */
|
|
load_connections (self);
|
|
|
|
priv->initialized = TRUE;
|
|
g_signal_emit (self, signals[INITIALIZED], 0, TRUE);
|
|
|
|
check_emit_usable (self);
|
|
|
|
END:
|
|
g_object_unref (self);
|
|
}
|
|
|
|
static void
|
|
query_properties (NMBluezDevice *self)
|
|
{
|
|
NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
|
|
GVariant *v;
|
|
|
|
switch (priv->bluez_version) {
|
|
case 4:
|
|
g_dbus_proxy_call (priv->proxy, "GetProperties", NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START, 3000,
|
|
NULL, get_properties_cb_4, g_object_ref (self));
|
|
break;
|
|
case 5:
|
|
g_object_freeze_notify (G_OBJECT (self));
|
|
_take_variant_property_address (self, g_dbus_proxy_get_cached_property (priv->proxy, "Address"));
|
|
_take_variant_property_connected (self, g_dbus_proxy_get_cached_property (priv->proxy, "Connected"));
|
|
_take_variant_property_paired (self, g_dbus_proxy_get_cached_property (priv->proxy, "Paired"));
|
|
_take_variant_property_name (self, g_dbus_proxy_get_cached_property (priv->proxy, "Name"));
|
|
_take_variant_property_uuids (self, g_dbus_proxy_get_cached_property (priv->proxy, "UUIDs"));
|
|
g_object_thaw_notify (G_OBJECT (self));
|
|
|
|
v = g_dbus_proxy_get_cached_property (priv->proxy, "Adapter");
|
|
if (VARIANT_IS_OF_TYPE_OBJECT_PATH (v)) {
|
|
g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
|
|
G_DBUS_PROXY_FLAGS_NONE,
|
|
NULL,
|
|
NM_BLUEZ_SERVICE,
|
|
g_variant_get_string (v, NULL),
|
|
NM_BLUEZ5_ADAPTER_INTERFACE,
|
|
NULL,
|
|
(GAsyncReadyCallback) adapter5_on_acquired,
|
|
g_object_ref (self));
|
|
g_variant_unref (v);
|
|
} else {
|
|
/* If the Adapter property is unset at this point, we won't try to acquire the adapter later on
|
|
* and the device stays unusable. This should not happen, but if it does, log a debug message. */
|
|
nm_log_dbg (LOGD_BT, "bluez[%s] device has no adapter property and cannot be used.", priv->path);
|
|
}
|
|
|
|
/* Check if any connections match this device */
|
|
load_connections (self);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
on_proxy_acquired (GObject *object, GAsyncResult *res, NMBluezDevice *self)
|
|
{
|
|
NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
|
|
GError *error = NULL;
|
|
|
|
priv->proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
|
|
|
|
if (!priv->proxy) {
|
|
nm_log_warn (LOGD_BT, "bluez[%s] failed to acquire device proxy: %s.", priv->path, error->message);
|
|
g_clear_error (&error);
|
|
g_signal_emit (self, signals[INITIALIZED], 0, FALSE);
|
|
} else {
|
|
g_signal_connect (priv->proxy, "g-properties-changed",
|
|
G_CALLBACK (properties_changed), self);
|
|
if (priv->bluez_version == 4) {
|
|
/* Watch for custom Bluez4 PropertyChanged signals */
|
|
_nm_dbus_signal_connect (priv->proxy, "PropertyChanged", G_VARIANT_TYPE ("(sv)"),
|
|
G_CALLBACK (bluez4_property_changed), self);
|
|
}
|
|
|
|
query_properties (self);
|
|
}
|
|
g_object_unref (self);
|
|
}
|
|
|
|
static void
|
|
on_bus_acquired (GObject *object, GAsyncResult *res, NMBluezDevice *self)
|
|
{
|
|
NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
|
|
GError *error = NULL;
|
|
|
|
priv->dbus_connection = g_bus_get_finish (res, &error);
|
|
|
|
if (!priv->dbus_connection) {
|
|
nm_log_warn (LOGD_BT, "bluez[%s] failed to acquire bus connection: %s.", priv->path, error->message);
|
|
g_clear_error (&error);
|
|
g_signal_emit (self, signals[INITIALIZED], 0, FALSE);
|
|
} else
|
|
check_emit_usable (self);
|
|
|
|
g_object_unref (self);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
get_property (GObject *object, guint prop_id,
|
|
GValue *value, GParamSpec *pspec)
|
|
{
|
|
NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE ((NMBluezDevice *) object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_PATH:
|
|
g_value_set_string (value, priv->path);
|
|
break;
|
|
case PROP_ADDRESS:
|
|
g_value_set_string (value, priv->address);
|
|
break;
|
|
case PROP_NAME:
|
|
g_value_set_string (value, priv->name);
|
|
break;
|
|
case PROP_CAPABILITIES:
|
|
g_value_set_uint (value, priv->capabilities);
|
|
break;
|
|
case PROP_USABLE:
|
|
g_value_set_boolean (value, priv->usable);
|
|
break;
|
|
case PROP_CONNECTED:
|
|
g_value_set_boolean (value, priv->connected);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
set_property (GObject *object, guint prop_id,
|
|
const GValue *value, GParamSpec *pspec)
|
|
{
|
|
NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE ((NMBluezDevice *) object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_PATH:
|
|
/* construct-only */
|
|
priv->path = g_value_dup_string (value);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
nm_bluez_device_init (NMBluezDevice *self)
|
|
{
|
|
}
|
|
|
|
NMBluezDevice *
|
|
nm_bluez_device_new (const char *path,
|
|
const char *adapter_address,
|
|
NMSettings *settings,
|
|
int bluez_version)
|
|
{
|
|
NMBluezDevice *self;
|
|
NMBluezDevicePrivate *priv;
|
|
const char *interface_name = NULL;
|
|
|
|
g_return_val_if_fail (path != NULL, NULL);
|
|
g_return_val_if_fail (NM_IS_SETTINGS (settings), NULL);
|
|
g_return_val_if_fail (bluez_version == 4 || bluez_version == 5, NULL);
|
|
|
|
self = (NMBluezDevice *) g_object_new (NM_TYPE_BLUEZ_DEVICE,
|
|
NM_BLUEZ_DEVICE_PATH, path,
|
|
NULL);
|
|
if (!self)
|
|
return NULL;
|
|
|
|
nm_log_dbg (LOGD_BT, "bluez[%s] create NMBluezDevice", path);
|
|
|
|
priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
|
|
|
|
priv->bluez_version = bluez_version;
|
|
priv->settings = g_object_ref (settings);
|
|
g_return_val_if_fail (bluez_version == 5 || (bluez_version == 4 && adapter_address), NULL);
|
|
if (adapter_address)
|
|
set_adapter_address (self, adapter_address);
|
|
|
|
g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_ADDED, G_CALLBACK (cp_connection_added), self);
|
|
g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_REMOVED, G_CALLBACK (cp_connection_removed), self);
|
|
g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_UPDATED, G_CALLBACK (cp_connection_updated), self);
|
|
|
|
g_bus_get (G_BUS_TYPE_SYSTEM,
|
|
NULL,
|
|
(GAsyncReadyCallback) on_bus_acquired,
|
|
g_object_ref (self));
|
|
|
|
switch (priv->bluez_version) {
|
|
case 4:
|
|
interface_name = NM_BLUEZ4_DEVICE_INTERFACE;
|
|
break;
|
|
case 5:
|
|
interface_name = NM_BLUEZ5_DEVICE_INTERFACE;
|
|
break;
|
|
}
|
|
|
|
g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
|
|
G_DBUS_PROXY_FLAGS_NONE,
|
|
NULL,
|
|
NM_BLUEZ_SERVICE,
|
|
priv->path,
|
|
interface_name,
|
|
NULL,
|
|
(GAsyncReadyCallback) on_proxy_acquired,
|
|
g_object_ref (self));
|
|
return self;
|
|
}
|
|
|
|
static void
|
|
dispose (GObject *object)
|
|
{
|
|
NMBluezDevice *self = NM_BLUEZ_DEVICE (object);
|
|
NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
|
|
NMSettingsConnection *to_delete = NULL;
|
|
|
|
nm_clear_g_source (&priv->check_emit_usable_id);
|
|
|
|
if (priv->pan_connection) {
|
|
/* Check whether we want to remove the created connection. If so, we take a reference
|
|
* and delete it at the end of dispose(). */
|
|
if (NM_FLAGS_HAS (nm_settings_connection_get_flags (priv->pan_connection),
|
|
NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED))
|
|
to_delete = g_object_ref (priv->pan_connection);
|
|
|
|
priv->pan_connection = NULL;
|
|
}
|
|
|
|
#if WITH_BLUEZ5_DUN
|
|
if (priv->b5_dun_context) {
|
|
nm_bluez5_dun_free (priv->b5_dun_context);
|
|
priv->b5_dun_context = NULL;
|
|
}
|
|
#endif
|
|
|
|
if (priv->settings) {
|
|
g_signal_handlers_disconnect_by_func (priv->settings, cp_connection_added, self);
|
|
g_signal_handlers_disconnect_by_func (priv->settings, cp_connection_removed, self);
|
|
g_signal_handlers_disconnect_by_func (priv->settings, cp_connection_updated, self);
|
|
}
|
|
|
|
g_slist_free_full (priv->connections, g_object_unref);
|
|
priv->connections = NULL;
|
|
|
|
if (priv->adapter5) {
|
|
g_signal_handlers_disconnect_by_func (priv->adapter5, adapter5_on_properties_changed, self);
|
|
g_clear_object (&priv->adapter5);
|
|
}
|
|
|
|
g_clear_object (&priv->dbus_connection);
|
|
|
|
G_OBJECT_CLASS (nm_bluez_device_parent_class)->dispose (object);
|
|
|
|
if (to_delete) {
|
|
nm_log_dbg (LOGD_BT, "bluez[%s] removing Bluetooth connection for NAP device: '%s' (%s)", priv->path,
|
|
nm_settings_connection_get_id (to_delete), nm_settings_connection_get_uuid (to_delete));
|
|
nm_settings_connection_delete (to_delete, FALSE);
|
|
g_object_unref (to_delete);
|
|
}
|
|
|
|
g_clear_object (&priv->settings);
|
|
}
|
|
|
|
static void
|
|
finalize (GObject *object)
|
|
{
|
|
NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE ((NMBluezDevice *) object);
|
|
|
|
nm_log_dbg (LOGD_BT, "bluez[%s]: finalize NMBluezDevice", priv->path);
|
|
|
|
g_free (priv->path);
|
|
g_free (priv->adapter_address);
|
|
g_free (priv->address);
|
|
g_free (priv->name);
|
|
g_free (priv->b4_iface);
|
|
|
|
if (priv->proxy)
|
|
g_signal_handlers_disconnect_by_data (priv->proxy, object);
|
|
g_clear_object (&priv->proxy);
|
|
|
|
G_OBJECT_CLASS (nm_bluez_device_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
nm_bluez_device_class_init (NMBluezDeviceClass *config_class)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (config_class);
|
|
|
|
object_class->get_property = get_property;
|
|
object_class->set_property = set_property;
|
|
object_class->dispose = dispose;
|
|
object_class->finalize = finalize;
|
|
|
|
obj_properties[PROP_PATH] =
|
|
g_param_spec_string (NM_BLUEZ_DEVICE_PATH, "", "",
|
|
NULL,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
|
|
G_PARAM_STATIC_STRINGS);
|
|
|
|
obj_properties[PROP_ADDRESS] =
|
|
g_param_spec_string (NM_BLUEZ_DEVICE_ADDRESS, "", "",
|
|
NULL,
|
|
G_PARAM_READABLE |
|
|
G_PARAM_STATIC_STRINGS);
|
|
|
|
obj_properties[PROP_NAME] =
|
|
g_param_spec_string (NM_BLUEZ_DEVICE_NAME, "", "",
|
|
NULL,
|
|
G_PARAM_READABLE |
|
|
G_PARAM_STATIC_STRINGS);
|
|
|
|
obj_properties[PROP_CAPABILITIES] =
|
|
g_param_spec_uint (NM_BLUEZ_DEVICE_CAPABILITIES, "", "",
|
|
0, G_MAXUINT, 0,
|
|
G_PARAM_READABLE |
|
|
G_PARAM_STATIC_STRINGS);
|
|
|
|
obj_properties[PROP_USABLE] =
|
|
g_param_spec_boolean (NM_BLUEZ_DEVICE_USABLE, "", "",
|
|
FALSE,
|
|
G_PARAM_READABLE |
|
|
G_PARAM_STATIC_STRINGS);
|
|
|
|
obj_properties[PROP_CONNECTED] =
|
|
g_param_spec_boolean (NM_BLUEZ_DEVICE_CONNECTED, "", "",
|
|
FALSE,
|
|
G_PARAM_READABLE |
|
|
G_PARAM_STATIC_STRINGS);
|
|
|
|
g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
|
|
|
|
signals[INITIALIZED] = g_signal_new (NM_BLUEZ_DEVICE_INITIALIZED,
|
|
G_OBJECT_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0,
|
|
NULL, NULL, NULL,
|
|
G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
|
|
|
|
signals[REMOVED] = g_signal_new (NM_BLUEZ_DEVICE_REMOVED,
|
|
G_OBJECT_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0,
|
|
NULL, NULL, NULL,
|
|
G_TYPE_NONE, 0);
|
|
}
|
|
|