This implements create an entry point in adapter to start the matching of Adv based on all monitors and invoke the RSSI tracking for Adv reporting. Reviewed-by: Abhishek Pandit-Subedi <abhishekpandit@xxxxxxxxxxxx> Reviewed-by: Alain Michaud <alainm@xxxxxxxxxxxx> Reviewed-by: Manish Mandlik <mmandlik@xxxxxxxxxxxx> --- Changes in v7: - Replace the use of GSList with struct queue - Adopt bt_ad_pattern from shared/ad - Add error logs Changes in v6: - Fix the termination condition of AD data paring and remove unnecessary length check Changes in v5: - Remove unittest helper functions Changes in v3: - Remove unused variables - Fix signature of queue_find() src/adapter.c | 44 +++++++++++--- src/adv_monitor.c | 151 +++++++++++++++++++++++++++++++++++----------- src/adv_monitor.h | 14 +++++ 3 files changed, 167 insertions(+), 42 deletions(-) diff --git a/src/adapter.c b/src/adapter.c index 6d0114a6b..0e3fd57f3 100644 --- a/src/adapter.c +++ b/src/adapter.c @@ -6597,10 +6597,28 @@ static void update_found_devices(struct btd_adapter *adapter, const uint8_t *data, uint8_t data_len) { struct btd_device *dev; + struct bt_ad *ad = NULL; struct eir_data eir_data; bool name_known, discoverable; char addr[18]; bool duplicate = false; + struct queue *matched_monitors = NULL; + + if (bdaddr_type != BDADDR_BREDR) + ad = bt_ad_new_with_data(data_len, data); + + /* During the background scanning, update the device only when the data + * match at least one Adv monitor + */ + if (ad) { + matched_monitors = btd_adv_monitor_content_filter( + adapter->adv_monitor_manager, ad); + bt_ad_unref(ad); + ad = NULL; + } + + if (!adapter->discovering && !matched_monitors) + return; memset(&eir_data, 0, sizeof(eir_data)); eir_parse(&eir_data, data, data_len); @@ -6646,18 +6664,22 @@ static void update_found_devices(struct btd_adapter *adapter, device_store_cached_name(dev, eir_data.name); /* - * Only skip devices that are not connected, are temporary and there - * is no active discovery session ongoing. + * Only skip devices that are not connected, are temporary, and there + * is no active discovery session ongoing and no matched Adv monitors */ - if (!btd_device_is_connected(dev) && (device_is_temporary(dev) && - !adapter->discovery_list)) { + if (!btd_device_is_connected(dev) && + (device_is_temporary(dev) && !adapter->discovery_list) && + !matched_monitors) { eir_data_free(&eir_data); return; } - /* Don't continue if not discoverable or if filter don't match */ - if (!discoverable || (adapter->filtered_discovery && - !is_filter_match(adapter->discovery_list, &eir_data, rssi))) { + /* If there is no matched Adv monitors, don't continue if not + * discoverable or if active discovery filter don't match. + */ + if (!matched_monitors && (!discoverable || + (adapter->filtered_discovery && !is_filter_match( + adapter->discovery_list, &eir_data, rssi)))) { eir_data_free(&eir_data); return; } @@ -6714,6 +6736,14 @@ static void update_found_devices(struct btd_adapter *adapter, eir_data_free(&eir_data); + /* After the device is updated, notify the matched Adv monitors */ + if (matched_monitors) { + btd_adv_monitor_notify_monitors(adapter->adv_monitor_manager, + dev, rssi, matched_monitors); + queue_destroy(matched_monitors, NULL); + matched_monitors = NULL; + } + /* * Only if at least one client has requested discovery, maintain * list of found devices and name confirming for legacy devices. diff --git a/src/adv_monitor.c b/src/adv_monitor.c index 74351d91e..9a04da6e1 100644 --- a/src/adv_monitor.c +++ b/src/adv_monitor.c @@ -29,15 +29,12 @@ #include "device.h" #include "log.h" #include "src/error.h" -#include "src/shared/ad.h" #include "src/shared/mgmt.h" #include "src/shared/queue.h" #include "src/shared/util.h" #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" @@ -84,13 +81,6 @@ enum monitor_state { MONITOR_STATE_HONORED, /* Accepted by kernel */ }; -struct pattern { - uint8_t ad_type; - uint8_t offset; - uint8_t length; - uint8_t value[BT_AD_MAX_DATA_LEN]; -}; - struct adv_monitor { struct adv_monitor_app *app; GDBusProxy *proxy; @@ -105,7 +95,7 @@ struct adv_monitor { struct queue *devices; /* List of adv_monitor_device objects */ enum monitor_type type; /* MONITOR_TYPE_* */ - struct queue *patterns; + struct queue *patterns; /* List of bt_ad_pattern objects */ }; /* Some data like last_seen, timer/timeout values need to be maintained @@ -133,6 +123,20 @@ struct app_match_data { const char *path; }; +struct adv_content_filter_info { + struct bt_ad *ad; + struct queue *matched_monitors; /* List of matched monitors */ +}; + +struct adv_rssi_filter_info { + struct btd_device *device; + int8_t rssi; +}; + +static void monitor_device_free(void *data); +static void adv_monitor_filter_rssi(struct adv_monitor *monitor, + struct btd_device *device, int8_t rssi); + const struct adv_monitor_type { enum monitor_type type; const char *name; @@ -155,10 +159,7 @@ static void app_reply_msg(struct adv_monitor_app *app, DBusMessage *reply) /* Frees a pattern */ static void pattern_free(void *data) { - struct pattern *pattern = data; - - if (!pattern) - return; + struct bt_ad_pattern *pattern = data; free(pattern); } @@ -464,7 +465,7 @@ static bool parse_patterns(struct adv_monitor *monitor, const char *path) int value_len; uint8_t *value; uint8_t offset, ad_type; - struct pattern *pattern; + struct bt_ad_pattern *pattern; DBusMessageIter struct_iter, value_iter; dbus_message_iter_recurse(&array_iter, &struct_iter); @@ -496,28 +497,10 @@ static bool parse_patterns(struct adv_monitor *monitor, const char *path) dbus_message_iter_get_fixed_array(&value_iter, &value, &value_len); - // Verify the values - if (offset > BT_AD_MAX_DATA_LEN - 1) - goto failed; - - if ((ad_type > BT_AD_3D_INFO_DATA && - ad_type != BT_AD_MANUFACTURER_DATA) || - ad_type < BT_AD_FLAGS) { - goto failed; - } - - if (!value || value_len <= 0 || value_len > BT_AD_MAX_DATA_LEN) - goto failed; - - pattern = new0(struct pattern, 1); + pattern = bt_ad_pattern_new(ad_type, offset, value_len, value); if (!pattern) goto failed; - pattern->ad_type = ad_type; - pattern->offset = offset; - pattern->length = value_len; - memcpy(pattern->value, value, pattern->length); - queue_push_tail(monitor->patterns, pattern); dbus_message_iter_next(&array_iter); @@ -952,6 +935,104 @@ void btd_adv_monitor_manager_destroy(struct btd_adv_monitor_manager *manager) manager_destroy(manager); } +/* Processes the content matching based pattern(s) of a monitor */ +static void adv_match_per_monitor(void *data, void *user_data) +{ + struct adv_monitor *monitor = data; + struct adv_content_filter_info *info = user_data; + + if (!monitor) { + error("Unexpected NULL adv_monitor object upon match"); + return; + } + + if (monitor->state != MONITOR_STATE_HONORED) + return; + + if (monitor->type == MONITOR_TYPE_OR_PATTERNS && + bt_ad_pattern_match(info->ad, monitor->patterns)) { + goto matched; + } + + return; + +matched: + if (!info->matched_monitors) + info->matched_monitors = queue_new(); + + queue_push_tail(info->matched_monitors, monitor); +} + +/* Processes the content matching for the monitor(s) of an app */ +static void adv_match_per_app(void *data, void *user_data) +{ + struct adv_monitor_app *app = data; + + if (!app) { + error("Unexpected NULL adv_monitor_app object upon match"); + return; + } + + queue_foreach(app->monitors, adv_match_per_monitor, user_data); +} + +/* Processes the content matching for every app without RSSI filtering and + * notifying monitors. The caller is responsible of releasing the memory of the + * list but not the ad data. + * Returns the list of monitors whose content match the ad data. + */ +struct queue *btd_adv_monitor_content_filter( + struct btd_adv_monitor_manager *manager, + struct bt_ad *ad) +{ + struct adv_content_filter_info info; + + if (!manager || !ad) + return NULL; + + info.ad = ad; + info.matched_monitors = NULL; + + queue_foreach(manager->apps, adv_match_per_app, &info); + + return info.matched_monitors; +} + +/* Wraps adv_monitor_filter_rssi() to processes the content-matched monitor with + * RSSI filtering and notifies it on device found/lost event + */ +static void monitor_filter_rssi(void *data, void *user_data) +{ + struct adv_monitor *monitor = data; + struct adv_rssi_filter_info *info = user_data; + + if (!monitor || !info) + return; + + adv_monitor_filter_rssi(monitor, info->device, info->rssi); +} + +/* Processes every content-matched monitor with RSSI filtering and notifies on + * device found/lost event. The caller is responsible of releasing the memory + * of matched_monitors list but not its data. + */ +void btd_adv_monitor_notify_monitors(struct btd_adv_monitor_manager *manager, + struct btd_device *device, int8_t rssi, + struct queue *matched_monitors) +{ + struct adv_rssi_filter_info info; + + if (!manager || !device || !matched_monitors || + queue_isempty(matched_monitors)) { + return; + } + + info.device = device; + info.rssi = rssi; + + queue_foreach(matched_monitors, monitor_filter_rssi, &info); +} + /* Matches a device based on btd_device object */ static bool monitor_device_match(const void *a, const void *b) { diff --git a/src/adv_monitor.h b/src/adv_monitor.h index 13d5d7282..2b4f68abf 100644 --- a/src/adv_monitor.h +++ b/src/adv_monitor.h @@ -11,16 +11,30 @@ #ifndef __ADV_MONITOR_H #define __ADV_MONITOR_H +#include <glib.h> + +#include "src/shared/ad.h" + struct mgmt; +struct queue; struct btd_device; struct btd_adapter; struct btd_adv_monitor_manager; +struct btd_adv_monitor_pattern; struct btd_adv_monitor_manager *btd_adv_monitor_manager_create( struct btd_adapter *adapter, struct mgmt *mgmt); void btd_adv_monitor_manager_destroy(struct btd_adv_monitor_manager *manager); +struct queue *btd_adv_monitor_content_filter( + struct btd_adv_monitor_manager *manager, + struct bt_ad *ad); + +void btd_adv_monitor_notify_monitors(struct btd_adv_monitor_manager *manager, + struct btd_device *device, int8_t rssi, + struct queue *matched_monitors); + void btd_adv_monitor_device_remove(struct btd_adv_monitor_manager *manager, struct btd_device *device); -- 2.26.2