diff options
-rw-r--r-- | hcid/dbus-adapter.c | 92 | ||||
-rw-r--r-- | hcid/dbus-adapter.h | 12 | ||||
-rw-r--r-- | hcid/dbus-api.txt | 19 | ||||
-rw-r--r-- | hcid/dbus-error.c | 5 | ||||
-rw-r--r-- | hcid/dbus-error.h | 1 | ||||
-rw-r--r-- | hcid/dbus-hci.c | 22 | ||||
-rwxr-xr-x | test/apitest | 1 |
7 files changed, 129 insertions, 23 deletions
diff --git a/hcid/dbus-adapter.c b/hcid/dbus-adapter.c index a36ab4fa..a4ca8712 100644 --- a/hcid/dbus-adapter.c +++ b/hcid/dbus-adapter.c @@ -1680,18 +1680,61 @@ static DBusHandlerResult adapter_last_used(DBusConnection *conn, return send_message_and_unref(conn, reply); } -static DBusHandlerResult adapter_dc_remote_device(DBusConnection *conn, - DBusMessage *msg, void *data) + +gboolean dc_pending_timeout_handler(void *data) { + int dd; + struct adapter *adapter = data; + struct pending_dc_info *pending_dc = adapter->pending_dc; DBusMessage *reply; + dd = hci_open_dev(adapter->dev_id); + + if (dd < 0) { + error_no_such_adapter(pending_dc->conn, + pending_dc->msg); + dc_pending_timeout_cleanup(adapter); + return FALSE; + } + + /* Send the HCI disconnect command */ + if (hci_disconnect(dd, pending_dc->conn_handle, + HCI_OE_USER_ENDED_CONNECTION, + 500) < 0) { + int err = errno; + error("Disconnect failed"); + error_failed(pending_dc->conn, pending_dc->msg, err); + } else { + reply = dbus_message_new_method_return(pending_dc->msg); + if (!reply) + error("Failed to allocate disconnect reply"); + else + send_message_and_unref(pending_dc->conn, reply); + } + + hci_close_dev(dd); + dc_pending_timeout_cleanup(adapter); + + return FALSE; +} + +void dc_pending_timeout_cleanup(struct adapter *adapter) +{ + dbus_connection_unref(adapter->pending_dc->conn); + dbus_message_unref(adapter->pending_dc->msg); + free(adapter->pending_dc); + adapter->pending_dc = NULL; +} + +static DBusHandlerResult adapter_dc_remote_device(DBusConnection *conn, + DBusMessage *msg, void *data) +{ struct adapter *adapter = data; struct slist *l = adapter->active_conn; const char *peer_addr; bdaddr_t peer_bdaddr; - int dd; - struct active_conn_info *dev; + DBusMessage *signal; if (!adapter->up) return error_not_ready(conn, msg); @@ -1710,29 +1753,38 @@ static DBusHandlerResult adapter_dc_remote_device(DBusConnection *conn, if (!l) return error_not_connected(conn, msg); - dev = l->data; + if(adapter->pending_dc) + return error_disconnect_in_progress(conn, msg); - dd = hci_open_dev(adapter->dev_id); - if (dd < 0) - return error_no_such_adapter(conn, msg); + adapter->pending_dc = malloc(sizeof(*adapter->pending_dc)); + if(!adapter->pending_dc) + return DBUS_HANDLER_RESULT_NEED_MEMORY; - /* Send the HCI disconnect command */ - if (hci_disconnect(dd, dev->handle, HCI_OE_USER_ENDED_CONNECTION, - 500) < 0) { - int err = errno; - error("Disconnect failed"); - hci_close_dev(dd); - return error_failed(conn, msg, err); + /* Start waiting... */ + adapter->pending_dc->timeout_id = + g_timeout_add(DC_PENDING_TIMEOUT, + dc_pending_timeout_handler, + adapter); + + if(!adapter->pending_dc->timeout_id) { + free(adapter->pending_dc); + adapter->pending_dc = NULL; + return DBUS_HANDLER_RESULT_NEED_MEMORY; } - hci_close_dev(dd); + adapter->pending_dc->conn = dbus_connection_ref(conn); + adapter->pending_dc->msg = dbus_message_ref(msg); + adapter->pending_dc->conn_handle = + ((struct active_conn_info *) l->data)->handle; - reply = dbus_message_new_method_return(msg); - if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; + /* ...and send a signal */ + signal = dev_signal_factory(adapter->dev_id, "RemoteDeviceDisconnectRequested", + DBUS_TYPE_STRING, &peer_addr, + DBUS_TYPE_INVALID); + send_message_and_unref(conn, signal); - return send_message_and_unref(conn, reply); + return DBUS_HANDLER_RESULT_HANDLED; } static void reply_authentication_failure(struct bonding_request_info *bonding) diff --git a/hcid/dbus-adapter.h b/hcid/dbus-adapter.h index 3c21c179..288cc338 100644 --- a/hcid/dbus-adapter.h +++ b/hcid/dbus-adapter.h @@ -34,6 +34,8 @@ #define BONDING_TIMEOUT 45000 /* 45 sec */ +#define DC_PENDING_TIMEOUT 2000 /* 2 secs */ + /* Discover types */ #define DISCOVER_TYPE_NONE 0x00 #define STD_INQUIRY 0x01 @@ -77,6 +79,13 @@ struct active_conn_info { uint16_t handle; }; +struct pending_dc_info { + DBusConnection *conn; + DBusMessage *msg; + uint16_t conn_handle; + guint timeout_id; +}; + struct adapter { uint16_t dev_id; int up; @@ -100,6 +109,7 @@ struct adapter { struct slist *active_conn; struct bonding_request_info *bonding; struct slist *pin_reqs; + struct pending_dc_info *pending_dc; }; DBusHandlerResult handle_adapter_method(DBusConnection *conn, DBusMessage *msg, void *data); @@ -112,4 +122,6 @@ struct slist *service_classes_str(uint32_t class); int pending_remote_name_cancel(struct adapter *adapter); +void dc_pending_timeout_cleanup(struct adapter *adapter); + #endif /* __ADAPTER_H */ diff --git a/hcid/dbus-api.txt b/hcid/dbus-api.txt index aa3a34fc..437f2baa 100644 --- a/hcid/dbus-api.txt +++ b/hcid/dbus-api.txt @@ -679,15 +679,22 @@ Methods string GetAddress() void DisconnectRemoteDevice(string address) This method disconnects a specific remote device by - terminating the low-level ACL connection. The use - of this method should be restricted to administrator - use only. + terminating the low-level ACL connection. The use of + this method should be restricted to administrator + use. + + A RemoteDeviceDisconnectRequested signal will be + sent and the actual disconnection will only happen 2 + seconds later. This enables upper-level applications + to terminate their connections gracefully before the + ACL connection is terminated. Possible errors: org.bluez.Error.NotReady org.bluez.Error.Failed org.bluez.Error.NoSuchAdapter org.bluez.Error.InvalidArguments org.bluez.Error.NotConnected + org.bluez.Error.InProgress void CreateBonding(string address) @@ -1001,6 +1008,12 @@ Signals void ModeChanged(string mode) This signal will be send if a low level connection between two devices has been created. + void RemoteDeviceDisconnectRequested(string address) + + This signal will be sent when a low level + disconnection to a remote device has been requested. + The actual disconnection will happen 2 seconds later. + void RemoteDeviceDisconnected(string address) This signal will be send if a low level connection diff --git a/hcid/dbus-error.c b/hcid/dbus-error.c index 8a87cff5..83b72ad7 100644 --- a/hcid/dbus-error.c +++ b/hcid/dbus-error.c @@ -278,6 +278,11 @@ DBusHandlerResult error_trusted_device_does_not_exists(DBusConnection *conn, DBu return error_does_not_exist(conn, msg, "Trusted device does not exist"); } +DBusHandlerResult error_disconnect_in_progress(DBusConnection *conn, DBusMessage *msg) +{ + return error_in_progress(conn, msg, "Disconnection in progress"); +} + static const char *strsdperror(int err) { diff --git a/hcid/dbus-error.h b/hcid/dbus-error.h index c88ce815..801c122d 100644 --- a/hcid/dbus-error.h +++ b/hcid/dbus-error.h @@ -65,5 +65,6 @@ DBusHandlerResult error_sdp_failed(DBusConnection *conn, DBusMessage *msg, int e DBusHandlerResult error_audit_already_exists(DBusConnection *conn, DBusMessage *msg); DBusHandlerResult error_trusted_device_already_exists(DBusConnection *conn, DBusMessage *msg); DBusHandlerResult error_trusted_device_does_not_exists(DBusConnection *conn, DBusMessage *msg); +DBusHandlerResult error_disconnect_in_progress(DBusConnection *conn, DBusMessage *msg); #endif /* __BLUEZ_DBUS_ERROR_H */ diff --git a/hcid/dbus-hci.c b/hcid/dbus-hci.c index 51f3ca65..8fd443b6 100644 --- a/hcid/dbus-hci.c +++ b/hcid/dbus-hci.c @@ -473,6 +473,14 @@ int unregister_adapter_path(const char *path) adapter->active_conn = NULL; } + /* Check if there is a pending RemoteDeviceDisconnect request */ + if (adapter->pending_dc) { + error_no_such_adapter(adapter->pending_dc->conn, + adapter->pending_dc->msg); + g_timeout_remove(adapter->pending_dc->timeout_id); + dc_pending_timeout_cleanup(adapter); + } + free (adapter); unreg: @@ -1742,6 +1750,20 @@ void hcid_dbus_disconn_complete(bdaddr_t *local, uint8_t status, adapter->bonding = NULL; } + /* Check if there is a pending RemoteDeviceDisconnect request */ + if (adapter->pending_dc) { + DBusMessage *reply; + + reply = dbus_message_new_method_return(adapter->pending_dc->msg); + if (!reply) + error("Failed to allocate disconnect reply"); + else + send_message_and_unref(adapter->pending_dc->conn, reply); + + g_timeout_remove(adapter->pending_dc->timeout_id); + dc_pending_timeout_cleanup(adapter); + } + /* Send the remote device disconnected signal */ message = dev_signal_factory(adapter->dev_id, "RemoteDeviceDisconnected", diff --git a/test/apitest b/test/apitest index 1c955136..601cf8df 100755 --- a/test/apitest +++ b/test/apitest @@ -69,6 +69,7 @@ dev_signals = [ "ModeChanged", "RemoteAliasChanged" "RemoteAliasCleared", "RemoteDeviceConnected", + "RemoteDeviceDisconnectRequested", "RemoteDeviceDisconnected", "BondingCreated", "BondingRemoved" ] |