client: Add advertise command
This adds advertise command which uses org.bluez.LEAdvertisingManager1 to
add an advertising instance:
[bluetooth]# advertise
broadcast off on peripheral
[bluetooth]# advertise on
Advertising object registered
@ Advertising Added: 1
< HCI Command: LE Set Advertising Data (0x08|0x0008) plen 32
Length: 3
Flags: 0x02
LE General Discoverable Mode
[bluetooth]# advertise off
Advertising object unregistered
@ Advertising Removed: 1
< HCI Command: LE Set Advertise Enable (0x08|0x000a) plen 1
Advertising: Disabled (0x00)
diff --git a/Makefile.tools b/Makefile.tools
index 0d5f143..7706dc7 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -5,6 +5,8 @@
client_bluetoothctl_SOURCES = client/main.c \
client/display.h client/display.c \
client/agent.h client/agent.c \
+ client/advertising.h \
+ client/advertising.c \
client/gatt.h client/gatt.c \
monitor/uuid.h monitor/uuid.c
client_bluetoothctl_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@ \
diff --git a/client/advertising.c b/client/advertising.c
new file mode 100644
index 0000000..34e3f59
--- /dev/null
+++ b/client/advertising.c
@@ -0,0 +1,179 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2016 Intel Corporation. All rights reserved.
+ *
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <readline/readline.h>
+#include <wordexp.h>
+
+#include "gdbus/gdbus.h"
+#include "display.h"
+#include "advertising.h"
+
+#define AD_PATH "/org/bluez/advertising"
+#define AD_IFACE "org.bluez.LEAdvertisement1"
+
+static gboolean registered = FALSE;
+static char *ad_type = NULL;
+
+static void ad_release(DBusConnection *conn)
+{
+ registered = FALSE;
+
+ g_dbus_unregister_interface(conn, AD_PATH, AD_IFACE);
+}
+
+static DBusMessage *release_advertising(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ rl_printf("Advertising released\n");
+
+ ad_release(conn);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable ad_methods[] = {
+ { GDBUS_METHOD("Release", NULL, NULL, release_advertising) },
+ { }
+};
+
+static void register_setup(DBusMessageIter *iter, void *user_data)
+{
+ DBusMessageIter dict;
+ const char *path = AD_PATH;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+ dbus_message_iter_close_container(iter, &dict);
+}
+
+static void register_reply(DBusMessage *message, void *user_data)
+{
+ DBusConnection *conn = user_data;
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, message) == FALSE) {
+ registered = TRUE;
+ rl_printf("Advertising object registered\n");
+ } else {
+ rl_printf("Failed to register advertisement: %s\n", error.name);
+ dbus_error_free(&error);
+
+ if (g_dbus_unregister_interface(conn, AD_PATH,
+ AD_IFACE) == FALSE)
+ rl_printf("Failed to unregister advertising object\n");
+ }
+}
+
+static gboolean get_type(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ const char *type = "peripheral";
+
+ if (!ad_type || strlen(ad_type) > 0)
+ type = ad_type;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &type);
+
+ return TRUE;
+}
+
+static const GDBusPropertyTable ad_props[] = {
+ { "Type", "s", get_type },
+ { }
+};
+
+void ad_register(DBusConnection *conn, GDBusProxy *manager, const char *type)
+{
+ if (registered == TRUE) {
+ rl_printf("Advertisement is already registered\n");
+ return;
+ }
+
+ ad_type = g_strdup(type);
+
+ if (g_dbus_register_interface(conn, AD_PATH, AD_IFACE, ad_methods,
+ NULL, ad_props, NULL, NULL) == FALSE) {
+ rl_printf("Failed to register advertising object\n");
+ return;
+ }
+
+ if (g_dbus_proxy_method_call(manager, "RegisterAdvertisement",
+ register_setup, register_reply,
+ conn, NULL) == FALSE) {
+ rl_printf("Failed to register advertising\n");
+ return;
+ }
+}
+
+static void unregister_setup(DBusMessageIter *iter, void *user_data)
+{
+ const char *path = AD_PATH;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+}
+
+static void unregister_reply(DBusMessage *message, void *user_data)
+{
+ DBusConnection *conn = user_data;
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, message) == FALSE) {
+ registered = FALSE;
+ rl_printf("Advertising object unregistered\n");
+ if (g_dbus_unregister_interface(conn, AD_PATH,
+ AD_IFACE) == FALSE)
+ rl_printf("Failed to unregister advertising object\n");
+ } else {
+ rl_printf("Failed to unregister advertisement: %s\n",
+ error.name);
+ dbus_error_free(&error);
+ }
+}
+
+void ad_unregister(DBusConnection *conn, GDBusProxy *manager)
+{
+ if (!manager)
+ ad_release(conn);
+
+ if (g_dbus_proxy_method_call(manager, "UnregisterAdvertisement",
+ unregister_setup, unregister_reply,
+ conn, NULL) == FALSE) {
+ rl_printf("Failed to unregister advertisement method\n");
+ return;
+ }
+}
diff --git a/client/main.c b/client/main.c
index 056331f..471879f 100644
--- a/client/main.c
+++ b/client/main.c
@@ -43,6 +43,7 @@
#include "agent.h"
#include "display.h"
#include "gatt.h"
+#include "advertising.h"
/* String display constants */
#define COLORED_NEW COLOR_GREEN "NEW" COLOR_OFF
@@ -58,6 +59,8 @@
static GDBusProxy *agent_manager;
static char *auto_register_agent = NULL;
+static GDBusProxy *ad_manager;
+
static GDBusProxy *default_ctrl;
static GDBusProxy *default_dev;
static GDBusProxy *default_attr;
@@ -77,6 +80,14 @@
NULL
};
+static const char * const ad_arguments[] = {
+ "on",
+ "off",
+ "peripheral",
+ "broadcast",
+ NULL
+};
+
static void proxy_leak(gpointer data)
{
printf("Leaking proxy %p\n", data);
@@ -445,6 +456,8 @@
gatt_add_descriptor(proxy);
} else if (!strcmp(interface, "org.bluez.GattManager1")) {
gatt_add_manager(proxy);
+ } else if (!strcmp(interface, "org.bluez.LEAdvertisingManager1")) {
+ ad_manager = proxy;
}
}
@@ -509,6 +522,11 @@
set_default_attribute(NULL);
} else if (!strcmp(interface, "org.bluez.GattManager1")) {
gatt_remove_manager(proxy);
+ } else if (!strcmp(interface, "org.bluez.LEAdvertisingManager1")) {
+ if (ad_manager == proxy) {
+ agent_manager = NULL;
+ ad_unregister(dbus_conn, NULL);
+ }
}
}
@@ -1783,6 +1801,78 @@
return NULL;
}
+static gboolean parse_argument_advertise(const char *arg, dbus_bool_t *value,
+ const char **type)
+{
+ const char * const *opt;
+
+ if (arg == NULL || strlen(arg) == 0) {
+ rl_printf("Missing on/off/type argument\n");
+ return FALSE;
+ }
+
+ if (strcmp(arg, "on") == 0 || strcmp(arg, "yes") == 0) {
+ *value = TRUE;
+ *type = "";
+ return TRUE;
+ }
+
+ if (strcmp(arg, "off") == 0 || strcmp(arg, "no") == 0) {
+ *value = FALSE;
+ return TRUE;
+ }
+
+ for (opt = ad_arguments; *opt; opt++) {
+ if (strcmp(arg, *opt) == 0) {
+ *value = TRUE;
+ *type = *opt;
+ return TRUE;
+ }
+ }
+
+ rl_printf("Invalid argument %s\n", arg);
+ return FALSE;
+}
+
+static void cmd_advertise(const char *arg)
+{
+ dbus_bool_t enable;
+ const char *type;
+
+ if (parse_argument_advertise(arg, &enable, &type) == FALSE)
+ return;
+
+ if (!ad_manager) {
+ rl_printf("LEAdvertisingManager not found\n");
+ return;
+ }
+
+ if (enable == TRUE)
+ ad_register(dbus_conn, ad_manager, type);
+ else
+ ad_unregister(dbus_conn, ad_manager);
+}
+
+static char *ad_generator(const char *text, int state)
+{
+ static int index, len;
+ const char *arg;
+
+ if (!state) {
+ index = 0;
+ len = strlen(text);
+ }
+
+ while ((arg = ad_arguments[index])) {
+ index++;
+
+ if (!strncmp(arg, text, len))
+ return strdup(arg);
+ }
+
+ return NULL;
+}
+
static const struct {
const char *cmd;
const char *arg;
@@ -1811,6 +1901,9 @@
capability_generator},
{ "default-agent",NULL, cmd_default_agent,
"Set agent as the default one" },
+ { "advertise", "<on/off/type>", cmd_advertise,
+ "Enable/disable advertising with given type",
+ ad_generator},
{ "set-scan-filter-uuids", "[uuid1 uuid2 ...]",
cmd_set_scan_filter_uuids, "Set scan filter uuids" },
{ "set-scan-filter-rssi", "[rssi]", cmd_set_scan_filter_rssi,