From patchwork Thu Sep 17 07:10:33 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miao-chen Chou X-Patchwork-Id: 252702 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.8 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id C17B8C43461 for ; Thu, 17 Sep 2020 07:11:02 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 7A57B2075E for ; Thu, 17 Sep 2020 07:11:02 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b="i76Mfhb5" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726245AbgIQHLA (ORCPT ); Thu, 17 Sep 2020 03:11:00 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47138 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726219AbgIQHK5 (ORCPT ); Thu, 17 Sep 2020 03:10:57 -0400 Received: from mail-pl1-x644.google.com (mail-pl1-x644.google.com [IPv6:2607:f8b0:4864:20::644]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4560EC06174A for ; Thu, 17 Sep 2020 00:10:57 -0700 (PDT) Received: by mail-pl1-x644.google.com with SMTP id r19so652542pls.1 for ; Thu, 17 Sep 2020 00:10:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=cKsMX997q72m3h0dSrTTor4DQOmqWhuxr0fHnTUpCZQ=; b=i76Mfhb5xT9kWmgWnwZ6moBMH26ND+pEfD29YXbxGtEyRFll0Ha77yt0qTsjPOZVDc CJ0f4C6TXqkhwM+mFqCXdPZ6KeZbI1QPrWozlho2+9PsIk3iL3/q664F18Y7i/w54Op6 QExsVYTfde9Ad5RIt0VhhcoPdyKDjOJ9oqmaM= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=cKsMX997q72m3h0dSrTTor4DQOmqWhuxr0fHnTUpCZQ=; b=QVJbsw9EYl0Emp35H0xVd0MF49kStTeObmR7p4UDvUFhvSqRTDXfgdEQQDFqm34CQx U1D3RJk6U4C48Umre89wR3pIqELoeS9JA/+5v4/ZmKIn2dC9Ke4KiodbhkNngH3sqiwT /jZmxDzcEiSN8Ao8XNilZu2mxE2rr9MAyh4Gk6Phz3zhtynS+mb+7wRgaUhezBFvMwbz TcTJgXeXjzIsWcAKFxViGe571L4sqWtxWwvfCxpWWmfs85Hol7M14ovycAU8/essgAcf QLodfliUI5rkw3N81J8mF8HUHQPFa23wWhp3/zvzk6QwfbEhVdpPV0eaABoHZzJ48hEJ Xtbg== X-Gm-Message-State: AOAM532g2kj+tJYIZcEc26IAQ+q85ZKEgEi0KCArR7z4NEODUDX3TGJ1 qF8eYITBEsK7gi8257z1wMDvu1WSEUIfYQ== X-Google-Smtp-Source: ABdhPJx0THzqeCEa8p7GQMT6xPGYHUHL+IpIuzlMsYdrzRwm0khOaqJn9+r5zYNuSTKw/jqcqvg/CA== X-Received: by 2002:a17:90a:de92:: with SMTP id n18mr7145201pjv.35.1600326656180; Thu, 17 Sep 2020 00:10:56 -0700 (PDT) Received: from mcchou0.mtv.corp.google.com ([2620:15c:202:201:de4a:3eff:fe75:1314]) by smtp.gmail.com with ESMTPSA id j26sm933478pgl.91.2020.09.17.00.10.55 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 17 Sep 2020 00:10:55 -0700 (PDT) From: Miao-chen Chou To: Bluetooth Kernel Mailing List Cc: Marcel Holtmann , Alain Michaud , Howard Chung , chromeos-bluetooth-upstreaming@chromium.org, Luiz Augusto von Dentz , Manish Mandlik , Manish Mandlik , Miao-chen Chou Subject: [BlueZ PATCH v4 2/8] adv_monitor: Implement unit tests for RSSI Filter Date: Thu, 17 Sep 2020 00:10:33 -0700 Message-Id: <20200917001020.BlueZ.v4.2.I5ae05701b2b792a3ea2ca98f4a5d977645b1afc2@changeid> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200917001020.BlueZ.v4.1.I2830b9c1212a64b062201ed9f2b71294f50ad22d@changeid> References: <20200917001020.BlueZ.v4.1.I2830b9c1212a64b062201ed9f2b71294f50ad22d@changeid> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Manish Mandlik This patch implements unit tests for the background scanning RSSI Filtering logic. Verified all tests PASS by running tests in unit/test-adv-monitor.c Reviewed-by: Alain Michaud Reviewed-by: Miao-chen Chou --- Changes in v4: - Fix commit message Changes in v3: - Fix commit message Changes in v2: - Cast test data to void * Makefile.am | 9 + doc/test-coverage.txt | 3 +- src/adv_monitor.c | 79 ++++++++ src/adv_monitor.h | 10 + unit/test-adv-monitor.c | 391 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 491 insertions(+), 1 deletion(-) create mode 100644 unit/test-adv-monitor.c diff --git a/Makefile.am b/Makefile.am index 22b4fa30c..6918f02b0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -527,6 +527,15 @@ unit_test_gattrib_LDADD = lib/libbluetooth-internal.la \ src/libshared-glib.la \ $(GLIB_LIBS) $(DBUS_LIBS) -ldl -lrt +unit_tests += unit/test-adv-monitor + +unit_test_adv_monitor_SOURCES = unit/test-adv-monitor.c \ + src/adv_monitor.h src/adv_monitor.c \ + src/device.h src/device.c \ + src/log.h src/log.c +unit_test_adv_monitor_LDADD = gdbus/libgdbus-internal.la \ + src/libshared-glib.la $(GLIB_LIBS) $(DBUS_LIBS) + if MIDI unit_tests += unit/test-midi unit_test_midi_CPPFLAGS = $(AM_CPPFLAGS) $(ALSA_CFLAGS) -DMIDI_TEST diff --git a/doc/test-coverage.txt b/doc/test-coverage.txt index 741492a3e..5296983e6 100644 --- a/doc/test-coverage.txt +++ b/doc/test-coverage.txt @@ -30,8 +30,9 @@ test-gobex-transfer 36 OBEX transfer handling test-gdbus-client 13 D-Bus client handling test-gatt 180 GATT qualification test cases test-hog 6 HID Over GATT qualification test cases +test-adv-monitor 5 Advertisement Monitor test cases ----- - 761 + 766 Automated end-to-end testing diff --git a/src/adv_monitor.c b/src/adv_monitor.c index 7baa5317f..046f5953f 100644 --- a/src/adv_monitor.c +++ b/src/adv_monitor.c @@ -1210,3 +1210,82 @@ static void adv_monitor_filter_rssi(struct adv_monitor *monitor, handle_device_lost_timeout, dev); } } + +/* Creates the dummy adv_monitor object for unit tests */ +void *btd_adv_monitor_rssi_test_setup(int8_t high_rssi, uint16_t high_timeout, + int8_t low_rssi, uint16_t low_timeout) +{ + struct adv_monitor *test_monitor = NULL; + + test_monitor = g_new0(struct adv_monitor, 1); + if (!test_monitor) + return NULL; + + test_monitor->app = g_new0(struct adv_monitor_app, 1); + if (!test_monitor->app) + goto app_failed; + + test_monitor->app->manager = g_new0(struct btd_adv_monitor_manager, 1); + if (!test_monitor->app->manager) + goto manager_failed; + + test_monitor->high_rssi = high_rssi; + test_monitor->high_rssi_timeout = high_timeout; + test_monitor->low_rssi = low_rssi; + test_monitor->low_rssi_timeout = low_timeout; + test_monitor->devices = queue_new(); + + return test_monitor; + +manager_failed: + g_free(test_monitor->app); + +app_failed: + g_free(test_monitor); + + return NULL; +} + +/* Cleanup after unit test is done */ +void btd_adv_monitor_rssi_test_teardown(void *monitor_obj) +{ + struct adv_monitor *monitor = monitor_obj; + + if (!monitor) + return; + + queue_destroy(monitor->devices, monitor_device_free); + g_free(monitor); +} + +/* Returns the current state of device - found/lost, used in unit tests */ +bool btd_adv_monitor_test_device_state(void *monitor_obj, void *device_obj) +{ + struct adv_monitor *monitor = monitor_obj; + struct btd_device *device = device_obj; + struct adv_monitor_device *dev = NULL; + + if (!monitor || !device) + return false; + + dev = queue_find(monitor->devices, monitor_device_match, device); + if (!dev) + return false; + + return dev->device_found; +} + +/* Helper function for the RSSI Filter unit tests */ +bool btd_adv_monitor_test_rssi(void *monitor_obj, void *device_obj, + int8_t adv_rssi) +{ + struct adv_monitor *monitor = monitor_obj; + struct btd_device *device = device_obj; + + if (!monitor || !device) + return false; + + adv_monitor_filter_rssi(monitor, device, adv_rssi); + + return btd_adv_monitor_test_device_state(monitor, device); +} diff --git a/src/adv_monitor.h b/src/adv_monitor.h index 14508e7d1..351e7f9aa 100644 --- a/src/adv_monitor.h +++ b/src/adv_monitor.h @@ -33,4 +33,14 @@ 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); +/* Following functions are the helper functions used for RSSI Filter unit tests + * defined in unit/test-adv-monitor.c + */ +void *btd_adv_monitor_rssi_test_setup(int8_t high_rssi, uint16_t high_timeout, + int8_t low_rssi, uint16_t low_timeout); +void btd_adv_monitor_rssi_test_teardown(void *monitor_obj); +bool btd_adv_monitor_test_device_state(void *monitor_obj, void *device_obj); +bool btd_adv_monitor_test_rssi(void *monitor_obj, void *device_obj, + int8_t adv_rssi); + #endif /* __ADV_MONITOR_H */ diff --git a/unit/test-adv-monitor.c b/unit/test-adv-monitor.c new file mode 100644 index 000000000..970be84b0 --- /dev/null +++ b/unit/test-adv-monitor.c @@ -0,0 +1,391 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2020 Google LLC + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#define _GNU_SOURCE +#include +#include +#include +#include + +#include "src/log.h" +#include "src/shared/tester.h" + +#include "src/adv_monitor.h" + +#define define_test(name, type, data, setup_fn, test_fn, teardown_fn) \ + do { \ + static struct test_data test; \ + test.test_type = type; \ + test.test_name = g_strdup(name); \ + if (type == TEST_RSSI_FILTER) { \ + test.rssi_filter_test_data = (void *)&data; \ + test.rssi_filter_test_data->test_info = &test; \ + } \ + tester_add(name, &test, setup_fn, test_fn, teardown_fn);\ + } while (0) + +#define ADV_INTERVAL 1 /* Advertisement interval in seconds */ +#define OUT_OF_RANGE -128 +#define END_OF_RSSI_TEST {0} + +#define RSSI_TEST_DONE(test_step) \ + (!test_step.adv_rssi && !test_step.duration && !test_step.result) + +#define DUMMY_BTD_DEVICE_OBJ ((void *) 0xF00) + +enum test_type { + TEST_RSSI_FILTER = 0, + TEST_CONTENT_FILTER, +}; + +enum result { + RESULT_DEVICE_NOT_FOUND = false, /* Initial state of a device */ + RESULT_DEVICE_FOUND = true, /* Device state when the + * Content/RSSI Filter match + */ + RESULT_DEVICE_LOST = false, /* Device state when the Low + * RSSI Filter match or if it + * goes offline/out-of-range + */ +}; + +struct rssi_filter_test { + void *adv_monitor_obj; /* struct adv_monitor object */ + void *btd_device_obj; /* struct btd_device object */ + struct test_data *test_info; + + const struct { + int8_t high_rssi_threshold; /* High RSSI threshold */ + uint16_t high_rssi_timeout; /* High RSSI threshold timeout*/ + int8_t low_rssi_threshold; /* Low RSSI threshold */ + uint16_t low_rssi_timeout; /* Low RSSI threshold timeout */ + } rssi_filter; + + time_t start_time; /* Start time of the test */ + uint16_t resume_step; /* Store the current sub-step of the + * test before suspending that test + */ + guint out_of_range_timer; /* Timer to simulate device offline */ + + const struct { + int8_t adv_rssi; /* Advertisement RSSI */ + uint16_t duration; /* Advertisement duration in seconds */ + enum result result; /* Device state after every step */ + } test_steps[]; +}; + +/* Parent data structure to hold the test data and information, + * used by tester_* functions and callbacks. + */ +struct test_data { + enum test_type test_type; + char *test_name; + + union { + struct rssi_filter_test *rssi_filter_test_data; + }; +}; + +/* RSSI Filter Test 1: + * - The Device Lost event should NOT get triggered even if the Adv RSSI is + * lower than LowRSSIThresh for more than LowRSSITimeout before finding + * the device first. + * - Similarly, the Device Found event should NOT get triggered if the Adv RSSI + * is greater than LowRSSIThresh but lower than HighRSSIThresh. + */ +static struct rssi_filter_test rssi_data_1 = { + .rssi_filter = {-40, 5, -60, 5}, + .test_steps = { + {-70, 6, RESULT_DEVICE_NOT_FOUND}, + {-50, 6, RESULT_DEVICE_NOT_FOUND}, + END_OF_RSSI_TEST, + }, +}; + +/* RSSI Filter Test 2: + * - The Device Found event should get triggered when the Adv RSSI is higher + * than HighRSSIThresh for more than HighRSSITimeout. + * - Once the device is found, the Device Lost event should NOT get triggered + * if the Adv RSSI drops below HighRSSIThresh but it is not lower than + * LowRSSIThresh. + * - When the Adv RSSI drops below LowRSSIThresh for more than LowRSSITimeout, + * the Device Lost event should get triggered. + */ +static struct rssi_filter_test rssi_data_2 = { + .rssi_filter = {-40, 5, -60, 5}, + .test_steps = { + {-30, 6, RESULT_DEVICE_FOUND}, + {-50, 6, RESULT_DEVICE_FOUND}, + {-70, 6, RESULT_DEVICE_LOST}, + END_OF_RSSI_TEST, + }, +}; + +/* RSSI Filter Test 3: + * - The Device Found event should get triggered only when the Adv RSSI is + * higher than HighRSSIThresh for more than HighRSSITimeout. + * - If the Adv RSSI drops below HighRSSIThresh, timer should reset and start + * counting once the Adv RSSI is above HighRSSIThresh. + * - Similarly, when tracking the Low RSSI, timer should reset when the Adv RSSI + * goes above LowRSSIThresh. The Device Lost event should get triggered only + * when the Adv RSSI is lower than LowRSSIThresh for more than LowRSSITimeout. + */ +static struct rssi_filter_test rssi_data_3 = { + .rssi_filter = {-40, 5, -60, 5}, + .test_steps = { + {-30, 2, RESULT_DEVICE_NOT_FOUND}, + {-50, 6, RESULT_DEVICE_NOT_FOUND}, + {-30, 4, RESULT_DEVICE_NOT_FOUND}, + {-30, 2, RESULT_DEVICE_FOUND}, + {-70, 2, RESULT_DEVICE_FOUND}, + {-50, 6, RESULT_DEVICE_FOUND}, + {-70, 4, RESULT_DEVICE_FOUND}, + {-70, 2, RESULT_DEVICE_LOST}, + END_OF_RSSI_TEST, + }, +}; + +/* RSSI Filter Test 4: + * - While tracking the High RSSI, timer should reset if the device goes + * offline/out-of-range for more than HighRSSITimeout. + * - Once the device is found, if the device goes offline/out-of-range for + * more than LowRSSITimeout, the Device Lost event should get triggered. + */ +static struct rssi_filter_test rssi_data_4 = { + .rssi_filter = {-40, 5, -60, 5}, + .test_steps = { + { -30, 2, RESULT_DEVICE_NOT_FOUND}, + {OUT_OF_RANGE, 6, RESULT_DEVICE_NOT_FOUND}, + { -30, 4, RESULT_DEVICE_NOT_FOUND}, + { -30, 2, RESULT_DEVICE_FOUND}, + { -70, 2, RESULT_DEVICE_FOUND}, + {OUT_OF_RANGE, 6, RESULT_DEVICE_LOST}, + END_OF_RSSI_TEST, + }, +}; + +/* RSSI Filter Test 5: + * - The Device Found event should get triggered only once even if the Adv RSSI + * stays higher than HighRSSIThresh for a longer period of time. + * - Once the device is found, while tracking the Low RSSI, timer should reset + * when the Adv RSSI goes above LowRSSIThresh. + * - The timer should NOT reset if the device goes offline/out-of-range for + * a very short period of time and comes back online/in-range before + * the timeouts. + */ +static struct rssi_filter_test rssi_data_5 = { + .rssi_filter = {-40, 5, -60, 5}, + .test_steps = { + { -30, 2, RESULT_DEVICE_NOT_FOUND}, + {OUT_OF_RANGE, 2, RESULT_DEVICE_NOT_FOUND}, + { -30, 2, RESULT_DEVICE_FOUND}, + { -30, 3, RESULT_DEVICE_FOUND}, + { -30, 3, RESULT_DEVICE_FOUND}, + { -70, 2, RESULT_DEVICE_FOUND}, + {OUT_OF_RANGE, 2, RESULT_DEVICE_FOUND}, + { -50, 6, RESULT_DEVICE_FOUND}, + { -70, 2, RESULT_DEVICE_FOUND}, + {OUT_OF_RANGE, 2, RESULT_DEVICE_FOUND}, + { -70, 2, RESULT_DEVICE_LOST}, + END_OF_RSSI_TEST, + }, +}; + +/* Initialize the data required for RSSI Filter test */ +static void setup_rssi_filter_test(gpointer data) +{ + struct rssi_filter_test *test = data; + + test->adv_monitor_obj = btd_adv_monitor_rssi_test_setup( + test->rssi_filter.high_rssi_threshold, + test->rssi_filter.high_rssi_timeout, + test->rssi_filter.low_rssi_threshold, + test->rssi_filter.low_rssi_timeout); + + /* The RSSI Filter logic uses btd_device object only as a key in the + * adv_monitor->devices list, it is never dereferenced nor used to + * perform any operations related to btd_device. So we can use any + * dummy address for unit testing. + */ + test->btd_device_obj = DUMMY_BTD_DEVICE_OBJ; + + tester_setup_complete(); +} + +/* Cleanup after the RSSI Filter test is done */ +static void teardown_rssi_filter_test(gpointer data) +{ + struct rssi_filter_test *test = data; + + btd_adv_monitor_rssi_test_teardown(test->adv_monitor_obj); + + tester_teardown_complete(); +} + +/* Execute the sub-steps of RSSI Filter test */ +static gboolean test_rssi_filter(gpointer data) +{ + struct rssi_filter_test *test = data; + time_t start_time = time(NULL); + bool ret = false; + + uint16_t i = 0; + uint16_t j = 0; + + /* If this is not the beginning of test, return to the sub-step + * before that test was suspended + */ + if (test->resume_step) { + start_time = test->start_time; + i = test->resume_step; + + /* Clear the test resume timer */ + g_source_remove(test->out_of_range_timer); + test->out_of_range_timer = 0; + + /* Check state of the device - found/lost, while device was + * offline/out-of-range + */ + ret = btd_adv_monitor_test_device_state(test->adv_monitor_obj, + test->btd_device_obj); + tester_debug("%s: [t=%.0lf, step=%d] Test resume, " + "device_found = %s", + test->test_info->test_name, + difftime(time(NULL), start_time), i, + ret ? "true" : "false"); + g_assert(ret == test->test_steps[i].result); + + i++; + } + + while (!RSSI_TEST_DONE(test->test_steps[i])) { + if (test->test_steps[i].adv_rssi == OUT_OF_RANGE) { + /* Simulate device offline/out-of-range by suspending + * the test. + * + * Note: All tester_* functions run sequentially by + * adding a next function to the main loop using + * g_idle_add(). If a timeout function is added using + * g_timeout_add_*(), it doesn't really get invoked as + * soon as the timer expires. Instead, it is invoked + * once the current function returns and the timer has + * expired. So, to give handle_device_lost_timeout() + * function a chance to run at the correct time, we + * must save the current state and exit from this + * function while we simulate the device offline. We can + * come back later to continue with the remaining steps. + */ + test->resume_step = i; + test->start_time = start_time; + test->out_of_range_timer = g_timeout_add_seconds( + test->test_steps[i].duration, + test_rssi_filter, data); + + /* Check the device state before suspending the test */ + ret = btd_adv_monitor_test_device_state( + test->adv_monitor_obj, + test->btd_device_obj); + tester_debug("%s: [t=%.0lf, step=%d] Test suspend, " + "device_found = %s", + test->test_info->test_name, + difftime(time(NULL), start_time), i, + ret ? "true" : "false"); + return FALSE; + } + + for (j = 0; j < test->test_steps[i].duration; j++) { + ret = btd_adv_monitor_test_rssi( + test->adv_monitor_obj, + test->btd_device_obj, + test->test_steps[i].adv_rssi); + tester_debug("%s: [t=%.0lf, step=%d] Test " + "advertisement RSSI %d, device_found = %s", + test->test_info->test_name, + difftime(time(NULL), start_time), i, + test->test_steps[i].adv_rssi, + ret ? "true" : "false"); + + /* Sleep for a second to simulate receiving + * advertisement once every second + */ + sleep(ADV_INTERVAL); + } + g_assert(ret == test->test_steps[i].result); + + i++; + } + + tester_debug("%s: [t=%.0lf] Test done", test->test_info->test_name, + difftime(time(NULL), start_time)); + + tester_test_passed(); + + return FALSE; +} + +/* Handler function to prepare for a test */ +static void setup_handler(gconstpointer data) +{ + const struct test_data *test = data; + + if (test->test_type == TEST_RSSI_FILTER) + setup_rssi_filter_test(test->rssi_filter_test_data); +} + +/* Handler function to cleanup after the test is done */ +static void teardown_handler(gconstpointer data) +{ + const struct test_data *test = data; + + if (test->test_type == TEST_RSSI_FILTER) + teardown_rssi_filter_test(test->rssi_filter_test_data); +} + +/* Handler function to execute a test with the given data set */ +static void test_handler(gconstpointer data) +{ + const struct test_data *test = data; + + if (test->test_type == TEST_RSSI_FILTER) + test_rssi_filter(test->rssi_filter_test_data); +} + +int main(int argc, char *argv[]) +{ + tester_init(&argc, &argv); + + __btd_log_init("*", 0); + + define_test("/advmon/rssi/1", TEST_RSSI_FILTER, rssi_data_1, + setup_handler, test_handler, teardown_handler); + define_test("/advmon/rssi/2", TEST_RSSI_FILTER, rssi_data_2, + setup_handler, test_handler, teardown_handler); + define_test("/advmon/rssi/3", TEST_RSSI_FILTER, rssi_data_3, + setup_handler, test_handler, teardown_handler); + define_test("/advmon/rssi/4", TEST_RSSI_FILTER, rssi_data_4, + setup_handler, test_handler, teardown_handler); + define_test("/advmon/rssi/5", TEST_RSSI_FILTER, rssi_data_5, + setup_handler, test_handler, teardown_handler); + + return tester_run(); +} From patchwork Thu Sep 17 07:10:37 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miao-chen Chou X-Patchwork-Id: 252701 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.8 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 217B2C43461 for ; Thu, 17 Sep 2020 07:11:26 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id DD9232083B for ; Thu, 17 Sep 2020 07:11:25 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b="H4deZI11" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726196AbgIQHLZ (ORCPT ); Thu, 17 Sep 2020 03:11:25 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47204 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726306AbgIQHLW (ORCPT ); Thu, 17 Sep 2020 03:11:22 -0400 Received: from mail-pl1-x631.google.com (mail-pl1-x631.google.com [IPv6:2607:f8b0:4864:20::631]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3D160C06174A for ; Thu, 17 Sep 2020 00:11:21 -0700 (PDT) Received: by mail-pl1-x631.google.com with SMTP id u9so644529plk.4 for ; Thu, 17 Sep 2020 00:11:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=UgExmGJD0WUGNTW5ZOzNd/c9xQ0pED0xft63St+aD78=; b=H4deZI11SuubrwPDhXp0JP35c2fGNgTQ8J2YBbJlWPmLpNBavhgVaYZJYd4qs6i9e2 MiaiTEM5MegjK9/am9k2/Rs1O5hpr0Bzv9kabYTmlxKi7NPIZI+2cNFVjF6xkgKwUyVY o1/8Nh5icpWP3Qq58LCEHoLnRCfjA2knVzQyM= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=UgExmGJD0WUGNTW5ZOzNd/c9xQ0pED0xft63St+aD78=; b=FGtcM78ka11LjLa1WMYZ0U/SGXdfLl1fj/ESfXw7ECrfgsZN2BUZMTGGY+OMNFvJiy sGKJ7E2dXC2dw5ko8HeCWnPVLf/JHjQRky6rOO3tQT8JPl4laF1ESHJ5p2szC95AFyWu +3y0ItX+ACelmR3fBMQNKs0FVYHQnuGhquajg91Hw9KgraW+tQBIGFtOeH2dYWru8K4Y 1Q1FJ8Fkx6aYfCFdy3UREu5pgx8WhUdMRr5RcidhkEMhJX29z+1qDBMu4KP009ABPRLd RZlaKLEU+hB0EgVwjD2sbrV58vjRv2GXVRAaVRk5IExrbcLEwghqH66CJLH+I1UBt7HD FSzg== X-Gm-Message-State: AOAM533Hp+cJNHzziw0HAsmRR4fPWrGFblNvhOMDGNEuE5Hdj/yUt4OV KHD3vBbgIM+Chc78yrEs7d3s+17+pLzdjA== X-Google-Smtp-Source: ABdhPJzOb98UE0Mm94MyqND52bFFemPfGRpAbe+aHAzinIOYpE9ek65jKdtJawQ/EjaEvim5GzA0Pw== X-Received: by 2002:a17:90a:ea08:: with SMTP id w8mr7438751pjy.124.1600326680393; Thu, 17 Sep 2020 00:11:20 -0700 (PDT) Received: from mcchou0.mtv.corp.google.com ([2620:15c:202:201:de4a:3eff:fe75:1314]) by smtp.gmail.com with ESMTPSA id j26sm933478pgl.91.2020.09.17.00.11.19 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 17 Sep 2020 00:11:19 -0700 (PDT) From: Miao-chen Chou To: Bluetooth Kernel Mailing List Cc: Marcel Holtmann , Alain Michaud , Howard Chung , chromeos-bluetooth-upstreaming@chromium.org, Luiz Augusto von Dentz , Manish Mandlik , Miao-chen Chou , Abhishek Pandit-Subedi Subject: [BlueZ PATCH v4 4/8] adv_monitor: Implement unit tests for content filter Date: Thu, 17 Sep 2020 00:10:37 -0700 Message-Id: <20200917001020.BlueZ.v4.4.I7f88b6e4c63c14d77974438eb2f07326aedbfd9b@changeid> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200917001020.BlueZ.v4.1.I2830b9c1212a64b062201ed9f2b71294f50ad22d@changeid> References: <20200917001020.BlueZ.v4.1.I2830b9c1212a64b062201ed9f2b71294f50ad22d@changeid> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org This implements the unit tests for verifying the correctness of advertisement data fields matching against a pattern. Reviewed-by: Abhishek Pandit-Subedi Reviewed-by: Alain Michaud Reviewed-by: Manish Mandlik --- (no changes since v3) Changes in v3: - Fix mixed declarations and assignments Changes in v2: - Cast test data to void * doc/test-coverage.txt | 4 +- unit/test-adv-monitor.c | 144 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+), 2 deletions(-) diff --git a/doc/test-coverage.txt b/doc/test-coverage.txt index 5296983e6..e15474a44 100644 --- a/doc/test-coverage.txt +++ b/doc/test-coverage.txt @@ -30,9 +30,9 @@ test-gobex-transfer 36 OBEX transfer handling test-gdbus-client 13 D-Bus client handling test-gatt 180 GATT qualification test cases test-hog 6 HID Over GATT qualification test cases -test-adv-monitor 5 Advertisement Monitor test cases +test-adv-monitor 9 Advertisement Monitor test cases ----- - 766 + 770 Automated end-to-end testing diff --git a/unit/test-adv-monitor.c b/unit/test-adv-monitor.c index 970be84b0..a19a3092b 100644 --- a/unit/test-adv-monitor.c +++ b/unit/test-adv-monitor.c @@ -40,6 +40,8 @@ if (type == TEST_RSSI_FILTER) { \ test.rssi_filter_test_data = (void *)&data; \ test.rssi_filter_test_data->test_info = &test; \ + } else if (type == TEST_CONTENT_FILTER) { \ + test.content_filter_test_data = (void *)&data; \ } \ tester_add(name, &test, setup_fn, test_fn, teardown_fn);\ } while (0) @@ -94,6 +96,22 @@ struct rssi_filter_test { } test_steps[]; }; +struct content_filter_test { + void *advmon_pattern; /* btd_adv_monitor_pattern */ + + bool expected_match; + + const struct { + uint8_t ad_type; + uint8_t offset; + uint8_t length; + uint8_t value[BT_AD_MAX_DATA_LEN]; + } pattern; + + uint8_t eir_len; + uint8_t eir[]; +}; + /* Parent data structure to hold the test data and information, * used by tester_* functions and callbacks. */ @@ -103,6 +121,7 @@ struct test_data { union { struct rssi_filter_test *rssi_filter_test_data; + struct content_filter_test *content_filter_test_data; }; }; @@ -211,6 +230,62 @@ static struct rssi_filter_test rssi_data_5 = { }, }; +/* Content Filter Test 1: + * The valid EIR data contains the given pattern whose content is a UUID16 AD + * data. + */ +static struct content_filter_test content_data_1 = { + .expected_match = true, + .pattern = {0x03, 0x02, 0x02, {0x09, 0x18} }, + .eir_len = 20, + .eir = {0x02, 0x01, 0x02, // flags + 0x06, 0xff, 0x96, 0xfd, 0xab, 0xcd, 0xef, // Mfr. Data + 0x05, 0x03, 0x0d, 0x18, 0x09, 0x18, // 16-bit UUIDs + 0x05, 0x16, 0x0d, 0x18, 0x12, 0x34}, // Service Data +}; + +/* Content Filter Test 2: + * The valid EIR data does not match the given pattern whose content is a UUID16 + * AD data. + */ +static struct content_filter_test content_data_2 = { + .expected_match = false, + .pattern = {0x03, 0x02, 0x02, {0x0d, 0x18} }, + .eir_len = 20, + .eir = {0x02, 0x01, 0x02, // flags + 0x06, 0xff, 0x96, 0xfd, 0xab, 0xcd, 0xef, // Mfr. Data + 0x05, 0x03, 0x0d, 0x18, 0x09, 0x18, // 16-bit UUIDs + 0x05, 0x16, 0x0d, 0x18, 0x12, 0x34}, // Service Data +}; + +/* Content Filter Test 3: + * The valid EIR data does not have the given pattern whose content is a UUID32 + * AD data. + */ +static struct content_filter_test content_data_3 = { + .expected_match = false, + .pattern = {0x05, 0x00, 0x04, {0x09, 0x18, 0x00, 0x00} }, + .eir_len = 20, + .eir = {0x02, 0x01, 0x02, // flags + 0x06, 0xff, 0x96, 0xfd, 0xab, 0xcd, 0xef, // Mfr. Data + 0x05, 0x03, 0x0d, 0x18, 0x09, 0x18, // 16-bit UUIDs + 0x05, 0x16, 0x0d, 0x18, 0x12, 0x34}, // Service Data +}; + +/* Content Filter Test 4: + * The valid EIR data does not match the given pattern whose content is a + * UUID16 AD data due to invalid starting position of matching. + */ +static struct content_filter_test content_data_4 = { + .expected_match = false, + .pattern = {0x03, 0x02, 0x02, {0x09, 0x18} }, + .eir_len = 20, + .eir = {0x02, 0x01, 0x02, // flags + 0x06, 0xff, 0x96, 0xfd, 0xab, 0xcd, 0xef, // Mfr. Data + 0x03, 0x03, 0x09, 0x18, // 16-bit UUIDs + 0x05, 0x16, 0x0d, 0x18, 0x12, 0x34}, // Service Data +}; + /* Initialize the data required for RSSI Filter test */ static void setup_rssi_filter_test(gpointer data) { @@ -343,6 +418,60 @@ static gboolean test_rssi_filter(gpointer data) return FALSE; } +/* Initialize the data required for Content Filter test */ +static void setup_content_filter_test(gpointer data) +{ + struct content_filter_test *test = data; + struct btd_adv_monitor_pattern *pattern = NULL; + + pattern = btd_adv_monitor_test_pattern_create(test->pattern.ad_type, + test->pattern.offset, + test->pattern.length, + test->pattern.value); + if (!pattern) { + tester_setup_failed(); + return; + } + + test->advmon_pattern = pattern; + tester_setup_complete(); +} + +/* Cleanup after the Content Filter test is done */ +static void teardown_content_filter_test(gpointer data) +{ + struct content_filter_test *test = data; + + if (!test) + tester_teardown_complete(); + + btd_adv_monitor_test_pattern_destroy(test->advmon_pattern); + test->advmon_pattern = NULL; + + tester_teardown_complete(); +} + +/* Execute the sub-steps of Content Filter test */ +static void test_content_filter(gpointer data) +{ + struct content_filter_test *test = data; + struct btd_adv_monitor_pattern *pattern = test->advmon_pattern; + + if (!pattern) { + tester_test_abort(); + return; + } + + if (btd_adv_monitor_pattern_match(test->eir, test->eir_len, + test->advmon_pattern) == + test->expected_match) { + tester_test_passed(); + return; + } + + tester_test_failed(); +} + /* Handler function to prepare for a test */ static void setup_handler(gconstpointer data) { @@ -350,6 +479,8 @@ static void setup_handler(gconstpointer data) if (test->test_type == TEST_RSSI_FILTER) setup_rssi_filter_test(test->rssi_filter_test_data); + else if (test->test_type == TEST_CONTENT_FILTER) + setup_content_filter_test(test->content_filter_test_data); } /* Handler function to cleanup after the test is done */ @@ -359,6 +490,8 @@ static void teardown_handler(gconstpointer data) if (test->test_type == TEST_RSSI_FILTER) teardown_rssi_filter_test(test->rssi_filter_test_data); + else if (test->test_type == TEST_CONTENT_FILTER) + teardown_content_filter_test(test->content_filter_test_data); } /* Handler function to execute a test with the given data set */ @@ -368,6 +501,8 @@ static void test_handler(gconstpointer data) if (test->test_type == TEST_RSSI_FILTER) test_rssi_filter(test->rssi_filter_test_data); + else if (test->test_type == TEST_CONTENT_FILTER) + test_content_filter(test->content_filter_test_data); } int main(int argc, char *argv[]) @@ -387,5 +522,14 @@ int main(int argc, char *argv[]) define_test("/advmon/rssi/5", TEST_RSSI_FILTER, rssi_data_5, setup_handler, test_handler, teardown_handler); + define_test("/advmon/content/1", TEST_CONTENT_FILTER, content_data_1, + setup_handler, test_handler, teardown_handler); + define_test("/advmon/content/2", TEST_CONTENT_FILTER, content_data_2, + setup_handler, test_handler, teardown_handler); + define_test("/advmon/content/3", TEST_CONTENT_FILTER, content_data_3, + setup_handler, test_handler, teardown_handler); + define_test("/advmon/content/4", TEST_CONTENT_FILTER, content_data_4, + setup_handler, test_handler, teardown_handler); + return tester_run(); } From patchwork Thu Sep 17 07:10:41 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miao-chen Chou X-Patchwork-Id: 252700 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.8 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 820C6C43461 for ; Thu, 17 Sep 2020 07:11:50 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 29C8B2083B for ; Thu, 17 Sep 2020 07:11:50 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b="bW5Iw/vq" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726299AbgIQHLt (ORCPT ); Thu, 17 Sep 2020 03:11:49 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47268 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726245AbgIQHLp (ORCPT ); Thu, 17 Sep 2020 03:11:45 -0400 Received: from mail-pg1-x534.google.com (mail-pg1-x534.google.com [IPv6:2607:f8b0:4864:20::534]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7B75AC06174A for ; Thu, 17 Sep 2020 00:11:45 -0700 (PDT) Received: by mail-pg1-x534.google.com with SMTP id y1so820422pgk.8 for ; Thu, 17 Sep 2020 00:11:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=wXANRElz/qKLo+YPc/C/DfIhCrjha/xK9SJq8ld3Zj4=; b=bW5Iw/vqKkEA4l5FHtbwoJDv+D76RItaw3xlrppATUyWw7hs22EjQnVigtFnlznoPa 5antFM5KrFFkEYSBHEk+HuBfrzfUDej8pw2vwPMRmEDR+EMSUaLoGeNdhe+vOesJH7R9 cAKPmZRxjh0ZUwDXsUIdiWHyYh+OMY+FpubBg= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=wXANRElz/qKLo+YPc/C/DfIhCrjha/xK9SJq8ld3Zj4=; b=j4/zymIHTJRSrsFKcdOg7jBvmMEFjoVi8CwQ927gd4sTigj0Oa2Dkl73aUj+ITDgCH C1YIvhvIkJo4JDcux6FszIYhATdH120vV13HoXbrdo6bU6kv+qJjsMKihBrw+pikzSqO GEAw8d+WW10vm8Je/zZTguNUJAN6wp92eg17hPD1O4gtR9ztEeQ18kX5mO1GSzB7HfHq bzHzN3PitfFu4uhAd88TqBc4kI2uyP4oJ05uXsg6QA0UCMDiuY0A7BA6qcnYXkpphjGh XaPs6YkqWcUNd+w6dr5Bcd9aavBQOxaanP8JQzdhWfMKHLIWIaGLZSV+6APGLnYQsf/B F3Sw== X-Gm-Message-State: AOAM532i9Q5v0ZEli1Y4TcyZbzedob+CMuvJQe+v8oCoZ1SfpSCfZ54Q CjisaGo5ol3DpkNeWtoMZvojPsmmnN/ROA== X-Google-Smtp-Source: ABdhPJy5mTj/3YOTN3YiJ2C5EptuBHmiJRAcCbDDb36vydCXONr6xkWB48A5w8VjxvewK4/PV4Hziw== X-Received: by 2002:a62:7c82:0:b029:13c:1611:66b9 with SMTP id x124-20020a627c820000b029013c161166b9mr25094985pfc.4.1600326704726; Thu, 17 Sep 2020 00:11:44 -0700 (PDT) Received: from mcchou0.mtv.corp.google.com ([2620:15c:202:201:de4a:3eff:fe75:1314]) by smtp.gmail.com with ESMTPSA id j26sm933478pgl.91.2020.09.17.00.11.43 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 17 Sep 2020 00:11:44 -0700 (PDT) From: Miao-chen Chou To: Bluetooth Kernel Mailing List Cc: Marcel Holtmann , Alain Michaud , Howard Chung , chromeos-bluetooth-upstreaming@chromium.org, Luiz Augusto von Dentz , Manish Mandlik , Miao-chen Chou Subject: [BlueZ PATCH v4 6/8] adv_monitor: Implement Add Adv Patterns Monitor cmd handler Date: Thu, 17 Sep 2020 00:10:41 -0700 Message-Id: <20200917001020.BlueZ.v4.6.Ibbcb11712b613ef95c31b41207c3ea945c830018@changeid> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200917001020.BlueZ.v4.1.I2830b9c1212a64b062201ed9f2b71294f50ad22d@changeid> References: <20200917001020.BlueZ.v4.1.I2830b9c1212a64b062201ed9f2b71294f50ad22d@changeid> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Howard Chung - 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 the call through syslog Reviewed-by: Alain Michaud Reviewed-by: Miao-chen Chou Reviewed-by: Manish Mandlik --- (no changes since v1) src/adv_monitor.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/src/adv_monitor.c b/src/adv_monitor.c index 211094c89..deaa1894a 100644 --- a/src/adv_monitor.c +++ b/src/adv_monitor.c @@ -609,11 +609,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_HONORED; + + 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 btd_adv_monitor_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); @@ -646,7 +694,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 */ @@ -1063,7 +1128,7 @@ 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) + if (!monitor || monitor->state != MONITOR_STATE_HONORED) return; /* Reset the intermediate matched status */ From patchwork Thu Sep 17 07:10:45 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miao-chen Chou X-Patchwork-Id: 252699 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.8 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id D2B1AC43461 for ; Thu, 17 Sep 2020 07:12:09 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 99B8E2075E for ; Thu, 17 Sep 2020 07:12:09 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b="DElzKW60" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726200AbgIQHMI (ORCPT ); Thu, 17 Sep 2020 03:12:08 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47332 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726178AbgIQHMH (ORCPT ); Thu, 17 Sep 2020 03:12:07 -0400 Received: from mail-pf1-x42e.google.com (mail-pf1-x42e.google.com [IPv6:2607:f8b0:4864:20::42e]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B9561C06174A for ; Thu, 17 Sep 2020 00:12:06 -0700 (PDT) Received: by mail-pf1-x42e.google.com with SMTP id z18so653256pfg.0 for ; Thu, 17 Sep 2020 00:12:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=8rRwiC+DDU1Y36GFikHuTKEZhj0osqiQgXYTeZEu+do=; b=DElzKW60fXrp36aMCuwLCroEmRdI/QLe4f+mF6g2FWosJuaX/BF5B3uUQfwTlEjI9F xBRJHk7GBuFgtcE7eMe1zfkrQedSlmE0joUUxexwNYL9/dpyfeXzrYosbu49x4mclhOL TfBj3bI5Zsbx/kAq7ocPJ2xaZx7UgJGDcDoM4= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=8rRwiC+DDU1Y36GFikHuTKEZhj0osqiQgXYTeZEu+do=; b=XV4Ji5ZTPslDn9jWmJFlnhhqe8t8eP2mYP3fhKMmNmRYc6MYhEi1//7hqRMVcfqQvh wCHgIN6MAhn/0IgVC1GWYjdeZr6M/EBjCs0N1wcnxZkpltd0qGRDn+wsu+Dewn7SW0T0 YTOlvH6uvKprZx6I1NUdkqePvaFE/EMvDIwJ8MqR+NEvlc1Nn0gjrVdtGEowxVfK8rfE cMsM94TvzJACAkQiQOmpR3jjmUJ4WneWdvFVmrVWMpCnrsXLl1Kpt6Vw/lSlbQmS/Oua 09vmmIqMV0Nd+JVRfXlNb71fpOfmYQIPjKA5LYlazk6nIGfkiHPOG7PeqRePVpoGeOxf Zrcw== X-Gm-Message-State: AOAM5301bR8nX/5AzQr/zxtlRMtPMaUmgYVWkjD+G+kNjZDr/HESXiRa CMa/2wGI8xU/KmPJd0chCQu1kvAQfod3EQ== X-Google-Smtp-Source: ABdhPJzx9zzM0V7Xsu7M5ozkvlJNQZYAMUIGCIFdhb5cmPAW9IbizanGc4ZYyNGjngjsc0zPF1BqyA== X-Received: by 2002:a05:6a00:14cc:b029:142:2501:3988 with SMTP id w12-20020a056a0014ccb029014225013988mr9829677pfu.77.1600326725778; Thu, 17 Sep 2020 00:12:05 -0700 (PDT) Received: from mcchou0.mtv.corp.google.com ([2620:15c:202:201:de4a:3eff:fe75:1314]) by smtp.gmail.com with ESMTPSA id j26sm933478pgl.91.2020.09.17.00.12.04 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 17 Sep 2020 00:12:05 -0700 (PDT) From: Miao-chen Chou To: Bluetooth Kernel Mailing List Cc: Marcel Holtmann , Alain Michaud , Howard Chung , chromeos-bluetooth-upstreaming@chromium.org, Luiz Augusto von Dentz , Manish Mandlik Subject: [BlueZ PATCH v4 8/8] adv_monitor: Issue Remove Adv Monitor mgmt call Date: Thu, 17 Sep 2020 00:10:45 -0700 Message-Id: <20200917001020.BlueZ.v4.8.Ifda683c92ff520bf58ac37c02dc40b8d9598d1b0@changeid> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200917001020.BlueZ.v4.1.I2830b9c1212a64b062201ed9f2b71294f50ad22d@changeid> References: <20200917001020.BlueZ.v4.1.I2830b9c1212a64b062201ed9f2b71294f50ad22d@changeid> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Alain Michaud 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 --- (no changes since v3) Changes in v3: - Fix const qualifier of a pointer src/adv_monitor.c | 128 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 120 insertions(+), 8 deletions(-) diff --git a/src/adv_monitor.c b/src/adv_monitor.c index b4fe39eff..27da30b51 100644 --- a/src/adv_monitor.c +++ b/src/adv_monitor.c @@ -88,6 +88,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_HONORED, /* Accepted by kernel */ + MONITOR_STATE_REMOVING, /* Removing from kernel */ }; struct btd_adv_monitor_pattern { @@ -103,6 +104,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 */ @@ -631,6 +633,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_HONORED; DBG("Calling Activate() on Adv Monitor of owner %s at path %s", @@ -639,8 +642,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) @@ -714,20 +716,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_HONORED) + 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 */ @@ -936,6 +995,55 @@ 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 struct adv_monitor *monitor = data; + const struct mgmt_ev_adv_monitor_removed *ev = user_data; + + return monitor->monitor_handle == ev->monitor_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_HONORED) + 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) +{ + const uint16_t adapter_id = manager->adapter_id; + struct btd_adv_monitor_manager *manager = user_data; + const struct mgmt_ev_adv_monitor_removed *ev = param; + + 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, ev); + + 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, @@ -955,6 +1063,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; }