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
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
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
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