/** * 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); g_object_notify (G_OBJECT (self), NM_BLUEZ_DEVICE_ADDRESS); }
gint nm_bluez_device_get_rssi (NMBluezDevice *self) { g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), 0); return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->rssi; }
static gboolean connection_compatible (NMBluezDevice *self, NMConnection *connection) { NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); 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 ( 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 void get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (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_RSSI: g_value_set_int (value, priv->rssi); break; case PROP_USABLE: g_value_set_boolean (value, priv->usable); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); 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 */ g_signal_connect (priv->proxy, "g-signal", G_CALLBACK (bluez4_property_changed), self); } query_properties (self); } g_object_unref (self); }
static void bluez_connect_cb (GDBusConnection *dbus_connection, GAsyncResult *res, gpointer user_data) { GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data); GObject *result_object = g_async_result_get_source_object (G_ASYNC_RESULT (result)); NMBluezDevice *self = NM_BLUEZ_DEVICE (result_object); NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); GError *error = NULL; char *device; GVariant *variant; variant = g_dbus_connection_call_finish (dbus_connection, res, &error); if (!variant) { g_simple_async_result_take_error (result, error); } else { g_variant_get (variant, "(s)", &device); g_simple_async_result_set_op_res_gpointer (result, g_strdup (device), g_free); priv->bt_iface = device; g_variant_unref (variant); } g_simple_async_result_complete (result); g_object_unref (result); g_object_unref (result_object); }
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; }
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; }
static void adapter5_on_acquired (GObject *object, GAsyncResult *res, NMBluezDevice *self) { NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); GError *error; 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); priv->initialized = TRUE; g_signal_emit (self, signals[INITIALIZED], 0, TRUE); check_emit_usable (self); } g_object_unref (self); }
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; }
static void load_connections (NMBluezDevice *self) { NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); const GSList *connections, *iter; connections = nm_connection_provider_get_connections (priv->provider); for (iter = connections; iter; iter = g_slist_next (iter)) cp_connection_added (priv->provider, NM_CONNECTION (iter->data), self); }
static void _internal_add_connection (NMBluezDevice *self, NMConnection *connection) { NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); if (!g_slist_find (priv->connections, connection)) { priv->connections = g_slist_prepend (priv->connections, g_object_ref (connection)); check_emit_usable (self); } }
static void finalize (GObject *object) { NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (object); g_free (priv->path); g_free (priv->address); g_free (priv->name); g_object_unref (priv->proxy); G_OBJECT_CLASS (nm_bluez_device_parent_class)->finalize (object); }
static void query_properties (NMBluezDevice *self) { NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); DBusGProxyCall *call; call = dbus_g_proxy_begin_call (priv->proxy, "GetProperties", get_properties_cb, self, NULL, G_TYPE_INVALID); if (!call) { nm_log_warn (LOGD_BT, "failed to request Bluetooth device properties for %s.", priv->path); } }
static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (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 _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; g_object_notify (G_OBJECT (self), NM_BLUEZ_DEVICE_CONNECTED); } } 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); g_object_notify (G_OBJECT (self), NM_BLUEZ_DEVICE_NAME); } } if (v) g_variant_unref (v); }
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 cp_connection_removed (NMConnectionProvider *provider, NMConnection *connection, NMBluezDevice *self) { NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); if (g_slist_find (priv->connections, connection)) { priv->connections = g_slist_remove (priv->connections, connection); if (priv->pan_connection == connection) { priv->pan_connection = NULL; g_clear_object (&priv->pan_connection_original); } g_object_unref (connection); check_emit_usable (self); } }
static void finalize (GObject *object) { NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (object); nm_log_dbg (LOGD_BT, "bluez[%s]: finalize NMBluezDevice", priv->path); g_free (priv->path); g_free (priv->address); g_free (priv->name); g_free (priv->bt_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); }
void nm_bluez_device_connect_async (NMBluezDevice *self, NMBluetoothCapabilities connection_bt_type, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *simple; NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); const char *dbus_iface; const char *connect_type = BLUETOOTH_CONNECT_NAP; g_return_if_fail (priv->capabilities & connection_bt_type & (NM_BT_CAPABILITY_DUN | NM_BT_CAPABILITY_NAP)); if (priv->bluez_version == 5) { g_return_if_fail (connection_bt_type == NM_BT_CAPABILITY_NAP); dbus_iface = BLUEZ5_NETWORK_INTERFACE; } else if (priv->bluez_version == 4 && connection_bt_type == NM_BT_CAPABILITY_DUN) { dbus_iface = BLUEZ4_SERIAL_INTERFACE; connect_type = BLUETOOTH_CONNECT_DUN; } else { g_return_if_fail (priv->bluez_version == 4 && connection_bt_type == NM_BT_CAPABILITY_NAP); dbus_iface = BLUEZ4_NETWORK_INTERFACE; } simple = g_simple_async_result_new (G_OBJECT (self), callback, user_data, nm_bluez_device_connect_async); g_dbus_connection_call (priv->dbus_connection, BLUEZ_SERVICE, priv->path, dbus_iface, "Connect", g_variant_new ("(s)", connect_type), NULL, G_DBUS_CALL_FLAGS_NONE, 20000, NULL, (GAsyncReadyCallback) bluez_connect_cb, simple); priv->connection_bt_type = connection_bt_type; }
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_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, BLUEZ_SERVICE, g_variant_get_string (v, NULL), 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 bluez_disconnect_cb (GDBusConnection *dbus_connection, GAsyncResult *res, gpointer user_data) { NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (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)); }
static void get_properties_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) { NMBluezDevice *self = NM_BLUEZ_DEVICE (user_data); NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); GHashTable *properties = NULL; GError *err = NULL; GValue *value; const char **uuids; if (!dbus_g_proxy_end_call (proxy, call, &err, DBUS_TYPE_G_MAP_OF_VARIANT, &properties, G_TYPE_INVALID)) { nm_log_warn (LOGD_BT, "bluez error getting device properties: %s", err && err->message ? err->message : "(unknown)"); g_error_free (err); g_signal_emit (self, signals[INITIALIZED], 0, FALSE); return; } value = g_hash_table_lookup (properties, "Address"); priv->address = value ? g_value_dup_string (value) : NULL; value = g_hash_table_lookup (properties, "Name"); priv->name = value ? g_value_dup_string (value) : NULL; value = g_hash_table_lookup (properties, "RSSI"); priv->rssi = value ? g_value_get_int (value) : 0; value = g_hash_table_lookup (properties, "UUIDs"); if (value) { uuids = (const char **) g_value_get_boxed (value); priv->capabilities = convert_uuids_to_capabilities (uuids); } else priv->capabilities = NM_BT_CAPABILITY_NONE; g_hash_table_unref (properties); priv->initialized = TRUE; g_signal_emit (self, signals[INITIALIZED], 0, TRUE); 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; GVariantType *v_type; v_properties = g_dbus_proxy_call_finish (priv->proxy, res, &err); if (!v_properties) { nm_log_warn (LOGD_BT, "bluez[%s] error getting device properties: %s", priv->path, err && err->message ? err->message : "(unknown)"); g_error_free (err); g_signal_emit (self, signals[INITIALIZED], 0, FALSE); goto END; } v_type = g_variant_type_new ("(a{sv})"); if (g_variant_is_of_type (v_properties, v_type)) { v_dict = g_variant_get_child_value (v_properties, 0); _set_properties (self, v_dict); g_variant_unref (v_dict); } else { nm_log_warn (LOGD_BT, "bluez[%s] GetProperties returns unexpected result of type %s", priv->path, g_variant_get_type_string (v_properties)); } g_variant_type_free (v_type); 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 check_emit_usable (NMBluezDevice *self) { NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); if ( priv->initialized && priv->capabilities && priv->name && priv->address) { if (!priv->usable) { priv->usable = TRUE; g_object_notify (G_OBJECT (self), NM_BLUEZ_DEVICE_USABLE); } } else { if (priv->usable) { priv->usable = FALSE; g_object_notify (G_OBJECT (self), NM_BLUEZ_DEVICE_USABLE); } } }
void nm_bluez_device_disconnect (NMBluezDevice *self) { NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); GVariant *args = NULL; const char *dbus_iface; g_return_if_fail (priv->dbus_connection); if (priv->bluez_version == 5) { g_return_if_fail (priv->connection_bt_type == NM_BT_CAPABILITY_NAP); dbus_iface = BLUEZ5_NETWORK_INTERFACE; } else if (priv->bluez_version == 4 && priv->connection_bt_type == NM_BT_CAPABILITY_DUN) { /* Can't pass a NULL interface name through dbus to bluez, so just * ignore the disconnect if the interface isn't known. */ if (!priv->bt_iface) return; args = g_variant_new ("(s)", priv->bt_iface), dbus_iface = BLUEZ4_SERIAL_INTERFACE; } else { g_return_if_fail (priv->bluez_version == 4 && priv->connection_bt_type == NM_BT_CAPABILITY_NAP); dbus_iface = BLUEZ4_NETWORK_INTERFACE; } g_dbus_connection_call (priv->dbus_connection, BLUEZ_SERVICE, priv->path, dbus_iface, "Disconnect", args ? args : g_variant_new ("()"), NULL, G_DBUS_CALL_FLAGS_NONE, 10000, NULL, (GAsyncReadyCallback) bluez_disconnect_cb, g_object_ref (self)); priv->connection_bt_type = NM_BT_CAPABILITY_NONE; }
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, priv->bluez_version); 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; g_object_notify (G_OBJECT (self), NM_BLUEZ_DEVICE_CAPABILITIES); } }
NMBluezDevice * nm_bluez_device_new (const char *path) { NMBluezDevice *self; NMBluezDevicePrivate *priv; NMDBusManager *dbus_mgr; DBusGConnection *connection; self = (NMBluezDevice *) g_object_new (NM_TYPE_BLUEZ_DEVICE, NM_BLUEZ_DEVICE_PATH, path, NULL); if (!self) return NULL; priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); dbus_mgr = nm_dbus_manager_get (); connection = nm_dbus_manager_get_connection (dbus_mgr); priv->proxy = dbus_g_proxy_new_for_name (connection, BLUEZ_SERVICE, priv->path, BLUEZ_DEVICE_INTERFACE); g_object_unref (dbus_mgr); dbus_g_object_register_marshaller (_nm_marshal_VOID__STRING_BOXED, G_TYPE_NONE, G_TYPE_STRING, G_TYPE_VALUE, G_TYPE_INVALID); dbus_g_proxy_add_signal (priv->proxy, "PropertyChanged", G_TYPE_STRING, G_TYPE_VALUE, G_TYPE_INVALID); dbus_g_proxy_connect_signal (priv->proxy, "PropertyChanged", G_CALLBACK (property_changed), self, NULL); query_properties (self); return self; }