2020-10-28 23:10:00

by Miao-chen Chou

[permalink] [raw]
Subject: [BlueZ PATCH v7 1/7] adv_monitor: Implement RSSI Filter logic for background scanning

From: Manish Mandlik <[email protected]>

This patch implements the RSSI Filter logic for background scanning.

Reviewed-by: Abhishek Pandit-Subedi <[email protected]>
Reviewed-by: Alain Michaud <[email protected]>
Reviewed-by: Miao-chen Chou <[email protected]>
Reviewed-by: Howard Chung <[email protected]>
---

Changes in v7:
- Add logs to NULL check of objects to reveal potential bugs
- Rename some members of struct adv_monitor_device

Changes in v5:
- Remove the use of unit test in commit message

Changes in v3:
- Fix commit message

doc/advertisement-monitor-api.txt | 5 +
src/adapter.c | 1 +
src/adv_monitor.c | 296 +++++++++++++++++++++++++++++-
src/adv_monitor.h | 4 +
4 files changed, 302 insertions(+), 4 deletions(-)

diff --git a/doc/advertisement-monitor-api.txt b/doc/advertisement-monitor-api.txt
index e09b6fd25..92c8ffc38 100644
--- a/doc/advertisement-monitor-api.txt
+++ b/doc/advertisement-monitor-api.txt
@@ -70,6 +70,11 @@ Properties string Type [read-only]
dBm indicates unset. The valid range of a timer is 1 to
300 seconds while 0 indicates unset.

+ If the peer device advertising interval is greater than the
+ HighRSSIThresholdTimer, the device will never be found. Similarly,
+ if it is greater than LowRSSIThresholdTimer, the device will be
+ considered as lost. Consider configuring these values accordingly.
+
array{(uint8, uint8, array{byte})} Patterns [read-only, optional]

If Type is set to 0x01, this must exist and has at least
diff --git a/src/adapter.c b/src/adapter.c
index c0053000a..6d0114a6b 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -1214,6 +1214,7 @@ void btd_adapter_remove_device(struct btd_adapter *adapter,
adapter->connect_list = g_slist_remove(adapter->connect_list, dev);

adapter->devices = g_slist_remove(adapter->devices, dev);
+ btd_adv_monitor_device_remove(adapter->adv_monitor_manager, dev);

adapter->discovery_found = g_slist_remove(adapter->discovery_found,
dev);
diff --git a/src/adv_monitor.c b/src/adv_monitor.c
index e441a5566..74351d91e 100644
--- a/src/adv_monitor.c
+++ b/src/adv_monitor.c
@@ -26,6 +26,7 @@

#include "adapter.h"
#include "dbus-common.h"
+#include "device.h"
#include "log.h"
#include "src/error.h"
#include "src/shared/ad.h"
@@ -35,6 +36,8 @@

#include "adv_monitor.h"

+static void monitor_device_free(void *data);
+
#define ADV_MONITOR_INTERFACE "org.bluez.AdvertisementMonitor1"
#define ADV_MONITOR_MGR_INTERFACE "org.bluez.AdvertisementMonitorManager1"

@@ -95,15 +98,36 @@ struct adv_monitor {

enum monitor_state state; /* MONITOR_STATE_* */

- int8_t high_rssi; /* high RSSI threshold */
- uint16_t high_rssi_timeout; /* high RSSI threshold timeout */
- int8_t low_rssi; /* low RSSI threshold */
- uint16_t low_rssi_timeout; /* low RSSI threshold timeout */
+ int8_t high_rssi; /* High RSSI threshold */
+ uint16_t high_rssi_timeout; /* High RSSI threshold timeout */
+ int8_t low_rssi; /* Low RSSI threshold */
+ uint16_t low_rssi_timeout; /* Low RSSI threshold timeout */
+ struct queue *devices; /* List of adv_monitor_device objects */

enum monitor_type type; /* MONITOR_TYPE_* */
struct queue *patterns;
};

+/* Some data like last_seen, timer/timeout values need to be maintained
+ * per device. struct adv_monitor_device maintains such data.
+ */
+struct adv_monitor_device {
+ struct adv_monitor *monitor;
+ struct btd_device *device;
+
+ time_t high_rssi_first_seen; /* Start time when RSSI climbs above
+ * the high RSSI threshold
+ */
+ time_t low_rssi_first_seen; /* Start time when RSSI drops below
+ * the low RSSI threshold
+ */
+ time_t last_seen; /* Time when last Adv was received */
+ bool found; /* State of the device - lost/found */
+ guint lost_timer; /* Timer to track if the device goes
+ * offline/out-of-range
+ */
+};
+
struct app_match_data {
const char *owner;
const char *path;
@@ -150,6 +174,9 @@ static void monitor_free(void *data)
g_dbus_proxy_unref(monitor->proxy);
g_free(monitor->path);

+ queue_destroy(monitor->devices, monitor_device_free);
+ monitor->devices = NULL;
+
queue_destroy(monitor->patterns, pattern_free);

free(monitor);
@@ -248,6 +275,7 @@ static struct adv_monitor *monitor_new(struct adv_monitor_app *app,
monitor->high_rssi_timeout = ADV_MONITOR_UNSET_TIMER;
monitor->low_rssi = ADV_MONITOR_UNSET_RSSI;
monitor->low_rssi_timeout = ADV_MONITOR_UNSET_TIMER;
+ monitor->devices = queue_new();

monitor->type = MONITOR_TYPE_NONE;
monitor->patterns = NULL;
@@ -923,3 +951,263 @@ void btd_adv_monitor_manager_destroy(struct btd_adv_monitor_manager *manager)

manager_destroy(manager);
}
+
+/* Matches a device based on btd_device object */
+static bool monitor_device_match(const void *a, const void *b)
+{
+ const struct adv_monitor_device *dev = a;
+ const struct btd_device *device = b;
+
+ if (!dev) {
+ error("Unexpected NULL adv_monitor_device object upon match");
+ return false;
+ }
+
+ if (dev->device != device)
+ return false;
+
+ return true;
+}
+
+/* Frees a monitor device object */
+static void monitor_device_free(void *data)
+{
+ struct adv_monitor_device *dev = data;
+
+ if (!dev) {
+ error("Unexpected NULL adv_monitor_device object upon free");
+ return;
+ }
+
+ if (dev->lost_timer) {
+ g_source_remove(dev->lost_timer);
+ dev->lost_timer = 0;
+ }
+
+ dev->monitor = NULL;
+ dev->device = NULL;
+
+ free(dev);
+}
+
+/* Removes a device from monitor->devices list */
+static void remove_device_from_monitor(void *data, void *user_data)
+{
+ struct adv_monitor *monitor = data;
+ struct btd_device *device = user_data;
+ struct adv_monitor_device *dev = NULL;
+
+ if (!monitor) {
+ error("Unexpected NULL adv_monitor object upon device remove");
+ return;
+ }
+
+ dev = queue_remove_if(monitor->devices, monitor_device_match, device);
+ if (dev) {
+ DBG("Device removed from the Adv Monitor at path %s",
+ monitor->path);
+ monitor_device_free(dev);
+ }
+}
+
+/* Removes a device from every monitor in an app */
+static void remove_device_from_app(void *data, void *user_data)
+{
+ struct adv_monitor_app *app = data;
+ struct btd_device *device = user_data;
+
+ if (!app) {
+ error("Unexpected NULL adv_monitor_app object upon device "
+ "remove");
+ return;
+ }
+
+ queue_foreach(app->monitors, remove_device_from_monitor, device);
+}
+
+/* Removes a device from every monitor in all apps */
+void btd_adv_monitor_device_remove(struct btd_adv_monitor_manager *manager,
+ struct btd_device *device)
+{
+ if (!manager || !device)
+ return;
+
+ queue_foreach(manager->apps, remove_device_from_app, device);
+}
+
+/* Creates a device object to track the per-device information */
+static struct adv_monitor_device *monitor_device_create(
+ struct adv_monitor *monitor,
+ struct btd_device *device)
+{
+ struct adv_monitor_device *dev = NULL;
+
+ dev = new0(struct adv_monitor_device, 1);
+ if (!dev)
+ return NULL;
+
+ dev->monitor = monitor;
+ dev->device = device;
+
+ queue_push_tail(monitor->devices, dev);
+
+ return dev;
+}
+
+/* Includes found/lost device's object path into the dbus message */
+static void report_device_state_setup(DBusMessageIter *iter, void *user_data)
+{
+ const char *path = device_get_path(user_data);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+}
+
+/* Handles a situation where the device goes offline/out-of-range */
+static gboolean handle_device_lost_timeout(gpointer user_data)
+{
+ struct adv_monitor_device *dev = user_data;
+ struct adv_monitor *monitor = dev->monitor;
+ time_t curr_time = time(NULL);
+
+ DBG("Device Lost timeout triggered for device %p "
+ "for the Adv Monitor at path %s", dev->device, monitor->path);
+
+ dev->lost_timer = 0;
+
+ if (dev->found && dev->last_seen) {
+ /* We were tracking for the Low RSSI filter. Check if there is
+ * any Adv received after the timeout function is invoked.
+ * If not, report the Device Lost event.
+ */
+ if (difftime(curr_time, dev->last_seen) >=
+ monitor->low_rssi_timeout) {
+ dev->found = false;
+
+ DBG("Calling DeviceLost() on Adv Monitor of owner %s "
+ "at path %s", monitor->app->owner, monitor->path);
+
+ g_dbus_proxy_method_call(monitor->proxy, "DeviceLost",
+ report_device_state_setup,
+ NULL, dev->device, NULL);
+ }
+ }
+
+ return FALSE;
+}
+
+/* Filters an Adv based on its RSSI value */
+static void adv_monitor_filter_rssi(struct adv_monitor *monitor,
+ struct btd_device *device, int8_t rssi)
+{
+ struct adv_monitor_device *dev = NULL;
+ time_t curr_time = time(NULL);
+ uint16_t adapter_id = monitor->app->manager->adapter_id;
+
+ /* If the RSSI thresholds and timeouts are not specified, report the
+ * DeviceFound() event without tracking for the RSSI as the Adv has
+ * already matched the pattern filter.
+ */
+ if (monitor->high_rssi == ADV_MONITOR_UNSET_RSSI &&
+ monitor->low_rssi == ADV_MONITOR_UNSET_RSSI &&
+ monitor->high_rssi_timeout == ADV_MONITOR_UNSET_TIMER &&
+ monitor->low_rssi_timeout == ADV_MONITOR_UNSET_TIMER) {
+ DBG("Calling DeviceFound() on Adv Monitor of owner %s "
+ "at path %s", monitor->app->owner, monitor->path);
+
+ g_dbus_proxy_method_call(monitor->proxy, "DeviceFound",
+ report_device_state_setup, NULL,
+ device, NULL);
+
+ return;
+ }
+
+ dev = queue_find(monitor->devices, monitor_device_match, device);
+ if (!dev) {
+ dev = monitor_device_create(monitor, device);
+ if (!dev) {
+ btd_error(adapter_id, "Failed to create Adv Monitor "
+ "device object.");
+ return;
+ }
+ }
+
+ if (dev->lost_timer) {
+ g_source_remove(dev->lost_timer);
+ dev->lost_timer = 0;
+ }
+
+ /* Reset the timings of found/lost if a device has been offline for
+ * longer than the high/low timeouts.
+ */
+ if (dev->last_seen) {
+ if (difftime(curr_time, dev->last_seen) >
+ monitor->high_rssi_timeout) {
+ dev->high_rssi_first_seen = 0;
+ }
+
+ if (difftime(curr_time, dev->last_seen) >
+ monitor->low_rssi_timeout) {
+ dev->low_rssi_first_seen = 0;
+ }
+ }
+ dev->last_seen = curr_time;
+
+ /* Check for the found devices (if the device is not already found) */
+ if (!dev->found && rssi > monitor->high_rssi) {
+ if (dev->high_rssi_first_seen) {
+ if (difftime(curr_time, dev->high_rssi_first_seen) >=
+ monitor->high_rssi_timeout) {
+ dev->found = true;
+
+ DBG("Calling DeviceFound() on Adv Monitor "
+ "of owner %s at path %s",
+ monitor->app->owner, monitor->path);
+
+ g_dbus_proxy_method_call(
+ monitor->proxy, "DeviceFound",
+ report_device_state_setup, NULL,
+ dev->device, NULL);
+ }
+ } else {
+ dev->high_rssi_first_seen = curr_time;
+ }
+ } else {
+ dev->high_rssi_first_seen = 0;
+ }
+
+ /* Check for the lost devices (only if the device is already found, as
+ * it doesn't make any sense to report the Device Lost event if the
+ * device is not found yet)
+ */
+ if (dev->found && rssi < monitor->low_rssi) {
+ if (dev->low_rssi_first_seen) {
+ if (difftime(curr_time, dev->low_rssi_first_seen) >=
+ monitor->low_rssi_timeout) {
+ dev->found = false;
+
+ DBG("Calling DeviceLost() on Adv Monitor "
+ "of owner %s at path %s",
+ monitor->app->owner, monitor->path);
+
+ g_dbus_proxy_method_call(
+ monitor->proxy, "DeviceLost",
+ report_device_state_setup, NULL,
+ dev->device, NULL);
+ }
+ } else {
+ dev->low_rssi_first_seen = curr_time;
+ }
+ } else {
+ dev->low_rssi_first_seen = 0;
+ }
+
+ /* Setup a timer to track if the device goes offline/out-of-range, only
+ * if we are tracking for the Low RSSI Threshold. If we are tracking
+ * the High RSSI Threshold, nothing needs to be done.
+ */
+ if (dev->found) {
+ dev->lost_timer =
+ g_timeout_add_seconds(monitor->low_rssi_timeout,
+ handle_device_lost_timeout, dev);
+ }
+}
diff --git a/src/adv_monitor.h b/src/adv_monitor.h
index 5cb372217..13d5d7282 100644
--- a/src/adv_monitor.h
+++ b/src/adv_monitor.h
@@ -12,6 +12,7 @@
#define __ADV_MONITOR_H

struct mgmt;
+struct btd_device;
struct btd_adapter;
struct btd_adv_monitor_manager;

@@ -20,4 +21,7 @@ struct btd_adv_monitor_manager *btd_adv_monitor_manager_create(
struct mgmt *mgmt);
void btd_adv_monitor_manager_destroy(struct btd_adv_monitor_manager *manager);

+void btd_adv_monitor_device_remove(struct btd_adv_monitor_manager *manager,
+ struct btd_device *device);
+
#endif /* __ADV_MONITOR_H */
--
2.26.2


2020-10-28 23:10:17

by Miao-chen Chou

[permalink] [raw]
Subject: [BlueZ PATCH v7 6/7] adv_monitor: Fix return type of RegisterMonitor() method

This modifies the D-Bus call return type to be asynchronous for
RegisterMonitor() method call.

The following test was performed:
- Enter bluetoothctl, exit the console and re-enter the console without
AlreadyExist error for RegisterMonitor() upon bring-up of the console.

Reviewed-by: Howard Chung <[email protected]>
Reviewed-by: Manish Mandlik <[email protected]>
---

(no changes since v1)

src/adv_monitor.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/adv_monitor.c b/src/adv_monitor.c
index 9d2a400a3..dbc3b2a92 100644
--- a/src/adv_monitor.c
+++ b/src/adv_monitor.c
@@ -694,6 +694,8 @@ static struct adv_monitor_app *app_create(DBusConnection *conn,

app->monitors = queue_new();

+ app->reg = dbus_message_ref(msg);
+
g_dbus_client_set_disconnect_watch(app->client, app_disconnect_cb, app);

/* Note that any property changes on a monitor object would not affect
@@ -705,8 +707,6 @@ static struct adv_monitor_app *app_create(DBusConnection *conn,

g_dbus_client_set_ready_watch(app->client, app_ready_cb, app);

- app->reg = dbus_message_ref(msg);
-
return app;
}

@@ -800,7 +800,7 @@ static DBusMessage *unregister_monitor(DBusConnection *conn,
}

static const GDBusMethodTable adv_monitor_methods[] = {
- { GDBUS_EXPERIMENTAL_METHOD("RegisterMonitor",
+ { GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterMonitor",
GDBUS_ARGS({ "application", "o" }),
NULL, register_monitor) },
{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("UnregisterMonitor",
--
2.26.2

2020-10-28 23:11:02

by Miao-chen Chou

[permalink] [raw]
Subject: [BlueZ PATCH v7 5/7] adv_monitor: Implement Add Adv Patterns Monitor cmd handler

From: Howard Chung <[email protected]>

- Send the MGMT_OP command to kernel upon registration of a Adv patterns
monitor.
- Call Activate() or Release() to client depending on the reply from
kernel

Reviewed-by: Alain Michaud <[email protected]>
Reviewed-by: Miao-chen Chou <[email protected]>
Reviewed-by: Manish Mandlik <[email protected]>
---

Changes in v7:
- Rename MONITOR_STATE_HONORED to MONITOR_STATE_ACTIVE
- Rebase on the adoption of bt_ad_pattern

src/adv_monitor.c | 69 +++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 67 insertions(+), 2 deletions(-)

diff --git a/src/adv_monitor.c b/src/adv_monitor.c
index 9a04da6e1..9d2a400a3 100644
--- a/src/adv_monitor.c
+++ b/src/adv_monitor.c
@@ -78,7 +78,7 @@ enum monitor_state {
MONITOR_STATE_NEW, /* New but not yet init'ed with actual values */
MONITOR_STATE_FAILED, /* Failed to be init'ed */
MONITOR_STATE_INITED, /* Init'ed but not yet sent to kernel */
- MONITOR_STATE_HONORED, /* Accepted by kernel */
+ MONITOR_STATE_ACTIVE, /* Accepted by kernel */
};

struct adv_monitor {
@@ -545,11 +545,59 @@ done:
return monitor->state != MONITOR_STATE_FAILED;
}

+/* Handles the callback of Add Adv Patterns Monitor command */
+static void add_adv_patterns_monitor_cb(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_rp_add_adv_patterns_monitor *rp = param;
+ struct adv_monitor *monitor = user_data;
+ uint16_t adapter_id = monitor->app->manager->adapter_id;
+
+ if (status != MGMT_STATUS_SUCCESS || !param) {
+ btd_error(adapter_id, "Failed to Add Adv Patterns Monitor "
+ "with status 0x%02x", status);
+ monitor_release(monitor, NULL);
+ return;
+ }
+
+ if (length < sizeof(*rp)) {
+ btd_error(adapter_id, "Wrong size of Add Adv Patterns Monitor "
+ "response");
+ monitor_release(monitor, NULL);
+ return;
+ }
+
+ monitor->state = MONITOR_STATE_ACTIVE;
+
+ DBG("Calling Activate() on Adv Monitor of owner %s at path %s",
+ monitor->app->owner, monitor->path);
+
+ g_dbus_proxy_method_call(monitor->proxy, "Activate", NULL, NULL, NULL,
+ NULL);
+
+ DBG("Adv Monitor with handle:0x%04x added",
+ le16_to_cpu(rp->monitor_handle));
+}
+
+static void monitor_copy_patterns(void *data, void *user_data)
+{
+ struct bt_ad_pattern *pattern = data;
+ struct mgmt_cp_add_adv_monitor *cp = user_data;
+
+ if (!pattern)
+ return;
+
+ memcpy(cp->patterns + cp->pattern_count, pattern, sizeof(*pattern));
+ cp->pattern_count++;
+}
+
/* Handles an Adv Monitor D-Bus proxy added event */
static void monitor_proxy_added_cb(GDBusProxy *proxy, void *user_data)
{
struct adv_monitor *monitor;
struct adv_monitor_app *app = user_data;
+ struct mgmt_cp_add_adv_monitor *cp = NULL;
+ uint8_t pattern_count, cp_len;
uint16_t adapter_id = app->manager->adapter_id;
const char *path = g_dbus_proxy_get_path(proxy);
const char *iface = g_dbus_proxy_get_interface(proxy);
@@ -582,7 +630,24 @@ static void monitor_proxy_added_cb(GDBusProxy *proxy, void *user_data)

queue_push_tail(app->monitors, monitor);

+ pattern_count = queue_length(monitor->patterns);
+ cp_len = sizeof(struct mgmt_cp_add_adv_monitor) +
+ pattern_count * sizeof(struct mgmt_adv_pattern);
+
+ cp = malloc0(cp_len);
+ queue_foreach(monitor->patterns, monitor_copy_patterns, cp);
+
+ if (!mgmt_send(app->manager->mgmt, MGMT_OP_ADD_ADV_PATTERNS_MONITOR,
+ adapter_id, cp_len, cp, add_adv_patterns_monitor_cb,
+ monitor, NULL)) {
+ error("Unable to send Add Adv Patterns Monitor command");
+ goto done;
+ }
+
DBG("Adv Monitor allocated for the object at path %s", path);
+
+done:
+ free(cp);
}

/* Handles the removal of an Adv Monitor D-Bus proxy */
@@ -946,7 +1011,7 @@ static void adv_match_per_monitor(void *data, void *user_data)
return;
}

- if (monitor->state != MONITOR_STATE_HONORED)
+ if (monitor->state != MONITOR_STATE_ACTIVE)
return;

if (monitor->type == MONITOR_TYPE_OR_PATTERNS &&
--
2.26.2

2020-10-28 23:43:53

by bluez.test.bot

[permalink] [raw]
Subject: RE: [BlueZ,v7,1/7] adv_monitor: Implement RSSI Filter logic for background scanning

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=372473

---Test result---

##############################
Test: CheckPatch - PASS

##############################
Test: CheckGitLint - PASS

##############################
Test: CheckBuild - PASS

##############################
Test: MakeCheck - PASS



---
Regards,
Linux Bluetooth