我希望能够自定义 HCI LE 广告报告,这样我就可以在不连接到 IOS 应用程序 (iPhone) 的情况下收集其他服务 UUID。
BLUEZ hcitool(通过 lescan 选项)为每个设备返回 HCI LE 广告报告,如下所示:
Num_Reports,
Event_Type[i],
Address_Type[i],
Address[i],
Length[i],
Data[i],
RSSI[i]
我检索此 HCI LE 广告报告的代码基于hcitool.c cmd_lescan函数,如下所示:
static void print_eir(const uint8_t *eir, uint8_t eir_len, bool le)
{
info("print_eir");
uint16_t len = 0;
if (eir_len == 0)
return;
while (len < eir_len - 1) {
uint8_t field_len = eir[0];
const uint8_t *data = &eir[2];
uint8_t data_len;
char name[239], label[100];
uint8_t flags, mask;
int i;
/* Check for the end of EIR */
if (field_len == 0)
break;
len += field_len + 1;
/* Do not continue EIR Data parsing if got incorrect length */
if (len > eir_len) {
len -= field_len + 1;
break;
}
data_len = field_len - 1;
switch (eir[1]) {
case BT_EIR_FLAGS:
info("BT_EIR_FLAGS");
flags = *data;
mask = flags;
info("Flags: 0x%2.2x", flags);
for (i = 0; eir_flags_table[i].str; i++) {
if (flags & (1 << eir_flags_table[i].bit)) {
info(" %s",
eir_flags_table[i].str);
mask &= ~(1 << eir_flags_table[i].bit);
}
}
break;
case BT_EIR_UUID16_SOME:
info("BT_EIR_UUID16_SOME\n");
if (data_len < sizeof(uint16_t))
break;
print_uuid16_list("16-bit Service UUIDs (partial)",
data, data_len);
break;
case BT_EIR_UUID16_ALL:
info("BT_EIR_UUID16_ALL\n");
if (data_len < sizeof(uint16_t))
break;
print_uuid16_list("16-bit Service UUIDs (complete)",
data, data_len);
break;
case BT_EIR_UUID32_SOME:
info("BT_EIR_UUID32_SOME\n");
if (data_len < sizeof(uint32_t))
break;
print_uuid32_list("32-bit Service UUIDs (partial)",
data, data_len);
break;
case BT_EIR_UUID32_ALL:
info("BT_EIR_UUID32_ALL\n");
if (data_len < sizeof(uint32_t))
break;
print_uuid32_list("32-bit Service UUIDs (complete)",
data, data_len);
break;
case BT_EIR_UUID128_SOME:
info("BT_EIR_UUID128_SOME\n");
if (data_len < 16)
break;
print_uuid128_list("128-bit Service UUIDs (partial)",
data, data_len);
break;
case BT_EIR_UUID128_ALL:
info("BT_EIR_UUID128_ALL\n");
if (data_len < 16)
break;
print_uuid128_list("128-bit Service UUIDs (complete)",
data, data_len);
break;
case BT_EIR_NAME_SHORT:
info("BT_EIR_NAME_SHORT");
memset(name, 0, sizeof(name));
memcpy(name, data, data_len);
info("Name (short): %s", name);
break;
case BT_EIR_NAME_COMPLETE:
info("BT_EIR_NAME_COMPLETE");
memset(name, 0, sizeof(name));
memcpy(name, data, data_len);
info("Name (complete): %s", name);
break;
case BT_EIR_SERVICE_UUID128:
info("BT_EIR_SERVICE_UUID128\n");
if (data_len < 16)
break;
print_uuid128_list("128-bit Service UUIDs",
data, data_len);
break;
case BT_EIR_SERVICE_DATA:
info("BT_EIR_SERVICE_DATA\n");
if (data_len < 2)
break;
sprintf(label, "Service Data (UUID 0x%4.4x)",
get_le16(&data[0]));
print_hex_field(label, &data[2], data_len - 2);
break;
case BT_EIR_RANDOM_ADDRESS:
info("BT_EIR_RANDOM_ADDRESS\n");
if (data_len < 6)
break;
print_addr("Random Address", data, 0x01);
break;
case BT_EIR_PUBLIC_ADDRESS:
info("BT_EIR_PUBLIC_ADDRESS\n");
if (data_len < 6)
break;
print_addr("Public Address", data, 0x00);
break;
case BT_EIR_TX_POWER:
info("BT_EIR_TX_POWER");
if (data_len < 1)
break;
info("TX power: %d dBm", (int8_t) *data);
break;
case BT_EIR_SMP_OOB_FLAGS:
info("BT_EIR_SMP_OOB_FLAGS");
info("SMP OOB Flags: 0x%2.2x", *data);
break;
default:
sprintf(label, "Unknown EIR field 0x%2.2x", eir[1]);
print_hex_field(label, data, data_len);
break;
}
eir += field_len + 1;
}
if (len < eir_len && eir[0] != 0)
packet_hexdump(eir, eir_len - len);
}
static int print_advertising_devices(int dd, uint8_t filter_type)
{
unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr;
struct hci_filter nf, of;
struct sigaction sa;
socklen_t olen;
int len;
info("print_advertising_devices");
olen = sizeof(of);
if (getsockopt(dd, SOL_HCI, HCI_FILTER, &of, &olen) < 0) {
error("Could not get socket options");
return -1;
}
hci_filter_clear(&nf);
hci_filter_set_ptype(HCI_EVENT_PKT, &nf);
hci_filter_set_event(EVT_LE_META_EVENT, &nf);
if (setsockopt(dd, SOL_HCI, HCI_FILTER, &nf, sizeof(nf)) < 0) {
error("Could not set socket options");
return -1;
}
memset(&sa, 0, sizeof(sa));
sa.sa_flags = SA_NOCLDSTOP;
sa.sa_handler = sigint_handler;
sigaction(SIGINT, &sa, NULL);
while (1) {
evt_le_meta_event *meta;
le_advertising_info *leinfo;
char btAddress[18];
while ((len = read(dd, buf, sizeof(buf))) < 0) {
if (errno == EINTR && signal_received == SIGINT) {
len = 0;
goto done;
}
if (errno == EAGAIN || errno == EINTR)
continue;
goto done;
}
ptr = buf + (1 + HCI_EVENT_HDR_SIZE);
len -= (1 + HCI_EVENT_HDR_SIZE);
meta = (void *) ptr;
if (meta->subevent != 0x02) // must be report type
goto done;
// Overlay report structure
leinfo = (le_advertising_info *) (meta->data + 1);
// Get report count
info("************** BLE Buffer **************");
uint8_t reports_count = meta->data[0];
info("report-count: %d", reports_count);
int i; // Dump LE report data
for (i = 0; i < leinfo->length; i++) {
printf("%02x", leinfo->data[i]);
}
printf("\n");
// Get BT address & type
ba2str(&leinfo->bdaddr, btAddress);
info("BT Address: %s, Type: %s,", btAddress, (leinfo->bdaddr_type == LE_PUBLIC_ADDRESS) ? "public" : "random");
// Get BT Service name
char name[30];
memset(name, 0, sizeof(name));
eir_parse_name(leinfo->data, leinfo->length, name, sizeof(name) - 1);
info("Service name: %s", name);
// Determine Range(RSSI)
int8_t rssi;
rssi = leinfo->data[leinfo->length];
if ((uint8_t) rssi == 0x99 || rssi == 127)
error("RSSI: invalid (0x%2.2x)", (uint8_t) rssi);
else
info("RSSI: %d dBm (0x%2.2x)", rssi, (uint8_t) rssi);
print_eir(leinfo->data, leinfo->length, true);
info("****************************************");
}
done:
setsockopt(dd, SOL_HCI, HCI_FILTER, &of, sizeof(of));
if (len < 0)
return -1;
return 0;
}
static void cmd_lescan(int dev_id)
{
int err, dd;
uint8_t own_type = 0x01; // Random
uint8_t scan_type = 0x00; // passive scan - not sending scan responses
uint8_t filter_type = 0;
uint8_t filter_policy = 0x00;
uint16_t interval = htobs(0x0010);
uint16_t window = htobs(0x0010);
uint8_t filter_dup = 0; // not filtering duplicates
info("cmd_lescan");
if (dev_id < 0)
dev_id = hci_get_route(NULL);
dd = hci_open_dev(dev_id);
if (dd < 0) {
perror("Could not open device");
exit(1);
}
err = hci_le_set_scan_parameters(dd, scan_type, interval, window,
own_type, filter_policy, 10000);
if (err < 0) {
perror("Set scan parameters failed");
exit(1);
}
err = hci_le_set_scan_enable(dd, 0x01, filter_dup, 10000);
if (err < 0) {
perror("Enable scan failed");
exit(1);
}
info("LE Scan ...");
err = print_advertising_devices(dd, filter_type);
if (err < 0) {
perror("Could not receive advertising events");
exit(1);
}
err = hci_le_set_scan_enable(dd, 0x00, filter_dup, 10000);
if (err < 0) {
perror("Disable scan failed");
exit(1);
}
hci_close_dev(dd);
}
int main(int argc, char** argv)
{
info("main");
// flush stdout immediately
setvbuf(stdout, NULL, _IONBF, 0);
cmd_lescan(-1);
return 0;
}
我能够转储 IOS 应用程序的 HCI LE EVENT 报告结果,如下所示:
RAW Dump: 02011a1106111111111111111111111111111111110909434253616d706c65
************** BLE Buffer **************
report-count: 1
BT Address: 5E:96:DC:70:18:11
Type: random,
ENTER: eir_parse_name
Service name: CBSample
RSSI: -45 dBm (0xd3)
ENTER: print_eir
BT_EIR_FLAGS
Flags: 0x1a
LE General Discoverable Mode
Simultaneous LE and BR/EDR (Controller)
Simultaneous LE and BR/EDR (Host)
BT_EIR_UUID128_SOME
ENTER: print_uuid128_list
128-bit Service UUIDs (partial): 1 entry
11111111-1111-1111-1111-111111111111
BT_EIR_NAME_COMPLETE
Name (complete): CBSample
****************************************
不幸的是,HCI LE 广告报告仅允许单个 128 位 BLE 服务 UUID。有没有办法自定义报告以允许在不连接到 iPhone(IOS) 等设备的情况下收集更多信息?