2018-02-08 11:10:29

by Szymon Janc

[permalink] [raw]
Subject: [RFC] adapter: Add support for Limited Discoverable mode

This allows to set adapter in Limited Discoverable mode for up to 60
seconds. Any registered advertising instances flags are updated as
well.
---
doc/adapter-api.txt | 20 ++++++++
src/adapter.c | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++++
src/adapter.h | 1 +
src/advertising.c | 4 +-
4 files changed, 155 insertions(+), 1 deletion(-)

diff --git a/doc/adapter-api.txt b/doc/adapter-api.txt
index 0533b674a..ea50eeb72 100644
--- a/doc/adapter-api.txt
+++ b/doc/adapter-api.txt
@@ -225,6 +225,26 @@ Properties string Address [readonly]

For any new adapter this settings defaults to false.

+ boolean LimitedDiscoverable [readwrite, experimental]
+
+ Switch an adapter to limited discoverable or
+ non-discoverable to either make it visible or hide it.
+ This is a global setting and should only be used by the
+ settings application.
+
+ Adapter can be in limited discoverable mode for up to
+ 60 seconds. DiscoverableTimeout is honored but capped to
+ 60 seconds limit.
+
+ In case the adapter is switched off or already
+ general discoverable, setting this value will fail.
+
+ When changing the Powered property the new state of
+ this property will be updated via a PropertiesChanged
+ signal.
+
+ For any new adapter this settings defaults to false.
+
boolean Pairable [readwrite]

Switch an adapter to pairable or non-pairable. This is
diff --git a/src/adapter.c b/src/adapter.c
index 5070c0f09..3898156e3 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -97,6 +97,8 @@
#define DISTANCE_VAL_INVALID 0x7FFF
#define PATHLOSS_MAX 137

+#define LIMITED_TIMEOUT (60)
+
static DBusConnection *dbus_conn = NULL;

static bool kernel_conn_control = false;
@@ -205,6 +207,7 @@ struct btd_adapter {
char *modalias; /* device id (modalias) */
bool stored_discoverable; /* stored discoverable mode */
uint32_t discoverable_timeout; /* discoverable time(sec) */
+ bool limited; /* if limited discoverable */
uint32_t pairable_timeout; /* pairable time(sec) */

char *current_alias; /* current adapter name alias */
@@ -540,6 +543,22 @@ static void settings_changed(struct btd_adapter *adapter, uint32_t settings)
ADAPTER_INTERFACE, "Connectable");

if (changed_mask & MGMT_SETTING_DISCOVERABLE) {
+ if (adapter->limited) {
+ bool discoverable;
+ bool powered;
+
+ discoverable = adapter->current_settings &
+ MGMT_SETTING_DISCOVERABLE;
+ powered = adapter->current_settings &
+ MGMT_SETTING_POWERED;
+
+ adapter->limited = discoverable && powered;
+
+ g_dbus_emit_property_changed(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE,
+ "LimitedDiscoverable");
+ }
+
g_dbus_emit_property_changed(dbus_conn, adapter->path,
ADAPTER_INTERFACE, "Discoverable");
store_adapter_info(adapter);
@@ -2830,6 +2849,107 @@ static void property_set_discoverable(const GDBusPropertyTable *property,
property_set_mode(adapter, MGMT_SETTING_DISCOVERABLE, iter, id);
}

+static gboolean property_get_limited(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ dbus_bool_t enable;
+
+ enable = adapter->limited &&
+ (adapter->current_settings & MGMT_SETTING_DISCOVERABLE);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &enable);
+
+ return TRUE;
+}
+
+static void set_limited(struct btd_adapter *adapter, bool enable,
+ GDBusPendingPropertySet id)
+{
+ struct property_set_data *data;
+ struct mgmt_cp_set_discoverable cp;
+ void *param;
+ uint16_t opcode, len;
+ uint8_t mode;
+
+ memset(&cp, 0, sizeof(cp));
+
+ if (enable) {
+ if (kernel_conn_control)
+ set_mode(adapter, MGMT_OP_SET_CONNECTABLE, 0x01);
+
+ cp.val = 0x02;
+ if (adapter->discoverable_timeout)
+ cp.timeout = htobs(MIN(adapter->discoverable_timeout,
+ LIMITED_TIMEOUT));
+ else
+ cp.timeout = htobs(LIMITED_TIMEOUT);
+
+ opcode = MGMT_OP_SET_DISCOVERABLE;
+ param = &cp;
+ len = sizeof(cp);
+ } else {
+ if (kernel_conn_control) {
+ mode = 0x00;
+ opcode = MGMT_OP_SET_CONNECTABLE;
+ param = &mode;
+ len = sizeof(mode);
+ } else {
+ cp.val = 0x00;
+ opcode = MGMT_OP_SET_DISCOVERABLE;
+ param = &cp;
+ len = sizeof(cp);
+ }
+ }
+
+ DBG("sending %s command for index %u", mgmt_opstr(opcode),
+ adapter->dev_id);
+
+ data = g_new0(struct property_set_data, 1);
+ data->adapter = adapter;
+ data->id = id;
+
+ if (mgmt_send(adapter->mgmt, opcode, adapter->dev_id, len, param,
+ property_set_mode_complete, data, g_free) > 0)
+ return;
+
+ g_free(data);
+ btd_error(adapter->dev_id, "Failed to set mode for index %u",
+ adapter->dev_id);
+
+ g_dbus_pending_property_error(id, ERROR_INTERFACE ".Failed", NULL);
+}
+
+static void property_set_limited(const GDBusPropertyTable *property,
+ DBusMessageIter *iter,
+ GDBusPendingPropertySet id, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ dbus_bool_t enable;
+
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED)) {
+ g_dbus_pending_property_error(id, ERROR_INTERFACE ".Failed",
+ "Not Powered");
+ return;
+ }
+
+ dbus_message_iter_get_basic(iter, &enable);
+
+ if (adapter->limited == !!enable) {
+ g_dbus_pending_property_success(id);
+ return;
+ }
+
+ if (enable && (adapter->current_settings & MGMT_SETTING_DISCOVERABLE)) {
+ g_dbus_pending_property_error(id, ERROR_INTERFACE ".Failed",
+ "General Discoverable");
+ return;
+ }
+
+ adapter->limited = true;
+ set_limited(adapter, enable, id);
+}
+
static gboolean property_get_discoverable_timeout(
const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
@@ -3101,6 +3221,8 @@ static const GDBusPropertyTable adapter_properties[] = {
{ "Powered", "b", property_get_powered, property_set_powered },
{ "Discoverable", "b", property_get_discoverable,
property_set_discoverable },
+ { "LimitedDiscoverable", "b", property_get_limited,
+ property_set_limited, NULL, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL},
{ "DiscoverableTimeout", "u", property_get_discoverable_timeout,
property_set_discoverable_timeout },
{ "Pairable", "b", property_get_pairable, property_set_pairable },
@@ -4117,6 +4239,15 @@ bool btd_adapter_get_discoverable(struct btd_adapter *adapter)
return false;
}

+bool btd_adapter_get_limited(struct btd_adapter *adapter)
+{
+ if ((adapter->current_settings & MGMT_SETTING_DISCOVERABLE) &&
+ adapter->limited)
+ return true;
+
+ return false;
+}
+
struct btd_gatt_database *btd_adapter_get_database(struct btd_adapter *adapter)
{
if (!adapter)
diff --git a/src/adapter.h b/src/adapter.h
index e619a5be9..1ddf26576 100644
--- a/src/adapter.h
+++ b/src/adapter.h
@@ -75,6 +75,7 @@ bool btd_adapter_get_pairable(struct btd_adapter *adapter);
bool btd_adapter_get_powered(struct btd_adapter *adapter);
bool btd_adapter_get_connectable(struct btd_adapter *adapter);
bool btd_adapter_get_discoverable(struct btd_adapter *adapter);
+bool btd_adapter_get_limited(struct btd_adapter *adapter);

struct btd_gatt_database *btd_adapter_get_database(struct btd_adapter *adapter);

diff --git a/src/advertising.c b/src/advertising.c
index 0af9f02d6..3dba1465d 100644
--- a/src/advertising.c
+++ b/src/advertising.c
@@ -625,7 +625,9 @@ static int refresh_adv(struct btd_adv_client *client, mgmt_request_func_t func)
if (client->type == AD_TYPE_PERIPHERAL) {
flags = MGMT_ADV_FLAG_CONNECTABLE;

- if (btd_adapter_get_discoverable(client->manager->adapter))
+ if (btd_adapter_get_limited(client->manager->adapter))
+ flags |= MGMT_ADV_FLAG_LIMITED_DISCOV;
+ else if (btd_adapter_get_discoverable(client->manager->adapter))
flags |= MGMT_ADV_FLAG_DISCOV;
}

--
2.14.3