Currently a client application is informed of the disconnection by the update of the Connected property to false. This sends a Disconnected signal with the disconnection reason before the property is updated. This helps client application to know the reason for the disconnection and to take appropriate action. --- v1->v2: Propagate numerical reason instead of text one v2->v3: Replace numerical value by name and message to be more consistent with Device.Connect error reply. src/adapter.c | 13 +++++++----- src/device.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++-- src/device.h | 3 ++- 3 files changed, 65 insertions(+), 8 deletions(-) diff --git a/src/adapter.c b/src/adapter.c index fd425e6d2..a10721489 100644 --- a/src/adapter.c +++ b/src/adapter.c @@ -7549,7 +7549,8 @@ struct agent *adapter_get_agent(struct btd_adapter *adapter) static void adapter_remove_connection(struct btd_adapter *adapter, struct btd_device *device, - uint8_t bdaddr_type) + uint8_t bdaddr_type, + uint8_t reason) { bool remove_device = false; @@ -7560,7 +7561,7 @@ static void adapter_remove_connection(struct btd_adapter *adapter, return; } - device_remove_connection(device, bdaddr_type, &remove_device); + device_remove_connection(device, bdaddr_type, &remove_device, reason); device_cancel_authentication(device, TRUE); @@ -7601,9 +7602,11 @@ static void adapter_stop(struct btd_adapter *adapter) struct btd_device *device = adapter->connections->data; uint8_t addr_type = btd_device_get_bdaddr_type(device); - adapter_remove_connection(adapter, device, BDADDR_BREDR); + adapter_remove_connection(adapter, device, BDADDR_BREDR, + MGMT_DEV_DISCONN_UNKNOWN); if (addr_type != BDADDR_BREDR) - adapter_remove_connection(adapter, device, addr_type); + adapter_remove_connection(adapter, device, addr_type, + MGMT_DEV_DISCONN_UNKNOWN); } g_dbus_emit_property_changed(dbus_conn, adapter->path, @@ -8551,7 +8554,7 @@ static void dev_disconnected(struct btd_adapter *adapter, device = btd_adapter_find_device(adapter, &addr->bdaddr, addr->type); if (device) { - adapter_remove_connection(adapter, device, addr->type); + adapter_remove_connection(adapter, device, addr->type, reason); disconnect_notify(device, reason); } diff --git a/src/device.c b/src/device.c index 56583f71a..9591fe0a7 100644 --- a/src/device.c +++ b/src/device.c @@ -3481,6 +3481,12 @@ static const GDBusMethodTable device_methods[] = { { } }; +static const GDBusSignalTable device_signals[] = { + { GDBUS_SIGNAL("Disconnected", + GDBUS_ARGS({ "name", "s" }, { "message", "s" })) }, + { } +}; + static gboolean dev_property_get_prefer_bearer(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) @@ -3732,8 +3738,53 @@ static void set_temporary_timer(struct btd_device *dev, unsigned int timeout) dev, NULL); } +static void emit_disconnect_reason(struct btd_device *device, + uint8_t reason) +{ + const char *name; + const char *message; + + switch (reason) { + case MGMT_DEV_DISCONN_UNKNOWN: + name = "org.bluez.Reason.Unknown"; + message = "disconnection-unknown"; + break; + case MGMT_DEV_DISCONN_TIMEOUT: + name = "org.bluez.Reason.Timeout"; + message = "disconnection-timeout"; + break; + case MGMT_DEV_DISCONN_LOCAL_HOST: + name = "org.bluez.Reason.Local"; + message = "disconnection-localhost"; + break; + case MGMT_DEV_DISCONN_REMOTE: + name = "org.bluez.Reason.Remote"; + message = "disconnection-remote"; + break; + case MGMT_DEV_DISCONN_AUTH_FAILURE: + name = "org.bluez.Reason.Authentication"; + message = "disconnection-authentication-failure"; + break; + case MGMT_DEV_DISCONN_LOCAL_HOST_SUSPEND: + name = "org.bluez.Reason.LocalSuspend"; + message = "disconnection-local-suspend"; + break; + default: + warn("Unknown disconnection value: %u", reason); + name = "org.bluez.Reason.Unknown"; + message = "disconnection-undefined"; + } + + g_dbus_emit_signal(dbus_conn, device->path, DEVICE_INTERFACE, + "Disconnected", + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &message, + DBUS_TYPE_INVALID); +} + void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type, - bool *remove) + bool *remove, + uint8_t reason) { struct bearer_state *state = get_state(device, bdaddr_type); DBusMessage *reply; @@ -3803,6 +3854,8 @@ void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type, g_slist_free_full(device->eir_uuids, g_free); device->eir_uuids = NULL; + emit_disconnect_reason(device, reason); + g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "Connected"); @@ -4704,7 +4757,7 @@ static struct btd_device *device_new(struct btd_adapter *adapter, if (g_dbus_register_interface(dbus_conn, device->path, DEVICE_INTERFACE, - device_methods, NULL, + device_methods, device_signals, device_properties, device, device_free) == FALSE) { error("Unable to register device interface for %s", address); diff --git a/src/device.h b/src/device.h index a35bb1386..4eebcebe9 100644 --- a/src/device.h +++ b/src/device.h @@ -134,7 +134,8 @@ gboolean device_is_authenticating(struct btd_device *device); void device_add_connection(struct btd_device *dev, uint8_t bdaddr_type, uint32_t flags); void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type, - bool *remove); + bool *remove, + uint8_t reason); void device_request_disconnect(struct btd_device *device, DBusMessage *msg); bool device_is_disconnecting(struct btd_device *device); void device_set_ltk(struct btd_device *device, const uint8_t val[16], -- 2.43.0