summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--hcid/dbus-adapter.c92
-rw-r--r--hcid/dbus-adapter.h12
-rw-r--r--hcid/dbus-api.txt19
-rw-r--r--hcid/dbus-error.c5
-rw-r--r--hcid/dbus-error.h1
-rw-r--r--hcid/dbus-hci.c22
-rwxr-xr-xtest/apitest1
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" ]