Как реализовать JobScheduler для сканирования маяков Bluetooth в фоновом режиме?

#android #bluetooth-lowenergy #ibeacon #android-bluetooth #ibeacon-android

#Android #bluetooth-низкое энергопотребление #ibeacon #android-bluetooth #ibeacon-Android

Вопрос:

Я пишу приложение, которое сканирует маяки Bluetooth, когда приложение находится на переднем плане и в фоновом режиме. Я разобрался с частью переднего плана, но я не знаю, что делать с фоновой частью, особенно в Android 8.0 и выше, где система не позволяет приложению работать дольше 15 минут в фоновом режиме.

Приложение должно сканировать маяки и получать их mac-адрес и UUID. Кроме того, он должен получить ответ на сканирование, потому что там есть некоторая информация, которую мне нужно расшифровать и сохранить. Я использовал руководство здесь для реализации сканирования переднего плана с помощью BluetoothLeScanner. Что касается фона, я попытался изменить режим сканирования на LOW_POWER, но ОС убивает приложение примерно через 15 минут. Пожалуйста, обратите внимание, что мне не нужна служба переднего плана с постоянным уведомлением, и меня устраивает, если сканирование выполняется только с интервалом ~ 15 минут.

Многие предлагали библиотеку Android Beacon, но я не смог найти требуемое описание Beacon для типа маяков, которые мы используем, Kontakt Beacon Pro BP16-3, и поэтому библиотека Beacon их не обнаруживает.

Информация, которая мне нужна от маяков, включает уникальный идентификатор и процент заряда батареи. Смотрите [здесь] ( https://support.kontakt.io/hc/en-gb/articles/206294004-How-to-check-the-battery-level-on-your-beacons) для получения подробной информации о том, где они находятся в ответе на сканирование.

Я был бы признателен за любую помощь в написании кода для поиска маяков в фоновом режиме, который работал бы на любой версии Android от 6.0 и выше, или за помощь в использовании библиотеки Beacon с маяком, о котором я упоминал выше.

РЕДАКТИРОВАТЬ: уникальный идентификатор и Bluetooth из ответа на сканирование

Когда я использую BLEScanner для сканирования маяков, я могу использовать ScanResult::getScanRecord() для получения объекта scanRecord. Затем я использую метод getServiceData (), чтобы получить массив байтов, первые 4 байта которого представляют уникальный идентификатор в ASCII, следующие два байта — версию прошивки в ASCII, и, наконец, последний байт — процент заряда батареи в шестнадцатеричном формате. Я даже подтвердил уровень заряда батареи с помощью официального приложения Kontakt, и поэтому я уверен, что это правильно.

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

 [2, 1, 6, 26, -1, 76, 0, 2, 21, -9, -126, 109, -90, 79, -94, 78, -104, -128, 36, -68, 91, 113, -32, -119, 62, -91, 68, 124, 56, -77, 8, 9, 75, 111, 110, 116, 97, 107, 116, 2, 10, -12, 10, 22, 13, -48, 68, 106, 69, 77, 52, 50, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0].
  

Кажется, что байты из индекса 46-49 указывают на уникальный идентификатор в ASCII, в данном случае «DjEM». Более того, байт с индексом 52 — это процент заряда батареи в десятичной системе счисления; В данном случае это 68.

Кажется, что с помощью BLE scanner я могу сэкономить много головной боли с точки зрения анализа уникального идентификатора и батареи. Однако было бы намного сложнее надежно реализовать фоновое сканирование. Следовательно, есть ли способ объединить лучшее из двух и получить библиотеку bacon для анализа уникального идентификатора и процента заряда батареи?

ПРАВКА2: сообщение библиотеки маяков при не распознавании моего маяка

Библиотеке beacon по-прежнему не удается обнаружить мой маяк, хотя я использую оба макета маяков iBeacon и EddyStone. В logcat выводится приведенное ниже:

  processing pdu type 16: 0201060d166afe0206010a64f456425a4d08094b6f6e74616b74000000000000000000000000000000000000000000000000000000000000000000000000 with startIndex: 5, endIndex: 16
This is not a matching Beacon advertisement. (Was expecting 02 15.  The bytes I see are: 0201060d166afe0206010a64f456425a4d08094b6f6e74616b74000000000000000000000000000000000000000000000000000000000000000000000000
Ignoring pdu type 01
  

Заранее спасибо.

Комментарии:

1. Есть ли у вас некоторая гибкость в ваших таймингах? Если да, то вы можете попробовать WorkManager и что-то вроде PeriodicWorkRequest. Они гарантированно будут запускаться, даже если приложение неактивно, и если вы установите интервал в 15 минут, то он сработает примерно через 15 мин.

2. @Ryujin сможет ли сканер ble сканировать каждый маяк или только те маяки, которые соответствуют фильтру. Другими словами, если я введу полный или пустой фильтр сканирования, будет ли он сканировать каждое устройство ble при его запуске в фоновом режиме? Спасибо.

3. Передача null в scanfilter, я думаю, делает это. Но это уже другой пункт из вашего основного вопроса.

4. @Ryujin Это связано, потому что если PeriodicWorkRequest позволяет мне сканировать любой маяк в фоновом режиме, то это отвечает на мой вопрос. Кроме того, могу ли я использовать PeriodicWorkRequest в любой версии ОС Android?

5. WorkManager — это API фоновых заданий общего назначения, он предназначен не только для BLE. Он полностью перенесен обратно на уровень API 14. Смотрите — developer.android.com/topic/libraries/architecture/workmanager

Ответ №1:

Вы не можете прочитать уровень заряда батареи вашего Kontakt.io beacon просто сканирует рекламу. Вы должны подключиться к нему с помощью встроенной службы GATT (подробнее об этом ниже).

Для общего сканирования в фоновом режиме и на переднем плане вы, безусловно, можете использовать библиотеку Android Beacon. Вы, вероятно, хотите использовать макет iBeacon:

         mBeaconManager.getBeaconParsers().add(new BeaconParser().
                setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"));
  

Но, хотя вышеуказанное устройство обнаружит ваши маяки, оно не сообщит вам уровень заряда батареи. Это потому, что Kontakt не объявляет уровень заряда батареи. Чтобы получить его, необходимо отдельно подключиться к службе GATT с UUID сервиса = 0x1805, а затем прочитать значение характеристики с UUID характеристики = 0x2a19. Это вернет значение в диапазоне 0-100, указывающее процентный уровень заряда батареи.

Вы можете увидеть подробную информацию о том, как к этому получить доступ в Kontakt.Раздел ввода-вывода «Другие сканеры BLE» здесь.

Хорошо, итак, это не говорит вам, как написать код для чтения этого значения. Для этого вам необходимо изучить основы программирования Bluetooth LE GATT, что требует некоторого обучения. Хорошее место для начала — здесь.Вам нужно будет запрограммировать несколько шагов:

  1. Сканирование устройства (библиотека Android Beacon выполняет эту часть за вас, используя свои API-интерфейсы ранжирования, предоставляя вам MAC-адрес устройства в String macAddress = beacon.getBluetoothAddress(); )
  2. Подключитесь к устройству. Если вы использовали библиотеку для выполнения обнаружения, вы можете получить ссылку на устройство и подключиться к нему с помощью bluetoothAdapter.getRemoteDevice(macAddress).connectGatt(...)
  3. Откройте для себя сервисы
  4. Найдите uuid службы 0x1805 в списке служб на предыдущем шаге.
  5. Ознакомьтесь с характеристиками обнаруженной службы выше
  6. Найдите uuid с характеристикой 0x2a19 в списке характеристик на предыдущем шаге.
  7. Прочитайте значение обнаруженной выше характеристики.

Все вышеперечисленное может быть включено в запланированное задание, которое будет выполняться каждые 15 минут, используя сохраненную копию строк Bluetooth MAC-адресов соседних маяков.

Это непросто, супер, я знаю. Я хотел бы дать вам несколько строк кода, чтобы просто получить этот уровень заряда батареи, но, к сожалению, это так не работает. Добро пожаловать в мир программирования Bluetooth LE GATT!

Комментарии:

1. Спасибо за подробный ответ! В прикрепленной мной ссылке говорится, что уровень заряда батареи и уникальный идентификатор включены в пакет ответа. На самом деле, когда я включаю режим отладки в библиотеке beacon и смотрю на logcat, я вижу байтовый ответ beacon и то, что библиотека не знала, как его проанализировать. Когда я преобразую этот байтовый ответ в ASCII, я вижу там уникальный идентификатор. Означает ли это, что мне все еще нужно использовать GATT?

2. Кроме того, если я использую планировщик заданий, выполняется ли фоновое сканирование для каждого маяка или только для маяков в регионе? И легко ли реализовать фоновое сканирование самостоятельно или это слишком хлопотно, и я должен просто использовать библиотеку beacon.

3. Можете ли вы вставить результаты сканирования (с ответом на сканирование) в конец вашего вопроса вместе с описанием того, что, по вашему мнению, является уровнем заряда батареи?

4. Реализовать фоновое сканирование самостоятельно легко для одной модели устройства, но нелегко обеспечить надежную работу на всех устройствах! Я проделываю огромную работу над библиотекой, чтобы сделать ее максимально надежной, и некоторые устройства по-прежнему вызывают сбои. Если вам в конечном итоге придется выполнять GATT, я бы позволил библиотеке выполнить сканирование, сохранить MAC-адреса Bluetooth в общих настройках, затем использовать вашу работу для обработки последних просмотренных устройств — считывать MAC-адреса из общих настроек, подключаться к каждому через GATT и считывать уровень заряда батареи, затем очищать общие настройки MAC.

5. Я отредактировал свой пост. Кроме того, библиотека beacon по-прежнему не обнаруживает мой маяк. Я добавил сообщение, которое он печатает в моем сообщении.

Ответ №2:

Согласно Контакту.документы io связаны, уровень заряда батареи также доступен в ответе на сканирование. (Примечание: ответ сканирования не всегда доступен при обнаружении пакета маяка, но он часто доступен — он предоставляется ОС Android и объединяется с данными сканирования при получении ответа сканирования.) В исходном массиве Android scan result bye ответ сканирования просто добавляется в конце обычных данных сканирования.

При использовании библиотеки Android Beacon также доступен ответ сканирования, операционная система Android принимает ответ сканирования и сохраняет его в поле BluetoothDevice#name. (Смотрите здесь). Библиотека Android Beacon при анализе маяка копирует это поле в поле Beacon#name. Таким образом, эта информация будет доступна вам в виде строки, если вы сможете проанализировать маяк, и устройство обнаружит ответ на сканирование.

Здесь два препятствия:

  1. Ваш контакт.io beacon, похоже, не рекламирует ничего, что на самом деле является рекламой beacon. Возможно, вам потребуется настроить его для рекламы формата iBeacon или, возможно, формата Eddystone-UID. Как только вы это сделаете и настроите библиотеку Android Beacon на этот макет, она обнаружит его. Обратите внимание, что показанные байты: processing pdu type 16: 0201060d166afe0206010a64f456425a4d08094b6f6e74616b74000000000000000000000000000000000000000000000000000000000000000000000000 with startIndex: 5, endIndex: 16
    This is not a matching Beacon advertisement.
    не соответствуют iBeacon или Eddystone. Похоже, это какая-то проприетарная 16-разрядная реклама сервиса GATT (тип объявления 0x16). Он имеет 16-разрядный UUID службы 0x0102, который ничему не соответствует в списке сигналов Bluetooth для стандартных или пользовательских 16-разрядных UUID. Вы так же хорошо догадываетесь, как и я, что это такое!

  2. #Имя BluetoothDevice# или #имя Beacon# будет строкой. Вам нужно будет преобразовать это в байты, а затем проанализировать уровень заряда батареи как Kontakt.io описано в документе scan response.

Комментарии:

1. Похоже, что из-за нулей в массиве байтов метод getName() возвращает только первую часть перед появлением нуля. Нет ли способа получить scanrecord с использованием библиотеки beacon? Я бы предпочел использовать фоновый режим библиотеки beacon, а не внедрять свой собственный.

2. Прямо сейчас библиотека Android Beacon напрямую не анализирует ответы на сканирование (она зависит от операционной системы, указанной выше). Эту функцию, безусловно, можно было бы добавить, и я не думаю, что это было бы ужасно сложно. Если вы заинтересованы в работе над этим в виде запроса на извлечение в библиотеку с открытым исходным кодом, я могу помочь вам начать. С другой стороны, вы могли бы что-то добавить, чтобы разобрать это из пользовательского макета маяка без изменений кода, если вы можете создать свой контакт. маяк ввода-вывода рекламирует стандартный маяк iBeacon, AltBeacon или Eddystone, который библиотека может декодировать, и ответ на сканирование прикрепляется в конце.

3. Если вы хотите продолжить с одним из вышеуказанных вариантов, я бы предложил открыть проблему здесь: github.com/AltBeacon/android-beacon-library/issues , что может упростить обсуждение.

4. И да, задания будут планироваться независимо от фонового режима. Если вы завершите работу своего приложения, следующее задание все равно будет выполняться по расписанию в течение ~ 15 минут.

5. Да, вы можете использовать задания без BootstrapNotifier. Мониторинг сообщает вам только о событиях входа / выхода. Ранжирование дает вам обратные вызовы каждую секунду со списком обнаруженных маяков. Оба работают с определением региона, указывающим, какие маяки будут соответствовать. Все нули соответствуют всем маякам.