Skip to content

Commit 7ff338d

Browse files
bluetooth: ble_adv_data: add manufacturer data find helper
Add ble_adv_data_manufacturer_data_find() to locate manufacturer-specific data in an advertising payload and prefix-match it against a target value. Follows the pattern of existing _find helpers for name, uuid and appearance. Modified unit tests to align with changes. Added changelog. Signed-off-by: Martynas Smilingis <martynas.smilingis@nordicsemi.no>
1 parent 3062b35 commit 7ff338d

4 files changed

Lines changed: 228 additions & 2 deletions

File tree

doc/nrf-bm/release_notes/release_notes_changelog.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ Libraries
8181
* An issue where the :c:func:`ble_conn_params_phy_radio_mode_get` function would incorrectly return the PHY mode mask of a pending update rather than the currently active PHY mode if a PHY update initiated by the :c:func:`ble_conn_params_phy_radio_mode_set` function was still in progress.
8282
* An issue where the SoftDevice define :c:macro:`BLE_GAP_PHYS_SUPPORTED` was used instead of the PHY preferences set with Kconfig when initiating or responding to a PHY update procedure.
8383

84+
* :ref:`lib_ble_adv` library:
85+
86+
* Added the :c:func:`ble_adv_data_manufacturer_data_find` function to locate manufacturer-specific data in an advertising payload and prefix-match it against a target value.
87+
8488
Bluetooth LE Services
8589
---------------------
8690

include/bm/bluetooth/ble_adv_data.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,23 @@ bool ble_adv_data_uuid_find(const uint8_t *buf, uint16_t len, const ble_uuid_t *
308308
*/
309309
bool ble_adv_data_appearance_find(const uint8_t *buf, uint16_t len, const uint16_t *appearance);
310310

311+
/**
312+
* @brief Search encoded Advertising data for manufacturer-specific data.
313+
*
314+
* @param[in] buf Encoded advertising data.
315+
* @param[in] len Buffer length.
316+
* @param[in] target_data Target manufacturer data to match (prefix). Starts with the
317+
* 2-byte little-endian company identifier followed by optional
318+
* vendor payload.
319+
* @param[in] target_data_len Length of the target manufacturer data.
320+
*
321+
* @retval true If manufacturer-specific data in which the first @p target_data_len bytes matches
322+
* @p target_data was found among @p buf.
323+
* @retval false If no match was found among @p buf, or if @p buf or @p target_data was @c NULL.
324+
*/
325+
bool ble_adv_data_manufacturer_data_find(const uint8_t *buf, uint16_t len,
326+
const uint8_t *target_data, uint8_t target_data_len);
327+
311328
#ifdef __cplusplus
312329
}
313330
#endif

lib/bluetooth/ble_adv/ble_adv_data.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,3 +742,32 @@ bool ble_adv_data_appearance_find(const uint8_t *data, uint16_t data_len,
742742

743743
return false;
744744
}
745+
746+
bool ble_adv_data_manufacturer_data_find(const uint8_t *data, uint16_t data_len,
747+
const uint8_t *target_data, uint8_t target_data_len)
748+
{
749+
uint16_t data_offset;
750+
uint16_t parsed_len;
751+
752+
if (!data || !target_data || target_data_len == 0) {
753+
return false;
754+
}
755+
756+
data_offset = 0;
757+
parsed_len = ble_adv_data_search(data, data_len, &data_offset,
758+
BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA);
759+
760+
if (data_offset == 0 || parsed_len == 0) {
761+
return false;
762+
}
763+
764+
if (target_data_len > parsed_len) {
765+
return false;
766+
}
767+
768+
if (memcmp(&data[data_offset], target_data, target_data_len) == 0) {
769+
return true;
770+
}
771+
772+
return false;
773+
}

tests/unit/lib/bluetooth/ble_adv_data/src/unity_test.c

Lines changed: 178 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,11 +196,17 @@ static void encode_128bit_uuid_test_data(void)
196196
*/
197197
uint32_t nrf_err;
198198
ble_uuid_t uuids[] = {
199-
{.uuid = TEST_UUID_128_VAL, .type = TEST_UUID_128_TYPE},
199+
{
200+
.uuid = TEST_UUID_128_VAL,
201+
.type = TEST_UUID_128_TYPE
202+
},
200203
};
201204
struct ble_adv_data adv_data = {
202205
.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE,
203-
.uuid_lists.complete = {.uuid = uuids, .len = 1},
206+
.uuid_lists.complete = {
207+
.uuid = uuids,
208+
.len = 1
209+
},
204210
};
205211

206212
__cmock_sd_ble_uuid_encode_Stub(stub_uuid_encode);
@@ -209,6 +215,30 @@ static void encode_128bit_uuid_test_data(void)
209215
TEST_ASSERT_EQUAL(NRF_SUCCESS, nrf_err);
210216
}
211217

218+
static void encode_manufacturer_test_data(const uint8_t *payload, uint8_t payload_len)
219+
{
220+
/*
221+
* Separate from common_test_data_encode() because that does not encode a
222+
* manufacturer-specific data AD field, which is required to test
223+
* ble_adv_data_manufacturer_data_find(). This encodes the Nordic company
224+
* identifier followed by an optional caller-provided payload as a
225+
* MANUFACTURER_SPECIFIC_DATA AD field.
226+
*/
227+
uint32_t nrf_err;
228+
struct ble_adv_data_manufacturer manuf = {
229+
.company_identifier = BLE_COMPANY_ID_NORDIC,
230+
.data = (uint8_t *)payload,
231+
.len = payload_len,
232+
};
233+
struct ble_adv_data adv_data = {
234+
.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE,
235+
.manufacturer_data = &manuf,
236+
};
237+
238+
nrf_err = ble_adv_data_encode(&adv_data, buf, &len);
239+
TEST_ASSERT_EQUAL(NRF_SUCCESS, nrf_err);
240+
}
241+
212242
/* ble_adv_data_encode() => Generic Unit Tests */
213243
void test_ble_adv_data_encode_error_null(void)
214244
{
@@ -1944,6 +1974,152 @@ void test_ble_adv_data_appearance_find_zero_length_data(void)
19441974
TEST_ASSERT_FALSE(found);
19451975
}
19461976

1977+
/* ble_adv_data_manufacturer_data_find() Unit Tests */
1978+
void test_ble_adv_data_manufacturer_data_find_company_id_only(void)
1979+
{
1980+
/* Encoded manufacturer data contains only the company ID,
1981+
* search with the same 2-byte company ID matches.
1982+
*/
1983+
bool found;
1984+
const uint8_t target[] = {0x59, 0x00}; /* Nordic company ID, little-endian */
1985+
1986+
encode_manufacturer_test_data(NULL, 0);
1987+
1988+
found = ble_adv_data_manufacturer_data_find(buf, len, target, sizeof(target));
1989+
TEST_ASSERT_TRUE(found);
1990+
}
1991+
1992+
void test_ble_adv_data_manufacturer_data_find_full_match(void)
1993+
{
1994+
/* Exact match of company ID + full payload. */
1995+
bool found;
1996+
const uint8_t payload[] = {0xDE, 0xAD, 0xBE, 0xEF};
1997+
const uint8_t target[] = {0x59, 0x00, 0xDE, 0xAD, 0xBE, 0xEF};
1998+
1999+
encode_manufacturer_test_data(payload, sizeof(payload));
2000+
2001+
found = ble_adv_data_manufacturer_data_find(buf, len, target, sizeof(target));
2002+
TEST_ASSERT_TRUE(found);
2003+
}
2004+
2005+
void test_ble_adv_data_manufacturer_data_find_prefix_match(void)
2006+
{
2007+
/* Target is a prefix of the encoded manufacturer data, prefix match succeeds. */
2008+
bool found;
2009+
const uint8_t payload[] = {0xDE, 0xAD, 0xBE, 0xEF};
2010+
const uint8_t target[] = {0x59, 0x00, 0xDE, 0xAD};
2011+
2012+
encode_manufacturer_test_data(payload, sizeof(payload));
2013+
2014+
found = ble_adv_data_manufacturer_data_find(buf, len, target, sizeof(target));
2015+
TEST_ASSERT_TRUE(found);
2016+
}
2017+
2018+
void test_ble_adv_data_manufacturer_data_find_wrong_company_id(void)
2019+
{
2020+
/* Different company ID, no match. */
2021+
bool found;
2022+
const uint8_t payload[] = {0xDE, 0xAD};
2023+
const uint8_t target[] = {0x4C, 0x00, 0xDE, 0xAD}; /* Random company ID */
2024+
2025+
encode_manufacturer_test_data(payload, sizeof(payload));
2026+
2027+
found = ble_adv_data_manufacturer_data_find(buf, len, target, sizeof(target));
2028+
TEST_ASSERT_FALSE(found);
2029+
}
2030+
2031+
void test_ble_adv_data_manufacturer_data_find_wrong_payload(void)
2032+
{
2033+
/* Company ID matches but payload differs. */
2034+
bool found;
2035+
const uint8_t payload[] = {0xDE, 0xAD, 0xBE, 0xEF};
2036+
const uint8_t target[] = {0x59, 0x00, 0x12, 0x34};
2037+
2038+
encode_manufacturer_test_data(payload, sizeof(payload));
2039+
2040+
found = ble_adv_data_manufacturer_data_find(buf, len, target, sizeof(target));
2041+
TEST_ASSERT_FALSE(found);
2042+
}
2043+
2044+
void test_ble_adv_data_manufacturer_data_find_target_longer_than_encoded(void)
2045+
{
2046+
/* Target length exceeds encoded manufacturer data length. */
2047+
bool found;
2048+
const uint8_t payload[] = {0xDE, 0xAD};
2049+
const uint8_t target[] = {0x59, 0x00, 0xDE, 0xAD, 0xBE, 0xEF};
2050+
2051+
encode_manufacturer_test_data(payload, sizeof(payload));
2052+
2053+
found = ble_adv_data_manufacturer_data_find(buf, len, target, sizeof(target));
2054+
TEST_ASSERT_FALSE(found);
2055+
}
2056+
2057+
void test_ble_adv_data_manufacturer_data_find_no_manuf_data_in_data(void)
2058+
{
2059+
/* Encoded data has no manufacturer-specific data AD field. */
2060+
bool found;
2061+
const uint8_t target[] = {0x59, 0x00};
2062+
2063+
common_test_data_encode();
2064+
2065+
found = ble_adv_data_manufacturer_data_find(buf, len, target, sizeof(target));
2066+
TEST_ASSERT_FALSE(found);
2067+
}
2068+
2069+
void test_ble_adv_data_manufacturer_data_find_null_data(void)
2070+
{
2071+
const uint8_t target[] = {0x59, 0x00};
2072+
2073+
bool found = ble_adv_data_manufacturer_data_find(NULL, 10, target, sizeof(target));
2074+
2075+
TEST_ASSERT_FALSE(found);
2076+
}
2077+
2078+
void test_ble_adv_data_manufacturer_data_find_null_target(void)
2079+
{
2080+
bool found;
2081+
2082+
encode_manufacturer_test_data(NULL, 0);
2083+
2084+
found = ble_adv_data_manufacturer_data_find(buf, len, NULL, 2);
2085+
TEST_ASSERT_FALSE(found);
2086+
}
2087+
2088+
void test_ble_adv_data_manufacturer_data_find_zero_target_len(void)
2089+
{
2090+
bool found;
2091+
const uint8_t target[] = {0x59, 0x00};
2092+
2093+
encode_manufacturer_test_data(NULL, 0);
2094+
2095+
found = ble_adv_data_manufacturer_data_find(buf, len, target, 0);
2096+
TEST_ASSERT_FALSE(found);
2097+
}
2098+
2099+
void test_ble_adv_data_manufacturer_data_find_zero_length_data(void)
2100+
{
2101+
const uint8_t target[] = {0x59, 0x00};
2102+
2103+
bool found = ble_adv_data_manufacturer_data_find(buf, 0, target, sizeof(target));
2104+
2105+
TEST_ASSERT_FALSE(found);
2106+
}
2107+
2108+
void test_ble_adv_data_manufacturer_data_find_only_other_ad_types(void)
2109+
{
2110+
/* Encoded data contains appearance, flags, tx_power, UUID, conn_int and name
2111+
* but no manufacturer-specific data AD field. Searching for manufacturer data
2112+
* should not match any of the other AD fields.
2113+
*/
2114+
bool found;
2115+
const uint8_t target[] = {0x59, 0x00};
2116+
2117+
common_test_data_encode();
2118+
2119+
found = ble_adv_data_manufacturer_data_find(buf, len, target, sizeof(target));
2120+
TEST_ASSERT_FALSE(found);
2121+
}
2122+
19472123
/* Unit Test Setup */
19482124
void setUp(void)
19492125
{

0 commit comments

Comments
 (0)