* [BlueZ PATCH v7 2/7] shared/ad: Add support of bt_ad_pattern
2020-10-28 23:05 [BlueZ PATCH v7 1/7] adv_monitor: Implement RSSI Filter logic for background scanning Miao-chen Chou
@ 2020-10-28 23:05 ` Miao-chen Chou
2020-10-28 23:05 ` [BlueZ PATCH v7 3/7] adv_monitor: Implement Adv matching based on stored monitors Miao-chen Chou
` (5 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Miao-chen Chou @ 2020-10-28 23:05 UTC (permalink / raw)
To: Bluetooth Kernel Mailing List
Cc: Alain Michaud, Manish Mandlik, Luiz Augusto von Dentz,
Howard Chung, chromeos-bluetooth-upstreaming, Marcel Holtmann,
Miao-chen Chou, Archie Pusaka, Sonny Sasaka
This adds struct bt_ad_pattern and helpers functions to facilitate
pattern matching.
Reviewed-by: Archie Pusaka <apusaka@chromium.org>
Reviewed-by: Sonny Sasaka <sonnysasaka@chromium.org>
---
(no changes since v1)
src/shared/ad.c | 160 ++++++++++++++++++++++++++++++++++++++++++++++++
src/shared/ad.h | 16 +++++
2 files changed, 176 insertions(+)
diff --git a/src/shared/ad.c b/src/shared/ad.c
index a34d7a147..5eadb4104 100644
--- a/src/shared/ad.c
+++ b/src/shared/ad.c
@@ -31,6 +31,12 @@ struct bt_ad {
struct queue *data;
};
+struct pattern_match_info {
+ struct bt_ad *ad;
+ struct bt_ad_pattern *current_pattern;
+ struct bt_ad_pattern *matched_pattern;
+};
+
struct bt_ad *bt_ad_new(void)
{
struct bt_ad *ad;
@@ -46,6 +52,76 @@ struct bt_ad *bt_ad_new(void)
return bt_ad_ref(ad);
}
+static bool ad_replace_data(struct bt_ad *ad, uint8_t type, void *data,
+ size_t len);
+
+static bool ad_is_type_valid(uint8_t type)
+{
+ if (type > BT_AD_3D_INFO_DATA && type != BT_AD_MANUFACTURER_DATA)
+ return false;
+ if (type < BT_AD_FLAGS)
+ return false;
+
+ return true;
+}
+
+struct bt_ad *bt_ad_new_with_data(size_t len, const uint8_t *data)
+{
+ struct bt_ad *ad;
+ uint16_t parsed_len = 0;
+ uint8_t *data_copy = NULL;
+
+ if (data == NULL || !len)
+ return NULL;
+
+ data_copy = malloc(len);
+ if (!data_copy)
+ return NULL;
+
+ memcpy(data_copy, data, len);
+
+ ad = bt_ad_new();
+ if (!ad)
+ goto cleanup;
+
+ while (parsed_len < len - 1) {
+ uint8_t d_len;
+ uint8_t d_type;
+ uint8_t *d;
+ uint8_t field_len = data_copy[0];
+
+ if (field_len == 0)
+ break;
+
+ parsed_len += field_len + 1;
+
+ if (parsed_len > len)
+ break;
+
+ d = &data_copy[2];
+ d_type = data_copy[1];
+ d_len = field_len - 1;
+
+ if (!ad_is_type_valid(d_type))
+ goto failed;
+
+ if (!ad_replace_data(ad, d_type, d, d_len))
+ goto failed;
+
+ data_copy += field_len + 1;
+ }
+
+ free(data_copy);
+ return ad;
+
+failed:
+ bt_ad_unref(ad);
+
+cleanup:
+ free(data_copy);
+ return NULL;
+}
+
struct bt_ad *bt_ad_ref(struct bt_ad *ad)
{
if (!ad)
@@ -994,3 +1070,87 @@ void bt_ad_clear_data(struct bt_ad *ad)
queue_remove_all(ad->data, NULL, NULL, data_destroy);
}
+
+struct bt_ad_pattern *bt_ad_pattern_new(uint8_t type, size_t offset, size_t len,
+ const uint8_t *data)
+{
+ struct bt_ad_pattern *pattern;
+
+ if (!data || !len || offset >= BT_AD_MAX_DATA_LEN ||
+ len > BT_AD_MAX_DATA_LEN || offset + len > BT_AD_MAX_DATA_LEN) {
+ return NULL;
+ }
+
+ if (!ad_is_type_valid(type))
+ return NULL;
+
+ pattern = new0(struct bt_ad_pattern, 1);
+ if (!pattern)
+ return NULL;
+
+ pattern->len = len;
+ pattern->type = type;
+ pattern->offset = offset;
+ memcpy(pattern->data, data, len);
+
+ return pattern;
+}
+
+static void pattern_ad_data_match(void *data, void *user_data)
+{
+ struct bt_ad_data *ad_data = data;
+ struct pattern_match_info *info = user_data;
+ struct bt_ad_pattern *pattern;
+
+ if (!ad_data || !info)
+ return;
+
+ if (info->matched_pattern)
+ return;
+
+ pattern = info->current_pattern;
+
+ if (!pattern || ad_data->type != pattern->type)
+ return;
+
+ if (ad_data->len < pattern->offset + pattern->len)
+ return;
+
+ if (!memcmp(ad_data->data + pattern->offset, pattern->data,
+ pattern->len)) {
+ info->matched_pattern = pattern;
+ }
+}
+
+static void pattern_match(void *data, void *user_data)
+{
+ struct bt_ad_pattern *pattern = data;
+ struct pattern_match_info *info = user_data;
+
+ if (!pattern || !info)
+ return;
+
+ if (info->matched_pattern)
+ return;
+
+ info->current_pattern = pattern;
+
+ bt_ad_foreach_data(info->ad, pattern_ad_data_match, info);
+}
+
+struct bt_ad_pattern *bt_ad_pattern_match(struct bt_ad *ad,
+ struct queue *patterns)
+{
+ struct pattern_match_info info;
+
+ if (!ad || queue_isempty(patterns))
+ return NULL;
+
+ info.ad = ad;
+ info.matched_pattern = NULL;
+ info.current_pattern = NULL;
+
+ queue_foreach(patterns, pattern_match, &info);
+
+ return info.matched_pattern;
+}
diff --git a/src/shared/ad.h b/src/shared/ad.h
index 83eacab66..13adcb406 100644
--- a/src/shared/ad.h
+++ b/src/shared/ad.h
@@ -68,6 +68,7 @@
typedef void (*bt_ad_func_t)(void *data, void *user_data);
struct bt_ad;
+struct queue;
struct bt_ad_manufacturer_data {
uint16_t manufacturer_id;
@@ -87,8 +88,17 @@ struct bt_ad_data {
size_t len;
};
+struct bt_ad_pattern {
+ uint8_t type;
+ uint8_t offset;
+ uint8_t len;
+ uint8_t data[BT_AD_MAX_DATA_LEN];
+};
+
struct bt_ad *bt_ad_new(void);
+struct bt_ad *bt_ad_new_with_data(size_t len, const uint8_t *data);
+
struct bt_ad *bt_ad_ref(struct bt_ad *ad);
void bt_ad_unref(struct bt_ad *ad);
@@ -156,3 +166,9 @@ void bt_ad_foreach_data(struct bt_ad *ad, bt_ad_func_t func, void *user_data);
bool bt_ad_remove_data(struct bt_ad *ad, uint8_t type);
void bt_ad_clear_data(struct bt_ad *ad);
+
+struct bt_ad_pattern *bt_ad_pattern_new(uint8_t type, size_t offset,
+ size_t len, const uint8_t *data);
+
+struct bt_ad_pattern *bt_ad_pattern_match(struct bt_ad *ad,
+ struct queue *patterns);
--
2.26.2
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [BlueZ PATCH v7 3/7] adv_monitor: Implement Adv matching based on stored monitors
2020-10-28 23:05 [BlueZ PATCH v7 1/7] adv_monitor: Implement RSSI Filter logic for background scanning Miao-chen Chou
2020-10-28 23:05 ` [BlueZ PATCH v7 2/7] shared/ad: Add support of bt_ad_pattern Miao-chen Chou
@ 2020-10-28 23:05 ` Miao-chen Chou
2020-10-29 17:25 ` Luiz Augusto von Dentz
2020-10-28 23:05 ` [BlueZ PATCH v7 4/7] adapter: Clear all Adv monitors upon bring-up Miao-chen Chou
` (4 subsequent siblings)
6 siblings, 1 reply; 10+ messages in thread
From: Miao-chen Chou @ 2020-10-28 23:05 UTC (permalink / raw)
To: Bluetooth Kernel Mailing List
Cc: Alain Michaud, Manish Mandlik, Luiz Augusto von Dentz,
Howard Chung, chromeos-bluetooth-upstreaming, Marcel Holtmann,
Miao-chen Chou, Abhishek Pandit-Subedi
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@chromium.org>
Reviewed-by: Alain Michaud <alainm@chromium.org>
Reviewed-by: Manish Mandlik <mmandlik@chromium.org>
---
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
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [BlueZ PATCH v7 3/7] adv_monitor: Implement Adv matching based on stored monitors
2020-10-28 23:05 ` [BlueZ PATCH v7 3/7] adv_monitor: Implement Adv matching based on stored monitors Miao-chen Chou
@ 2020-10-29 17:25 ` Luiz Augusto von Dentz
2020-10-31 1:04 ` Miao-chen Chou
0 siblings, 1 reply; 10+ messages in thread
From: Luiz Augusto von Dentz @ 2020-10-29 17:25 UTC (permalink / raw)
To: Miao-chen Chou
Cc: Bluetooth Kernel Mailing List, Alain Michaud, Manish Mandlik,
Luiz Augusto von Dentz, Howard Chung,
ChromeOS Bluetooth Upstreaming, Marcel Holtmann,
Abhishek Pandit-Subedi, An, Tedd
Hi Miao,
On Thu, Oct 29, 2020 at 12:54 AM Miao-chen Chou <mcchou@chromium.org> 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@chromium.org>
> Reviewed-by: Alain Michaud <alainm@chromium.org>
> Reviewed-by: Manish Mandlik <mmandlik@chromium.org>
> ---
>
> 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
If I compile patch by patch this one fails:
[detached HEAD 5a221084c] adv_monitor: Implement Adv matching based on
stored monitors
Author: Miao-chen Chou <mcchou@chromium.org>
Date: Wed Oct 28 16:05:30 2020 -0700
3 files changed, 167 insertions(+), 42 deletions(-)
Executing: make -j12
make --no-print-directory all-am
CC src/bluetoothd-adapter.o
CC src/bluetoothd-adv_monitor.o
src/adv_monitor.c:137:13: error: ‘adv_monitor_filter_rssi’ used but
never defined [-Werror]
137 | static void adv_monitor_filter_rssi(struct adv_monitor *monitor,
| ^~~~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
You might want to move these functions to the patch they start use
them so we don't break bisect, you can test them with something like:
git rebase -i origin/master --exec=make
@An, Tedd We should probably update the CI to do something like the
above so it can detect when a set would introduce a patch that doesn't
build.
--
Luiz Augusto von Dentz
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [BlueZ PATCH v7 3/7] adv_monitor: Implement Adv matching based on stored monitors
2020-10-29 17:25 ` Luiz Augusto von Dentz
@ 2020-10-31 1:04 ` Miao-chen Chou
0 siblings, 0 replies; 10+ messages in thread
From: Miao-chen Chou @ 2020-10-31 1:04 UTC (permalink / raw)
To: Luiz Augusto von Dentz
Cc: Bluetooth Kernel Mailing List, Alain Michaud, Manish Mandlik,
Luiz Augusto von Dentz, Howard Chung,
ChromeOS Bluetooth Upstreaming, Marcel Holtmann,
Abhishek Pandit-Subedi, An, Tedd
Hi Luiz,
On Thu, Oct 29, 2020 at 10:25 AM Luiz Augusto von Dentz
<luiz.dentz@gmail.com> wrote:
>
> Hi Miao,
>
> On Thu, Oct 29, 2020 at 12:54 AM Miao-chen Chou <mcchou@chromium.org> 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@chromium.org>
> > Reviewed-by: Alain Michaud <alainm@chromium.org>
> > Reviewed-by: Manish Mandlik <mmandlik@chromium.org>
> > ---
> >
> > 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
>
> If I compile patch by patch this one fails:
>
> [detached HEAD 5a221084c] adv_monitor: Implement Adv matching based on
> stored monitors
> Author: Miao-chen Chou <mcchou@chromium.org>
> Date: Wed Oct 28 16:05:30 2020 -0700
> 3 files changed, 167 insertions(+), 42 deletions(-)
> Executing: make -j12
> make --no-print-directory all-am
> CC src/bluetoothd-adapter.o
> CC src/bluetoothd-adv_monitor.o
> src/adv_monitor.c:137:13: error: ‘adv_monitor_filter_rssi’ used but
> never defined [-Werror]
> 137 | static void adv_monitor_filter_rssi(struct adv_monitor *monitor,
> | ^~~~~~~~~~~~~~~~~~~~~~~
> cc1: all warnings being treated as errors
>
> You might want to move these functions to the patch they start use
> them so we don't break bisect, you can test them with something like:
>
> git rebase -i origin/master --exec=make
>
> @An, Tedd We should probably update the CI to do something like the
> above so it can detect when a set would introduce a patch that doesn't
> build.
>
Since the RSSI filtering patch introduces some functions that will be
used by the following patch, so I merge this commit with the other one
where it is used in v8.
> --
> Luiz Augusto von Dentz
Thanks,
Miao
^ permalink raw reply [flat|nested] 10+ messages in thread
* [BlueZ PATCH v7 4/7] adapter: Clear all Adv monitors upon bring-up
2020-10-28 23:05 [BlueZ PATCH v7 1/7] adv_monitor: Implement RSSI Filter logic for background scanning Miao-chen Chou
2020-10-28 23:05 ` [BlueZ PATCH v7 2/7] shared/ad: Add support of bt_ad_pattern Miao-chen Chou
2020-10-28 23:05 ` [BlueZ PATCH v7 3/7] adv_monitor: Implement Adv matching based on stored monitors Miao-chen Chou
@ 2020-10-28 23:05 ` Miao-chen Chou
2020-10-28 23:05 ` [BlueZ PATCH v7 5/7] adv_monitor: Implement Add Adv Patterns Monitor cmd handler Miao-chen Chou
` (3 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Miao-chen Chou @ 2020-10-28 23:05 UTC (permalink / raw)
To: Bluetooth Kernel Mailing List
Cc: Alain Michaud, Manish Mandlik, Luiz Augusto von Dentz,
Howard Chung, chromeos-bluetooth-upstreaming, Marcel Holtmann,
Miao-chen Chou
This clears all Adv monitors upon daemon bring-up by issuing
MGMT_OP_REMOVE_ADV_MONITOR command with monitor_handle 0.
The following test was performed:
- Add an Adv Monitor using btmgmt, restart bluetoothd and observe the
monitor got removed.
Reviewed-by: Alain Michaud <alainm@chromium.org>
Reviewed-by: Manish Mandlik <mmandlik@chromium.org>
Reviewed-by: Howard Chung <howardchung@google.com>
---
(no changes since v1)
src/adapter.c | 39 +++++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/src/adapter.c b/src/adapter.c
index 0e3fd57f3..0f855d848 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -9509,6 +9509,43 @@ failed:
btd_adapter_unref(adapter);
}
+static void reset_adv_monitors_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_rp_remove_adv_monitor *rp = param;
+
+ if (status != MGMT_STATUS_SUCCESS) {
+ error("Failed to reset Adv Monitors: %s (0x%02x)",
+ mgmt_errstr(status), status);
+ return;
+ }
+
+ if (length < sizeof(*rp)) {
+ error("Wrong size of remove Adv Monitor response for reset "
+ "all Adv Monitors");
+ return;
+ }
+
+ DBG("Removed all Adv Monitors");
+}
+
+static void reset_adv_monitors(uint16_t index)
+{
+ struct mgmt_cp_remove_adv_monitor cp;
+
+ DBG("sending remove Adv Monitor command with handle 0");
+
+ /* Handle 0 indicates to remove all */
+ cp.monitor_handle = 0;
+ if (mgmt_send(mgmt_master, MGMT_OP_REMOVE_ADV_MONITOR, index,
+ sizeof(cp), &cp, reset_adv_monitors_complete, NULL,
+ NULL) > 0) {
+ return;
+ }
+
+ error("Failed to reset Adv Monitors");
+}
+
static void index_added(uint16_t index, uint16_t length, const void *param,
void *user_data)
{
@@ -9523,6 +9560,8 @@ static void index_added(uint16_t index, uint16_t length, const void *param,
return;
}
+ reset_adv_monitors(index);
+
adapter = btd_adapter_new(index);
if (!adapter) {
btd_error(index,
--
2.26.2
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [BlueZ PATCH v7 5/7] adv_monitor: Implement Add Adv Patterns Monitor cmd handler
2020-10-28 23:05 [BlueZ PATCH v7 1/7] adv_monitor: Implement RSSI Filter logic for background scanning Miao-chen Chou
` (2 preceding siblings ...)
2020-10-28 23:05 ` [BlueZ PATCH v7 4/7] adapter: Clear all Adv monitors upon bring-up Miao-chen Chou
@ 2020-10-28 23:05 ` Miao-chen Chou
2020-10-28 23:05 ` [BlueZ PATCH v7 6/7] adv_monitor: Fix return type of RegisterMonitor() method Miao-chen Chou
` (2 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Miao-chen Chou @ 2020-10-28 23:05 UTC (permalink / raw)
To: Bluetooth Kernel Mailing List
Cc: Alain Michaud, Manish Mandlik, Luiz Augusto von Dentz,
Howard Chung, chromeos-bluetooth-upstreaming, Marcel Holtmann,
Miao-chen Chou
From: Howard Chung <howardchung@google.com>
- 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 <alainm@chromium.org>
Reviewed-by: Miao-chen Chou <mcchou@chromium.org>
Reviewed-by: Manish Mandlik <mmandlik@chromium.org>
---
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
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [BlueZ PATCH v7 6/7] adv_monitor: Fix return type of RegisterMonitor() method
2020-10-28 23:05 [BlueZ PATCH v7 1/7] adv_monitor: Implement RSSI Filter logic for background scanning Miao-chen Chou
` (3 preceding siblings ...)
2020-10-28 23:05 ` [BlueZ PATCH v7 5/7] adv_monitor: Implement Add Adv Patterns Monitor cmd handler Miao-chen Chou
@ 2020-10-28 23:05 ` Miao-chen Chou
2020-10-28 23:05 ` [BlueZ PATCH v7 7/7] adv_monitor: Issue Remove Adv Monitor mgmt call Miao-chen Chou
2020-10-28 23:33 ` [BlueZ,v7,1/7] adv_monitor: Implement RSSI Filter logic for background scanning bluez.test.bot
6 siblings, 0 replies; 10+ messages in thread
From: Miao-chen Chou @ 2020-10-28 23:05 UTC (permalink / raw)
To: Bluetooth Kernel Mailing List
Cc: Alain Michaud, Manish Mandlik, Luiz Augusto von Dentz,
Howard Chung, chromeos-bluetooth-upstreaming, Marcel Holtmann,
Miao-chen Chou
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 <howardchung@google.com>
Reviewed-by: Manish Mandlik <mmandlik@chromium.org>
---
(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
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [BlueZ PATCH v7 7/7] adv_monitor: Issue Remove Adv Monitor mgmt call
2020-10-28 23:05 [BlueZ PATCH v7 1/7] adv_monitor: Implement RSSI Filter logic for background scanning Miao-chen Chou
` (4 preceding siblings ...)
2020-10-28 23:05 ` [BlueZ PATCH v7 6/7] adv_monitor: Fix return type of RegisterMonitor() method Miao-chen Chou
@ 2020-10-28 23:05 ` Miao-chen Chou
2020-10-28 23:33 ` [BlueZ,v7,1/7] adv_monitor: Implement RSSI Filter logic for background scanning bluez.test.bot
6 siblings, 0 replies; 10+ messages in thread
From: Miao-chen Chou @ 2020-10-28 23:05 UTC (permalink / raw)
To: Bluetooth Kernel Mailing List
Cc: Alain Michaud, Manish Mandlik, Luiz Augusto von Dentz,
Howard Chung, chromeos-bluetooth-upstreaming, Marcel Holtmann
From: Alain Michaud <alainm@chromium.org>
This calls Remove Adv Monitor command to kernel and handles the callback
during a monitor removal initiated by a D-Bus client. This also
registers callback for getting notified on Adv Monitor Removed event, so
that the Adv monitor manager can invalidate the monitor by calling
Release() on its proxy.
The following tests were performed.
- In bluetoothctl console, add a monitor and remove the monitor by its
index and verify the removal in both the output of btmgmt and syslog.
- In bluetoothctl console, add a monitor, remove the monitor via
btmgmt and verify the removal in syslog.
Reviewed-by: Howard Chung <howardchung@google.com>
Reviewed-by: Alain Michaud <alainm@chromium.org>
---
Changes in v7:
- Rename MONITOR_STATE_HONORED to MONITOR_STATE_ACTIVE
Changes in v4:
- Fix build error
Changes in v3:
- Fix const qualifier of a pointer
src/adv_monitor.c | 132 +++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 124 insertions(+), 8 deletions(-)
diff --git a/src/adv_monitor.c b/src/adv_monitor.c
index dbc3b2a92..c786015c8 100644
--- a/src/adv_monitor.c
+++ b/src/adv_monitor.c
@@ -79,6 +79,7 @@ enum monitor_state {
MONITOR_STATE_FAILED, /* Failed to be init'ed */
MONITOR_STATE_INITED, /* Init'ed but not yet sent to kernel */
MONITOR_STATE_ACTIVE, /* Accepted by kernel */
+ MONITOR_STATE_REMOVING, /* Removing from kernel */
};
struct adv_monitor {
@@ -87,6 +88,7 @@ struct adv_monitor {
char *path;
enum monitor_state state; /* MONITOR_STATE_* */
+ uint16_t monitor_handle; /* Kernel Monitor Handle */
int8_t high_rssi; /* High RSSI threshold */
uint16_t high_rssi_timeout; /* High RSSI threshold timeout */
@@ -567,6 +569,7 @@ static void add_adv_patterns_monitor_cb(uint8_t status, uint16_t length,
return;
}
+ monitor->monitor_handle = le16_to_cpu(rp->monitor_handle);
monitor->state = MONITOR_STATE_ACTIVE;
DBG("Calling Activate() on Adv Monitor of owner %s at path %s",
@@ -575,8 +578,7 @@ static void add_adv_patterns_monitor_cb(uint8_t status, uint16_t length,
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));
+ DBG("Adv monitor with handle:0x%04x added", monitor->monitor_handle);
}
static void monitor_copy_patterns(void *data, void *user_data)
@@ -650,20 +652,77 @@ done:
free(cp);
}
+/* Handles the callback of Remove Adv Monitor command */
+static void remove_adv_monitor_cb(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct adv_monitor *monitor = user_data;
+ const struct mgmt_rp_remove_adv_monitor *rp = param;
+ uint16_t adapter_id = monitor->app->manager->adapter_id;
+
+ if (status != MGMT_STATUS_SUCCESS || !param) {
+ btd_error(adapter_id, "Failed to Remove Adv Monitor with "
+ "status 0x%02x", status);
+ goto done;
+ }
+
+ if (length < sizeof(*rp)) {
+ btd_error(adapter_id, "Wrong size of Remove Adv Monitor "
+ "response");
+ goto done;
+ }
+
+done:
+ queue_remove(monitor->app->monitors, monitor);
+
+ DBG("Adv Monitor removed with handle:0x%04x, path %s",
+ monitor->monitor_handle, monitor->path);
+
+ monitor_free(monitor);
+}
+
+
/* Handles the removal of an Adv Monitor D-Bus proxy */
static void monitor_proxy_removed_cb(GDBusProxy *proxy, void *user_data)
{
struct adv_monitor *monitor;
+ struct mgmt_cp_remove_adv_monitor cp;
struct adv_monitor_app *app = user_data;
+ uint16_t adapter_id = app->manager->adapter_id;
- monitor = queue_remove_if(app->monitors, monitor_match, proxy);
- if (monitor) {
- DBG("Adv Monitor removed for the object at path %s",
- monitor->path);
+ monitor = queue_find(app->monitors, monitor_match, proxy);
- /* The object was gone, so we don't need to call Release() */
- monitor_free(monitor);
+ /* A monitor removed event from kernel can remove a monitor and notify
+ * the app on Release() where this callback can be invoked, so we
+ * simply skip here.
+ */
+ if (!monitor)
+ return;
+
+ if (monitor->state != MONITOR_STATE_ACTIVE)
+ goto done;
+
+ monitor->state = MONITOR_STATE_REMOVING;
+
+ cp.monitor_handle = cpu_to_le16(monitor->monitor_handle);
+
+ if (!mgmt_send(app->manager->mgmt, MGMT_OP_REMOVE_ADV_MONITOR,
+ adapter_id, sizeof(cp), &cp, remove_adv_monitor_cb,
+ monitor, NULL)) {
+ btd_error(adapter_id, "Unable to send Remove Advt Monitor "
+ "command");
+ goto done;
}
+
+ return;
+
+done:
+ queue_remove(app->monitors, monitor);
+
+ DBG("Adv Monitor removed in state %02x with path %s", monitor->state,
+ monitor->path);
+
+ monitor_free(monitor);
}
/* Creates an app object, initiates it and sets D-Bus event handlers */
@@ -872,6 +931,59 @@ static const GDBusPropertyTable adv_monitor_properties[] = {
{ }
};
+/* Matches a monitor based on its handle */
+static bool removed_monitor_match(const void *data, const void *user_data)
+{
+ const uint16_t *handle = user_data;
+ const struct adv_monitor *monitor = data;
+
+ if (!data || !handle)
+ return false;
+
+ return monitor->monitor_handle == *handle;
+}
+
+/* Remove the matched monitor and reports the removal to the app */
+static void app_remove_monitor(void *data, void *user_data)
+{
+ struct adv_monitor_app *app = data;
+ struct adv_monitor *monitor;
+
+ monitor = queue_find(app->monitors, removed_monitor_match, user_data);
+ if (monitor) {
+ if (monitor->state == MONITOR_STATE_ACTIVE)
+ monitor_release(monitor, NULL);
+
+ queue_remove(app->monitors, monitor);
+
+ DBG("Adv Monitor at path %s removed", monitor->path);
+
+ monitor_free(monitor);
+ }
+}
+
+/* Processes Adv Monitor removed event from kernel */
+static void adv_monitor_removed_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct btd_adv_monitor_manager *manager = user_data;
+ const struct mgmt_ev_adv_monitor_removed *ev = param;
+ uint16_t handle = ev->monitor_handle;
+ const uint16_t adapter_id = manager->adapter_id;
+
+ if (length < sizeof(*ev)) {
+ btd_error(adapter_id, "Wrong size of Adv Monitor Removed "
+ "event");
+ return;
+ }
+
+ /* Traverse the apps to find the monitor */
+ queue_foreach(manager->apps, app_remove_monitor, &handle);
+
+ DBG("Adv Monitor removed event with handle 0x%04x processed",
+ ev->monitor_handle);
+}
+
/* Allocates a manager object */
static struct btd_adv_monitor_manager *manager_new(
struct btd_adapter *adapter,
@@ -891,6 +1003,10 @@ static struct btd_adv_monitor_manager *manager_new(
manager->adapter_id = btd_adapter_get_index(adapter);
manager->apps = queue_new();
+ mgmt_register(manager->mgmt, MGMT_EV_ADV_MONITOR_REMOVED,
+ manager->adapter_id, adv_monitor_removed_callback,
+ manager, NULL);
+
return manager;
}
--
2.26.2
^ permalink raw reply related [flat|nested] 10+ messages in thread
* RE: [BlueZ,v7,1/7] adv_monitor: Implement RSSI Filter logic for background scanning
2020-10-28 23:05 [BlueZ PATCH v7 1/7] adv_monitor: Implement RSSI Filter logic for background scanning Miao-chen Chou
` (5 preceding siblings ...)
2020-10-28 23:05 ` [BlueZ PATCH v7 7/7] adv_monitor: Issue Remove Adv Monitor mgmt call Miao-chen Chou
@ 2020-10-28 23:33 ` bluez.test.bot
6 siblings, 0 replies; 10+ messages in thread
From: bluez.test.bot @ 2020-10-28 23:33 UTC (permalink / raw)
To: linux-bluetooth, mcchou
[-- Attachment #1: Type: text/plain, Size: 557 bytes --]
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
^ permalink raw reply [flat|nested] 10+ messages in thread