* [RFC PATCH 1/2] plugin: add bonding complete and cancel callbacks with optional retry
2012-01-21 1:59 [RFC PATCH 0/2] auto-pairing plugin Scott James Remnant
@ 2012-01-21 1:59 ` Scott James Remnant
2012-01-21 1:59 ` [RFC PATCH 2/2] autopair: add autopair plugin Scott James Remnant
2012-01-23 14:28 ` [RFC PATCH 0/2] auto-pairing plugin Bastien Nocera
2 siblings, 0 replies; 4+ messages in thread
From: Scott James Remnant @ 2012-01-21 1:59 UTC (permalink / raw)
To: linux-bluetooth; +Cc: keybuk, Scott James Remnant
Allow plugins to register a device-level bonding complete callback
which, if it returns TRUE, specifies that a failed bonding attempt
should be retried after a short backoff period.
Since these will likely store state, require them to register a
bonding cancelled callback at the same time so they can clean up.
---
src/adapter.c | 2 +-
src/device.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/device.h | 12 ++++++++
3 files changed, 99 insertions(+), 1 deletions(-)
diff --git a/src/adapter.c b/src/adapter.c
index a75a0c4..d489e50 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -2975,7 +2975,7 @@ void adapter_remove_connection(struct btd_adapter *adapter,
if (device_is_authenticating(device))
device_cancel_authentication(device, TRUE);
- if (device_is_temporary(device)) {
+ if (device_is_temporary(device) && !device_is_retrying(device)) {
const char *path = device_get_path(device);
DBG("Removing temporary device %s", path);
diff --git a/src/device.c b/src/device.c
index 16855b1..50dfe8c 100644
--- a/src/device.c
+++ b/src/device.c
@@ -86,6 +86,7 @@ struct bonding_req {
GIOChannel *io;
guint listener_id;
struct btd_device *device;
+ uint8_t capability;
};
struct authentication_req {
@@ -149,6 +150,10 @@ struct btd_device {
guint auto_id; /* Auto connect source id */
gboolean connected;
+ GSList *bonding_callbacks;
+ GSList *bonding_cancel_callbacks;
+ uint8_t bonding_retry_status;
+ guint bonding_retry_timer;
sdp_list_t *tmp_records;
@@ -232,6 +237,9 @@ static void device_free(gpointer user_data)
g_slist_free_full(device->attios, g_free);
g_slist_free_full(device->attios_offline, g_free);
+ g_slist_free(device->bonding_callbacks);
+ g_slist_free(device->bonding_cancel_callbacks);
+
g_attrib_unref(device->attrib);
if (device->tmp_records)
@@ -247,6 +255,9 @@ static void device_free(gpointer user_data)
if (device->auto_id)
g_source_remove(device->auto_id);
+ if (device->bonding_retry_timer)
+ g_source_remove(device->bonding_retry_timer);
+
DBG("%p", device);
g_free(device->authr);
@@ -2340,6 +2351,65 @@ static void device_auth_req_free(struct btd_device *device)
device->authr = NULL;
}
+void btd_device_register_bonding_cb(struct btd_device *device,
+ btd_device_bonding_cb_t cb,
+ btd_device_bonding_cancel_cb_t ccb) {
+ device->bonding_callbacks = g_slist_prepend(
+ device->bonding_callbacks, cb);
+ device->bonding_cancel_callbacks = g_slist_prepend(
+ device->bonding_cancel_callbacks, ccb);
+}
+
+void btd_device_unregister_bonding_cb(struct btd_device *device,
+ btd_device_bonding_cb_t cb,
+ btd_device_bonding_cancel_cb_t ccb) {
+ device->bonding_callbacks = g_slist_remove(
+ device->bonding_callbacks, cb);
+ device->bonding_cancel_callbacks = g_slist_remove(
+ device->bonding_cancel_callbacks, ccb);
+}
+
+static gboolean device_bonding_retry(gpointer data)
+{
+ struct btd_device *device = data;
+ struct btd_adapter *adapter = device_get_adapter(device);
+ struct bonding_req *bonding = device->bonding;
+ int err;
+
+ DBG("retrying");
+ err = adapter_create_bonding(adapter, &device->bdaddr,
+ bonding->capability);
+ if (err < 0) {
+ DBG("retry failed");
+ device_bonding_complete(device, device->bonding_retry_status);
+ }
+
+ return FALSE;
+}
+
+static gboolean device_bonding_get_retry(struct btd_device *device,
+ uint8_t status)
+{
+ GSList *l;
+ btd_device_bonding_cb_t cb;
+ gboolean retry = FALSE;
+
+ for (l = device->bonding_callbacks; l != NULL; l = g_slist_next(l)) {
+ cb = l->data;
+ retry |= cb(device, status);
+ }
+
+ g_slist_free(device->bonding_callbacks);
+ device->bonding_callbacks = NULL;
+
+ return retry;
+}
+
+gboolean device_is_retrying(struct btd_device *device)
+{
+ return device->bonding_retry_timer != 0;
+}
+
void device_bonding_complete(struct btd_device *device, uint8_t status)
{
struct bonding_req *bonding = device->bonding;
@@ -2347,6 +2417,14 @@ void device_bonding_complete(struct btd_device *device, uint8_t status)
DBG("bonding %p status 0x%02x", bonding, status);
+ if (device_bonding_get_retry(device, status) && status) {
+ DBG("retrying in 3s");
+ device->bonding_retry_status = status;
+ device->bonding_retry_timer = g_timeout_add(3000,
+ device_bonding_retry, device);
+ return;
+ }
+
if (auth && auth->type == AUTH_TYPE_NOTIFY && auth->agent)
agent_cancel(auth->agent);
@@ -2437,6 +2515,8 @@ void device_cancel_bonding(struct btd_device *device, uint8_t status)
struct bonding_req *bonding = device->bonding;
DBusMessage *reply;
char addr[18];
+ GSList *l;
+ btd_device_bonding_cancel_cb_t ccb;
if (!bonding)
return;
@@ -2444,6 +2524,12 @@ void device_cancel_bonding(struct btd_device *device, uint8_t status)
ba2str(&device->bdaddr, addr);
DBG("Canceling bonding request for %s", addr);
+ for (l = device->bonding_cancel_callbacks; l != NULL;
+ l = g_slist_next(l)) {
+ ccb = l->data;
+ ccb(device);
+ }
+
if (device->authr)
device_cancel_authentication(device, FALSE);
diff --git a/src/device.h b/src/device.h
index 13005ae..4f7fd53 100644
--- a/src/device.h
+++ b/src/device.h
@@ -73,6 +73,7 @@ void device_set_temporary(struct btd_device *device, gboolean temporary);
void device_set_bonded(struct btd_device *device, gboolean bonded);
void device_set_auto_connect(struct btd_device *device, gboolean enable);
gboolean device_is_connected(struct btd_device *device);
+gboolean device_is_retrying(struct btd_device *device);
DBusMessage *device_create_bonding(struct btd_device *device,
DBusConnection *conn, DBusMessage *msg,
const char *agent_path, uint8_t capability);
@@ -100,6 +101,17 @@ guint device_add_disconnect_watch(struct btd_device *device,
void device_remove_disconnect_watch(struct btd_device *device, guint id);
void device_set_class(struct btd_device *device, uint32_t value);
+typedef gboolean (*btd_device_bonding_cb_t) (struct btd_device *device,
+ uint8_t status);
+typedef void (*btd_device_bonding_cancel_cb_t) (struct btd_device *device);
+
+void btd_device_register_bonding_cb(struct btd_device *dev,
+ btd_device_bonding_cb_t cb,
+ btd_device_bonding_cancel_cb_t ccb);
+void btd_device_unregister_bonding_cb(struct btd_device *dev,
+ btd_device_bonding_cb_t cb,
+ btd_device_bonding_cancel_cb_t ccb);
+
#define BTD_UUIDS(args...) ((const char *[]) { args, NULL } )
struct btd_device_driver {
--
1.7.7.3
^ permalink raw reply related [flat|nested] 4+ messages in thread* [RFC PATCH 2/2] autopair: add autopair plugin
2012-01-21 1:59 [RFC PATCH 0/2] auto-pairing plugin Scott James Remnant
2012-01-21 1:59 ` [RFC PATCH 1/2] plugin: add bonding complete and cancel callbacks with optional retry Scott James Remnant
@ 2012-01-21 1:59 ` Scott James Remnant
2012-01-23 14:28 ` [RFC PATCH 0/2] auto-pairing plugin Bastien Nocera
2 siblings, 0 replies; 4+ messages in thread
From: Scott James Remnant @ 2012-01-21 1:59 UTC (permalink / raw)
To: linux-bluetooth; +Cc: keybuk, Scott James Remnant
The autopair plugin sends an initial PIN of 0000 to "dumb" devices such
as mice and headsets (list of classes unashamedly stolen from Android)
and registers a bonding complete callback for that device.
If bonding should fail with the fixed PIN, the device is blacklisted
from auto-pairing and the pairing tried again after a short back-off
period, this time using the Agent's RequestPinCode method.
---
Makefile.am | 5 +
acinclude.m4 | 6 ++
plugins/autopair.c | 209 ++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 220 insertions(+), 0 deletions(-)
create mode 100644 plugins/autopair.c
diff --git a/Makefile.am b/Makefile.am
index 8dbe603..cdfdb93 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -278,6 +278,11 @@ builtin_modules += dbusoob
builtin_sources += plugins/dbusoob.c
endif
+if AUTOPAIRPLUGIN
+builtin_modules += autopair
+builtin_sources += plugins/autopair.c
+endif
+
if MAINTAINER_MODE
plugin_LTLIBRARIES += plugins/external-dummy.la
plugins_external_dummy_la_SOURCES = plugins/external-dummy.c
diff --git a/acinclude.m4 b/acinclude.m4
index 57fc5e0..187a049 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -218,6 +218,7 @@ AC_DEFUN([AC_ARG_BLUEZ], [
dbusoob_enable=no
wiimote_enable=no
thermometer_enable=no
+ autopair_enable=no
AC_ARG_ENABLE(optimization, AC_HELP_STRING([--disable-optimization], [disable code optimization]), [
optimization_enable=${enableval}
@@ -374,6 +375,10 @@ AC_DEFUN([AC_ARG_BLUEZ], [
thermometer_enable=${enableval}
])
+ AC_ARG_ENABLE(autopair, AC_HELP_STRING([--enable-autopair], [enable auto-pairplugin]), [
+ autopair_enable=${enableval}
+ ])
+
if (test "${fortify_enable}" = "yes"); then
CFLAGS="$CFLAGS -D_FORTIFY_SOURCE=2"
fi
@@ -432,4 +437,5 @@ AC_DEFUN([AC_ARG_BLUEZ], [
AM_CONDITIONAL(DBUSOOBPLUGIN, test "${dbusoob_enable}" = "yes")
AM_CONDITIONAL(WIIMOTEPLUGIN, test "${wiimote_enable}" = "yes")
AM_CONDITIONAL(THERMOMETERPLUGIN, test "${thermometer_enable}" = "yes")
+ AM_CONDITIONAL(AUTOPAIRPLUGIN, test "${autopair_enable}" = "yes")
])
diff --git a/plugins/autopair.c b/plugins/autopair.c
new file mode 100644
index 0000000..6099b0f
--- /dev/null
+++ b/plugins/autopair.c
@@ -0,0 +1,209 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Google Inc.
+ *
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/stat.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <glib.h>
+
+#include <bluetooth/bluetooth.h>
+
+#include "glib-compat.h"
+#include "plugin.h"
+#include "adapter.h"
+#include "device.h"
+#include "storage.h"
+#include "textfile.h"
+#include "bt_ids.h"
+#include "log.h"
+
+static GSList *attempting = NULL;
+static GSList *dynamic_blacklist = NULL;
+
+static gboolean autopair_bondingcb(struct btd_device *device, uint8_t status)
+{
+ GSList *match;
+ bdaddr_t *ba;
+
+ match = g_slist_find(attempting, device);
+ if (!match)
+ return FALSE;
+
+ attempting = g_slist_remove_link(attempting, match);
+ btd_device_unref(device);
+
+ /* successful pair */
+ if (!status)
+ return FALSE;
+
+ /* failed: blacklist and retry with the user's agent */
+ ba = g_new0(bdaddr_t, 1);
+ device_get_address(device, ba, NULL);
+ dynamic_blacklist = g_slist_prepend(dynamic_blacklist, ba);
+
+ return TRUE;
+}
+
+static void autopair_bonding_cancelcb(struct btd_device *device)
+{
+ GSList *match;
+
+ if ((match = g_slist_find(attempting, device))) {
+ attempting = g_slist_remove_link(attempting, match);
+ btd_device_unref (device);
+ }
+}
+
+static gboolean autopair_attempt(struct btd_device *device)
+{
+ if (g_slist_find(attempting, device))
+ return FALSE;
+
+ attempting = g_slist_prepend(attempting, btd_device_ref(device));
+
+ btd_device_register_bonding_cb(device, autopair_bondingcb,
+ autopair_bonding_cancelcb);
+ return TRUE;
+}
+
+static ssize_t autopair_pincb(struct btd_adapter *adapter,
+ struct btd_device *device, char *pinbuf)
+{
+ bdaddr_t local, peer;
+ uint32_t class;
+
+ /* Only autopair on host-initiated connections */
+ if (!device_is_bonding(device, NULL))
+ return 0;
+
+ device_get_address(device, &peer, NULL);
+ adapter_get_address(adapter, &local);
+
+ if (g_slist_find_custom(dynamic_blacklist, &peer, (GCompareFunc) bacmp))
+ return 0;
+
+ if (read_remote_class(&local, &peer, &class) != 0)
+ return 0;
+
+ switch (BLUETOOTH_DEVICE_CLASS(class)) {
+ case BLUETOOTH_DEVICE_CLASS_AUDIO_VIDEO_WEARABLE_HEADSET:
+ case BLUETOOTH_DEVICE_CLASS_AUDIO_VIDEO_HANDSFREE:
+ case BLUETOOTH_DEVICE_CLASS_AUDIO_VIDEO_HEADPHONES:
+ case BLUETOOTH_DEVICE_CLASS_AUDIO_VIDEO_PORTABLE_AUDIO:
+ case BLUETOOTH_DEVICE_CLASS_AUDIO_VIDEO_HIFI_AUDIO:
+ /* These are the classes Android attempts auto-pairing with */
+ case BLUETOOTH_DEVICE_CLASS_PERIPHERAL_POINTING:
+ /* Attempt auto-pairing with mice too */
+ if (autopair_attempt(device)) {
+ DBG("attempting auto-pairing with device (%d)", class);
+ memcpy(pinbuf, "0000", 4);
+ return 4;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int autopair_probe(struct btd_adapter *adapter)
+{
+ btd_adapter_register_pin_cb(adapter, autopair_pincb);
+
+ return 0;
+}
+
+static void autopair_remove(struct btd_adapter *adapter)
+{
+ btd_adapter_unregister_pin_cb(adapter, autopair_pincb);
+}
+
+static struct btd_adapter_driver autopair_driver = {
+ .name = "autopair",
+ .probe = autopair_probe,
+ .remove = autopair_remove,
+};
+
+static void autopair_add_blacklist(char *key, char *value, void *data)
+{
+ bdaddr_t *ba;
+
+ if (strcmp(value, "blacklist"))
+ return;
+
+ ba = g_new0(bdaddr_t, 1);
+ str2ba(key, ba);
+ dynamic_blacklist = g_slist_prepend(dynamic_blacklist, ba);
+}
+
+static int autopair_init(void)
+{
+ char filename[PATH_MAX + 1];
+
+ /* Load dynamic blacklist */
+ create_name(filename, PATH_MAX, STORAGEDIR, "autopair", "blacklist");
+ textfile_foreach(filename, autopair_add_blacklist, NULL);
+
+ return btd_register_adapter_driver(&autopair_driver);
+}
+
+static void autopair_exit(void)
+{
+ GSList *l;
+ struct btd_device *device;
+ char filename[PATH_MAX + 1];
+ bdaddr_t *ba;
+ char addr[18];
+
+ btd_unregister_adapter_driver(&autopair_driver);
+
+ /* Unregister device-level callbacks and unreference */
+ for (l = attempting; l != NULL; l = g_slist_next(l)) {
+ device = l->data;
+ btd_device_unregister_bonding_cb(device, autopair_bondingcb,
+ autopair_bonding_cancelcb);
+ btd_device_unref(device);
+ }
+ g_slist_free(attempting);
+
+ /* Save dynamic blacklist */
+ create_name(filename, PATH_MAX, STORAGEDIR, "autopair", "blacklist");
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ for (l = dynamic_blacklist; l != NULL; l = g_slist_next(l)) {
+ ba = l->data;
+ ba2str(ba, addr);
+ textfile_put(filename, addr, "blacklist");
+ }
+
+ g_slist_free_full(dynamic_blacklist, g_free);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(autopair, VERSION,
+ BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, autopair_init, autopair_exit)
--
1.7.7.3
^ permalink raw reply related [flat|nested] 4+ messages in thread