Android 4.3: BLE: фильтрация поведения startLeScan()

Я работаю над сенсорным устройством BluetoothLE, для которого мне нужно сформировать передачу данных "один ко многим". Согласно спецификации, у периферийных устройств может быть только один мастер, и из-за ограничений микросхемы и стека, над которыми я работаю, мастер может иметь только три ведомых. Из того, что я понимаю, Android не может стать BLE-ведомым в любом случае, поэтому использование моего устройства в качестве ведущего устройства не вариант.

Как в спецификации BT4, так и в документации производителя говорится о другом режиме работы, который называется режимом вещания. В широковещательном режиме соединение никогда не устанавливается, и данные приложения передаются как часть рекламного пакета. Это точно соответствует моим потребностям, так как многие телефоны Android/iOS могут одновременно сканировать каждый пакет. Рекламный пакет передается многократно пакетами, поэтому я полагаю, что получение данных будет в основном надежным. Если пакет потерян здесь и там, это может быть допустимо.

Интересно, что я хочу, чтобы эти пакеты передавали данные с датчиков, которые обновляются с частотой 10-20 Гц. Из примеров, которые я нашел в Интернете, BLE в этом режиме в основном используется для реализаций типа "iBeacon", где они передают статические данные. Я не могу найти информацию о том, как рекламные пакеты фильтруются в стеке Android. Возможно, они возвращают один результат на аппаратный адрес Bluetooth или это может быть уникальная комбинация адреса и данных. Второй вариант будет работать для этого приложения. Если запуск и остановка сканирования сбрасывает фильтр, я также могу заставить что-то работать.

В документации Android ничего не говорится о том, как работает фильтрация устройств в методе сканирования. Мне удалось найти одно сообщение в сети, пытающееся решить эту же проблему, которая имеет нерешенный ответ: BLE: многократное обнаружение одного и того же периферийного устройства во время сканирования. В iOS мой коллега сообщает мне, что есть параметр, который можно передать в функцию сканирования, которая делает это возможным.

Я попытался отследить код от вызова startLeScan() в исходном коде Android, но код довольно сложный, и использование абстракции затруднило идентификацию реализации объекта, который его содержит. Самое дальнее, что я получил, - это объект IBluetoothGatt, возвращенный из метода класса BluetoothManagerService getBluetoothGatt(). Этот объект получает запрос на запуск сканирования. Он создается около строки 790 BluetoothManagerService.java в текущей редакции на github. Объект создается из результата сообщения, поэтому я подозреваю, что результат может зависеть от телефона / драйвера. Я не в состоянии проследить это дальше.

Другой вопрос, который я хотел бы решить, - это как быстро можно включать и выключать сканирование. Сканирование является энергоемкой операцией, однако передача данных будет происходить периодически с довольно точным таймером реального времени. В результате было бы хорошей оптимизацией, если сканирование можно включать и выключать, так чтобы широковещательная передача и сканирование синхронизировались, а сканер выключался в остальные 90% времени. Это, вероятно, нужно будет проверить экспериментально.

Я все еще занимаюсь технико-экономическим обоснованием, чтобы выяснить, возможно ли это для нашего аксессуара для Android. Мой нынешний телефон еще не может работать с версией 4.3, поэтому у меня нет возможности проверить / взломать это экспериментально.

5 ответов

До сих пор с Android 4.3 и 4.4 это выглядит как беспорядок: некоторые устройства вызывают onLeScan(устройство BluetoothDevice, int rssi, byte[] scanRecord) несколько раз для одного устройства за один просмотр, некоторые нет. Не существует способа настроить фильтрацию, как в iOS (см. Ответ Arkadiusz Konior). Итак, теперь я начинаю список, потому что я не могу задать своим пользователям такой вопрос об их устройстве.

Тем не менее, перезапуск сканирования также не проблема на "нефильтрующих" устройствах. Итак, я перезапускаю сканирование на каждом устройстве.

Не фильтрует (постоянно вызывает onLeScan())

  • Samsung Galaxy S4 с 4.2.2 с использованием Samsung BLE SDK (я владел этим устройством)
  • Nexus 5 с 4.4 (добавлено [vegarwe]. Устройство будет непрерывно сканировать записи для ближайших устройств во время сканирования)
  • Samsung Galaxy S3 с 4.3 (JSS15J.I9300XXUGMK6, я тестировал на этом устройстве)
  • Samsung Galaxy S4 с 4.3 и 4.4.2 с использованием Android SDK (добавлено arnaud.b, сборка не предоставляется)
  • HTC One с 4.4.2 (добавлено arnaud.b, номер сборки не arnaud.b)

Фильтрующие устройства (относится к стандарту)

  • Nexus 4 с 4.3, 4.4 (у меня есть это устройство)
  • Nexus 7 2013 4G с 4.4.2 (KOT49H, я тестировал на этом устройстве)
  • Samsung Galaxy S4 mini с 4.2.2 (я тестировал на этом устройстве)
  • Motorola Moto X (добавлено пользователем user1603602, информация о версии для Android отсутствует)
  • Motorola Moto G с 4.3 (falcon_umts, мое тестирующее устройство)
  • Sony Xperia Tablet Z Wifi с Android 4.3 (сборка 10.4.B.0.577, модель SGP311, мое тестирующее устройство)
  • OnePlus One с 5.0.1 и 5.1.1 (Cyanogen 12.1)

Неизвестное поведение фильтрации (помогите связать устройство с определенной группой)

  • Nexus 7 2013 (сообщается о другом поведении, как здесь. Но я читал больше отчетов о том, что он относится к первой группе.)
  • Другие устройства SAMSUNG, HTC, Motorola, ...,

Текст на страницах 2535-2536 в спецификации Bluetooth (Core_v4.1.pdf) о дублирующих рекламных отчетах несколько неясен. Однако текст на странице 1258 понятен. Он указывает параметр Filter_Duplicates для команды HCI_LE_Set_Scan_Enable. В Android версии 4.4 (Kitkat) этот параметр равен 0x00 (дублирующая фильтрация отключена).

Существует простой способ выяснить, выполняется ли какая-либо фильтрация в чипе Bluetooth из версий Android 4.4 (Kitkat). Сделайте телефон разработчиком, введите параметры разработчика и установите флажок "Включить журнал отслеживания Bluetooth HCI". Затем выключите и включите Bluetooth один раз, чтобы настройки перекусили. Отныне все пакеты HCI между процессором приложения и чипом Bluetooth будут храниться на телефоне в файле, который извлекается с помощью adb pull storage/emulated/legacy/btsnoop_hci.log . Это не текстовый файл, и вам нужна программа с http://www.fte.com/products/default.aspx или wireshark для просмотра btsnoop_hci.log. Для wireshark вам нужна довольно свежая версия, потому что более старые версии не поддерживают BLE. По моему опыту, в микросхеме Bluetooth нет никакой фильтрации, т.е. событие HCI "Событие рекламного отчета LE" отправляется для каждого ADV_IND и ADV_NONCONN_IND, которые получает микросхема Bluetooth. Это касается телефонов с чипами Bluetooth Qualcomm/Atheros WCN 3680 и Broadcom BCM 4339.

Исправление: путь к btsnoop_hci.log может отличаться в зависимости от производителя телефона. Вы можете найти правильный путь по команде adb shell cat и т.д. /bluetooth/bt_stack.conf | grep BtSnoopFileName

Я разрабатываю приложение для Android 4.3 (Nexus 4 и 7), используя BLE, и, исходя из моих наблюдений, сканирование возвращает одно и то же устройство несколько раз, если не было запроса SCAN REQUEST, отправленного обратно на периферийное устройство.

Устройство может рекламироваться двумя способами: пассивным и активным. В пассивном режиме периферийное устройство просто сообщает все свои данные и не прослушивает после отправки периодического пакета. Он просто отправляет, спит, отправляет, спит... В активном режиме датчик также объявляет, но сообщение максимально короткое. После отправки он переключается на прослушивание на очень короткое время. Когда сканированный обнаруживает короткое сообщение, он немедленно отправляет команду SCAN REQUEST на периферийное устройство и получает ответ с более подробной информацией. Насколько я вижу, Android не отправлял запрос на сканирование несколько раз за одно сканирование.

Давайте предположим, что у нас есть 2 устройства в диапазоне. Одним из них является датчик nRF Temp от Nordic (пассивная реклама) и еще одно подключаемое устройство. Я получил следующий ответ сканирования:

11-10 21:32:54.281: D/BluetoothAdapter(13468): startLeScan(): null
11-10 21:32:54.281: D/BluetoothAdapter(13468): onClientRegistered() - status=0 clientIf=4
11-10 21:32:54.321: D/BluetoothAdapter(13468): onScanResult() - Device=CD:61:1A:A8:BC:BE RSSI=-94
11-10 21:32:55.122: D/BluetoothAdapter(13468): onScanResult() - Device=CB:32:81:CF:FD:00 RSSI=-61
11-10 21:32:56.414: D/BluetoothAdapter(13468): onScanResult() - Device=CB:32:81:CF:FD:00 RSSI=-62
11-10 21:32:57.715: D/BluetoothAdapter(13468): onScanResult() - Device=CB:32:81:CF:FD:00 RSSI=-61
11-10 21:32:59.016: D/BluetoothAdapter(13468): onScanResult() - Device=CB:32:81:CF:FD:00 RSSI=-63
11-10 21:33:01.609: D/BluetoothAdapter(13468): onScanResult() - Device=CB:32:81:CF:FD:00 RSSI=-63
11-10 21:33:02.901: D/BluetoothAdapter(13468): onScanResult() - Device=CB:32:81:CF:FD:00 RSSI=-63
11-10 21:33:04.212: D/BluetoothAdapter(13468): onScanResult() - Device=CB:32:81:CF:FD:00 RSSI=-62
11-10 21:33:04.282: D/BluetoothAdapter(13468): stopLeScan()

Как вы можете видеть, подключаемое устройство обнаружилось только один раз, а другое - 7 раз.

Другой вопрос, который я хотел бы решить, - это как быстро можно включать и выключать сканирование. Сканирование является энергоемкой операцией, однако передача данных будет происходить периодически с довольно точным таймером реального времени. В результате было бы хорошей оптимизацией, если сканирование можно включать и выключать, так что широковещательная передача и сканирование синхронизируются, а сканер выключается в остальные 90% времени. Это, вероятно, нужно будет проверить экспериментально.

Частота сканирования зависит от устройства. Кроме того, реклама обычно делается на 3 каналах: 37, 38 и 39, чтобы увеличить вероятность быть найденным. Однако это может быть хорошей идеей, чтобы получать рекламные пакеты с "активных" устройств несколько раз.

Фактическая спецификация Bluetooth говорит:

Повторные рекламные отчеты не требуется отправлять на хост. Двойной рекламный отчет - это рекламный отчет для того же адреса устройства, пока Канальный уровень остается в состоянии сканирования. Рекламные данные могут измениться; рекламные данные или данные ответов сканирования не считаются значимыми при определении дублирующих рекламных отчетов.

В соответствии со спецификацией это применимо в течение периода сканирования, что говорит о том, что правильным способом является остановка и возобновление сканирования каждый раз, когда вы получаете рекламу.

Исходя из моего опыта работы с BLE, кажется, что отправка переменных данных в рекламных объявлениях - не очень хорошая идея. Почти все предполагает, что данные из рекламы не меняются. Если вы хотите на самом деле отправлять переменные данные (например, показания термометра), тогда гораздо лучше подключиться к устройству и сделать это через характеристику. Это более надежно и потребляет гораздо меньше энергии. Недостатком является то, что вы можете подключить только до 8 устройств одновременно.

Рекламные объявления предназначены для обнаружения присутствия устройств и их идентификации.

В iOS этот флаг называется CBCentralManagerScanOptionAllowDuplicatesKey, Передача функции сканирования вызывает уведомление для каждого рекламного пакета. Я не смог найти похожий флаг в Android.

Другие вопросы по тегам