Re: [BlueZ PATCH v6 2/6] adv_monitor: Implement Adv matching based on stored monitors

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Miao,

On Tue, Oct 6, 2020 at 5:17 PM Miao-chen Chou <mcchou@xxxxxxxxxxxx> wrote:
>
> 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 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     |  35 ++++++--
>  src/adv_monitor.c | 219 ++++++++++++++++++++++++++++++++++++++++------
>  src/adv_monitor.h |  12 +++
>  3 files changed, 234 insertions(+), 32 deletions(-)
>
> diff --git a/src/adapter.c b/src/adapter.c
> index 6d0114a6b..fdd9b3808 100644
> --- a/src/adapter.c
> +++ b/src/adapter.c
> @@ -6601,6 +6601,15 @@ static void update_found_devices(struct btd_adapter *adapter,
>         bool name_known, discoverable;
>         char addr[18];
>         bool duplicate = false;
> +       GSList *matched_monitors;
> +
> +       /* During the background scanning, update the device only when the data
> +        * match at least one Adv monitor
> +        */
> +       matched_monitors = btd_adv_monitor_content_filter(
> +                               adapter->adv_monitor_manager, data, data_len);
> +       if (!adapter->discovering && !matched_monitors)
> +               return;
>
>         memset(&eir_data, 0, sizeof(eir_data));
>         eir_parse(&eir_data, data, data_len);
> @@ -6646,18 +6655,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 +6727,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);
> +               g_slist_free(matched_monitors);
> +               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 31ed30a00..fcb127cd4 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,7 +81,7 @@ enum monitor_state {
>         MONITOR_STATE_HONORED,  /* Accepted by kernel */
>  };
>
> -struct pattern {
> +struct btd_adv_monitor_pattern {
>         uint8_t ad_type;
>         uint8_t offset;
>         uint8_t length;
> @@ -133,6 +130,23 @@ struct app_match_data {
>         const char *path;
>  };
>
> +struct adv_content_filter_info {
> +       uint8_t eir_len;
> +       const uint8_t *eir;
> +
> +       bool matched;                   /* Intermediate state per monitor */
> +       GSList *matched_monitors;       /* List of matched monitors */

Please use struct queue on new code.

> +};
> +
> +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,7 +169,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;
> +       struct btd_adv_monitor_pattern *pattern = data;
>
>         if (!pattern)
>                 return;
> @@ -435,6 +449,36 @@ failed:
>         return false;
>  }
>
> +/* Allocates and initiates a pattern with the given content */
> +static struct btd_adv_monitor_pattern *pattern_create(
> +       uint8_t ad_type, uint8_t offset, uint8_t length, const uint8_t *value)
> +{
> +       struct btd_adv_monitor_pattern *pattern;
> +
> +       if (offset > BT_AD_MAX_DATA_LEN - 1)
> +               return NULL;
> +
> +       if ((ad_type > BT_AD_3D_INFO_DATA &&
> +               ad_type != BT_AD_MANUFACTURER_DATA) ||
> +               ad_type < BT_AD_FLAGS) {
> +               return NULL;
> +       }
> +
> +       if (!value || !length || offset + length > BT_AD_MAX_DATA_LEN)
> +               return NULL;
> +
> +       pattern = new0(struct btd_adv_monitor_pattern, 1);
> +       if (!pattern)
> +               return NULL;
> +
> +       pattern->ad_type = ad_type;
> +       pattern->offset = offset;
> +       pattern->length = length;
> +       memcpy(pattern->value, value, pattern->length);

I wonder why you didn't add pattern matching into bt_ad directly, that
should make it simpler to unit test such feature and enable using in
other tools as well.

> +       return pattern;
> +}
> +
>  /* Retrieves Patterns from the remote Adv Monitor object, verifies the values
>   * and update the local Adv Monitor
>   */
> @@ -464,7 +508,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 btd_adv_monitor_pattern *pattern;
>                 DBusMessageIter struct_iter, value_iter;
>
>                 dbus_message_iter_recurse(&array_iter, &struct_iter);
> @@ -496,28 +540,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 = pattern_create(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 +978,149 @@ void btd_adv_monitor_manager_destroy(struct btd_adv_monitor_manager *manager)
>         manager_destroy(manager);
>  }
>
> +static bool pattern_match(const uint8_t *eir, uint8_t eir_len,
> +                               const struct btd_adv_monitor_pattern *pattern)
> +{
> +       const uint8_t *data;
> +       uint8_t idx = 0;
> +       uint8_t field_len, data_len, data_type;
> +
> +       while (idx < eir_len - 1) {
> +               field_len = eir[0];
> +
> +               /* Check for the end of EIR */
> +               if (field_len == 0)
> +                       break;
> +
> +               idx += field_len + 1;
> +
> +               /* Do not continue filtering if got incorrect length */
> +               if (idx > eir_len)
> +                       break;
> +
> +               data = &eir[2];
> +               data_type = eir[1];
> +               data_len = field_len - 1;
> +
> +               eir += field_len + 1;
> +
> +               if (data_type != pattern->ad_type)
> +                       continue;
> +
> +               if (data_len < pattern->offset + pattern->length)
> +                       continue;
> +
> +               if (!memcmp(data + pattern->offset, pattern->value,
> +                               pattern->length))
> +                       return true;
> +       }

Perhaps something like:

struct bt_ad *bt_ad_new_with_data(uint8_t *data, uint8_t len);

and

bool bt_ad_has_pattern(struct bt_ad *ad, const struct bt_ad_pattern *pattern);

> +       return false;
> +}
> +
> +/* Processes the content matching based on a pattern */
> +static void adv_match_per_pattern(void *data, void *user_data)
> +{
> +       struct btd_adv_monitor_pattern *pattern = data;
> +       struct adv_content_filter_info *info = user_data;
> +
> +       if (!pattern || info->matched)
> +               return;
> +
> +       info->matched = pattern_match(info->eir, info->eir_len, pattern);
> +}
> +
> +/* 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 && monitor->state != MONITOR_STATE_HONORED)
> +               return;
> +
> +       /* Reset the intermediate matched status */
> +       info->matched = false;
> +
> +       if (monitor->type == MONITOR_TYPE_OR_PATTERNS) {
> +               queue_foreach(monitor->patterns, adv_match_per_pattern, info);
> +               if (info->matched)
> +                       goto matched;
> +       }
> +
> +       return;
> +
> +matched:
> +       info->matched_monitors = g_slist_prepend(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)
> +               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 data.
> + * Returns the list of monitors whose content match eir.
> + */
> +GSList *btd_adv_monitor_content_filter(struct btd_adv_monitor_manager *manager,
> +                                       const uint8_t *eir, uint8_t eir_len)
> +{
> +       struct adv_content_filter_info info;
> +
> +       if (!manager || !eir || !eir_len)
> +               return NULL;
> +
> +       info.eir_len = eir_len;
> +       info.eir = eir;
> +       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(gpointer a, gpointer b)
> +{
> +       struct adv_monitor *monitor = a;
> +       struct adv_rssi_filter_info *info = b;
> +
> +       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,
> +                                       GSList *matched_monitors)
> +{
> +       struct adv_rssi_filter_info info;
> +
> +       if (!manager || !device || !matched_monitors)
> +               return;
> +
> +       info.device = device;
> +       info.rssi = rssi;
> +
> +       g_slist_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..e2482e11e 100644
> --- a/src/adv_monitor.h
> +++ b/src/adv_monitor.h
> @@ -11,16 +11,28 @@
>  #ifndef __ADV_MONITOR_H
>  #define __ADV_MONITOR_H
>
> +#include <glib.h>
> +
> +#include "src/shared/ad.h"
> +
>  struct mgmt;
>  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);
>
> +GSList *btd_adv_monitor_content_filter(struct btd_adv_monitor_manager *manager,
> +                                       const uint8_t *eir, uint8_t eir_len);
> +
> +void btd_adv_monitor_notify_monitors(struct btd_adv_monitor_manager *manager,
> +                                       struct btd_device *device, int8_t rssi,
> +                                       GSList *matched_monitors);
> +
>  void btd_adv_monitor_device_remove(struct btd_adv_monitor_manager *manager,
>                                    struct btd_device *device);
>
> --
> 2.26.2
>


-- 
Luiz Augusto von Dentz



[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux