Bluetooth scanner android studio

Сканирование Bluetooth-устройств в радиусе действия с помощью приложения для Android

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

Как я понимаю, BroadcastReceiver не является огнем. Logcat не показывает никаких сообщений об ошибках и приложение не показывает Bluetooth-устройства. Что я тут неправильно сделал?

Моя Android Studio — версия 3.6.1. мой minSdkVersion — 18, а compileSdkVersion — 29.

2 ответа

Ну, тебе не нужно звонить mBluetoothAdapter.startDiscovery(); дважды. Это требуется один раз. И убедитесь, что у вас есть Местоположение и Bluetooth-разрешение в вашем манифесте. И убедитесь, что ваше устройство доступно для обнаружения для других устройств.

Ну, я размещаю код. Надеюсь, это поможет вам

А вот как это назвать

Вы не запросили местоположение или разрешения Bluetooth, необходимые для выполнения сканирования. См. https://developer.android.com/training/permissions/requesting#perm -check. Как ни странно, это не упоминается на https://developer.android.com/guide/ темы / подключения / Bluetooth.

В частности, вам необходимо проверить наличие разрешения времени выполнения ACCESS_FINE_LOCATION, и, если оно не предоставлено, вам необходимо запросить разрешение.

Источник

Create a Bluetooth Scanner With Android’s Bluetooth API

Bluetooth has become a very popular technology, especially on mobile devices. It’s a technology to discover and transfer data between nearby devices. Virtually every modern mobile device has Bluetooth capabilities these days. If you want to make an app interface with another Bluetooth enabled device, ranging from phones to speakers, you must know how to use Android’s Bluetooth API.

In this tutorial, we will be making an app that is similar to the built-in Bluetooth app in Android’s settings. It will include the following features:

  • enable Bluetooth on a device
  • display a list of paired devices
  • discover and list nearby Bluetooth devices

We will also go over the basics to connect and send data to another Bluetooth device. I’ve created a project to get us started, which you can download from GitHub. The below screenshot illustrates what the starter project looks like. If you get stuck or run into problems, then you can take a look at the finished project on GitHub.

1. Enabling Bluetooth

Before we can enable Bluetooth on an Android device, we need to request the necessary permissions. We do this in the app’s manifest. The BLUETOOTH permission allows our app to connect, disconnect, and transfer data with another Bluetooth device. The BLUETOOTH_ADMIN permission allows our app to discover new Bluetooth devices and change the device’s Bluetooth settings.

We will use the Bluetooth adapter to interface with Bluetooth. We instantiate the adapter in the ListActivity class. If the adapter is null , this means Bluetooth is not supported by the device and the app will not work on the current device. We handle this situation by showing an alert dialog to the user and exiting the app.

If Bluetooth is available on the device, we need to enable it. To enable Bluetooth, we start an intent provided to us by the Android SDK, BluetoothAdapter.ACTION_REQUEST_ENABLE . This will present a dialog to the user, asking them for permission to enable Bluetooth on the device. REQUEST_BLUETOOTH is a static integer we set to identify the activity request.

2. Obtaining a List of Paired Devices

In this step, we scan for paired Bluetooth devices and display them in a list. In the context of a mobile device, a Bluetooth device can either be:

It is important to know the difference between a paired and a connected Bluetooth device. Paired devices are aware of each other’s existence and share a link key, which can be used to authenticate, resulting in a connection. Devices are automatically paired once an encrypted connection is established.

Connected devices share an RFCOMM channel, allowing them to send and receive data. A device may have many paired devices, but it can only be connected to one device at a time.

Bluetooth devices are represented by the BluetoothDevice object. A list of paired devices can be obtained by invoking the getBondedDevices() method, which returns a set of BluetoothDevice objects. We invoke the getBondedDevices() method in the DeviceListFragment ‘s onCreate() method.

We use the getName() and getAddress() methods to obtain more information about the Bluetooth devices. The getName() method returns the public identifier of the device while the getAddress() method returns the device’s MAC address, an identifier uniquely identifying the device.

Now that we have a list of the paired devices, we create a DeviceItem object for each BluetoothDevice object. We then add each DeviceItem object to an array named deviceItemList . We’ll use this array to display the list of paired Bluetooth devices in our app. The code for displaying the list of DeviceItem objects is already present in the starter project.

3. Discover Nearby Bluetooth Devices

The next step is to discover devices the device isn’t paired with yet, unknown devices, and add them to the list of paired devices. We do this when the user taps the scan button. The code to handle this is located in DeviceListFragment .

Читайте также:  Отключить внутреннюю память андроид как

We first need to make a BroadcastReceiver and override the onReceive() method. The onReceive() method is invoked whenever a a Bluetooth device is found.

The onReceive() method takes an intent as its second argument. We can check what kind of intent is broadcasting with by invoking getAction() . If the action is BluetoothDevice.ACTION_FOUND , then we know we have found a Bluetooth device. When this occurs, we create a DeviceItem object using the device’s name and MAC address. Finally, we add the DeviceItem object to the ArrayAdapter to display it in our app.

When the scan button is toggled on, we simply need to register the receiver we just made and invoke the startDiscovery() method. If the scan button is toggled off, we unregister the receiver and invoke cancelDiscovery() . Keep in mind that discovery takes up a lot of resources. If your application connects with another Bluetooth device, you should always cancel discovery prior to connecting.

We also clear the ArrayAdapter object, mAdapter , when discovery begins. When we start scanning, we don’t want to include old devices that may no longer be in range of the device.

That’s it. We have finished our Bluetooth scanner.

4. Connecting to a Device

Bluetooth connections work like any other connection. There is a server and a client, which communicate via RFCOMM sockets. On Android, RFCOMM sockets are represented as a BluetoothSocket object. Fortunately for us, most of the technical code for servers is handled by the Android SDK and available through the Bluetooth API.

Connecting as a client is simple. Your first obtain the RFCOMM socket from the desired BluetoothDevice by calling createRfcommSocketToServiceRecord() , passing in a UUID, a 128-bit value that you create. The UUID is similar to a port number.

For example, let’s assume you are making a chat app that uses Bluetooth to chat with other nearby users. To find other users to chat with, you would want to look for other devices with your chat app installed. To do this, we would look for the UUID in the list of services of the nearby devices. Using a UUID to listen and accept Bluetooth connections automatically adds that UUID to the phone’s list of services, or service discovery protocol.

Once the BluetoothSocket is created, you call connect() on the BluetoothSocket . This will initialize a connection with the BluetoothDevice through the RFCOMM socket. Once our device is connected, we can use the socket to exchange data with the connected device. Doing this is similar to any standard server implementation.

Maintaining a Bluetooth connection is costly so we need to close the socket when we no longer need it. To close the socket, we call close() on the BluetoothSocket .

The following code snippet shows how to connect with a given BluetoothDevice :

Connecting as a server is slightly more difficult. First, from your BluetoothAdapter , you must get a BluetoothServerSocket , which will be used to listen for a connection. This is only used to obtain the connection’s shared RFCOMM socket. Once the connection is established, the server socket is no longer need and can be closed by calling close() on it.

We instantiate a server socket by calling listenUsingRfcommWithServiceRecord(String name, UUID mUUID) . This method takes two parameters, a name of type String and a unique identifier of type UUID . The name parameter is the name we give the service when it is added to the phone’s SDP (Service Discovery Protocol) entry. The unique identifier should match the UUID the client trying to connect is using.

We then call accept() on the newly obtained BluetoothServerSocket to wait for a connection. When the accept() call returns something that isn’t null , we assign it to our BluetoothSocket , which we can then use to exchange data with the connected device.

The following code snippet shows how to accept a connection as a server:

Reading and writing to the connection is done using streams, InputStream and OutputStream . We can get a reference to these streams by calling getInputStream() and getOutputStream() on the BluetoothSocket . To read from and write to these streams, we call read() and write() respectively.

The following code snippet shows how to do this for a single integer:

You can find both examples in the finished project on GitHub.

Conclusion

We have successfully made our own Bluetooth scanner and learned the following:

  • request the necessary Bluetooth permissions
  • enable Bluetooth on your phone
  • get a list of paired devices
  • scan and display a list of nearby Bluetooth devices
  • establish a Bluetooth connection between two devices
  • send and receive data over a Bluetooth connection

Feel free to use the code in the finished project on GitHub and modify it in your own applications.

Источник

Android Bluetooth Low Energy (BLE) — готовим правильно, часть #1 (scanning)

Содержание

Часть #1 (scanning), вы здесь.

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

Особенности работы BLE под Android

Google документация по BLE очень общая, в некоторых случаях нет важной информации или она устарела, примеры приложений не показывают, как правильно использовать BLE. Я обнаружил лишь несколько источников, как правильно сделать BLE. Презентация Stuart Kent дает замечательный материал для старта. Для некоторых продвинутых тем есть хорошая статья Nordic.

Читайте также:  Как прошить кирпич андроид который не реагирует не

Android BLE API это низкоуровневые операции, в реальных приложениях нужно использовать несколько слоев абстракции (как например сделано «из коробки» в iOS-CoreBluetooth). Обычно нужно самостоятельно сделать: очередь команд, bonding, обслуживание соединений, обработка ошибок и багов, мультипоточный доступ . Самые известные библиотеки: SweetBlue, RxAndroidBle и Nordic. На мой взгляд самая легкая для изучения — Nordic, см. детали тут.

Производители делают изменения в Android BLE стеке или полностью заменяют на свою реализацию. И надо учитывать разницу поведения для разных устройств в приложении. То что прекрасно работает на одном телефоне, может не работать на других! В целом не все так плохо, например реализация Samsung сделана лучше собственной реализации от Google!

В Android есть несколько известных (и неизвестных) багов которые должны быть обработаны, особенно в версиях 4,5 и 6. Более поздние версии работают намного лучше, но тоже имеют определенные проблемы, такие как случайные сбои соединения с ошибкой 133. Подробнее об этом ниже.

Не претендую на то, что я решил все проблемы, но мне удалось выйти на «приемлемый» уровень. Начнем со сканирования.

Сканирование устройств

Перед подключением к устройству вам нужно его просканировать. Это делается при помощи класса BluetoothLeScanner :

Сканер пытается найти устройства в соответствии с настройками filters и scanSettings , при обнаружении устройства вызывается scanCallback :

В результате сканирования мы получаем экземпляр ScanResult , в котором есть объект BluetoothDevice , его используют для подключения к устройству. Но прежде чем начать подключаться, поговорим о сканировании подробнее, ScanResult содержит несколько полезных сведений об устройстве:

Advertisement data — массив байтов с информацией об устройстве, для большинства устройств это имя и UUID сервисов, можно задать в filters имя устройства и UUID сервисов для поиска конкретных устройств.

RSSI уровень — уровень сигнала (насколько близко устройство).

… дополнительные данные, см. документацию по ScanResult здесь.

Помним про жизненный цикл Activity , onScanResult может вызываться многократно для одних и тех же устройств, при пересоздании Activity сканирование может запускаться повторно, вызываю лавину вызовов onScanResult .

Настраиваем фильтр для сканирования

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

Сканирование устройств по UUID сервиса

Используется если вам необходимо найти устройства определенной категории, например мониторы артериального давления со стандартным сервисным UUID: 1810. При сканировании устройство может содержать в Advertisement data UUID сервис, который характеризует это устройство. На самом деле эти данные ненадежные, фактически сервисы могут не поддерживаться, или подделываться Advertisement data данные, в общем тут есть творческий момент.

Прим. переводчика: одно из моих устройств со специфичной прошивкой, вообще не содержало список UUID сервисов в Advertisement data, хотя все остальные прошивки этого устройства работали ожидаемо.

Пример сканирования службы с артериальным давлением:

Обратите внимание на короткий UUID (например 1810 ), он называется 16-bit UUID и является частью длинного 128-bit UUID (в данном случае 00001810-000000-1000-8000-000-00805f9b34fb ). Короткий UUID это BASE_PART длинного UUID, см. спецификацию здесь.

Сканирование устройств по имени

Поиск устройств использует точное совпадение имени устройства, обычно это применяется в двух случаях:

поиск конкретного устройства

поиск конкретной модели устройства, например, мой нагрудный напульсник Polar H7 определяется как «Polar H7 391BBB014», первая часть — «Polar H7» общая для всех таких устройств этой модели, а последняя часть «391BBB014» — уникальный серийный номер. Это очень распространенная практика. Если вы хотите найти все устройства «Polar H7», то фильтр по имени вам не поможет, придется искать подстроку у всех отсканированных устройств в ScanResult . Пример с поиском точно по имени:

Сканирование устройств по MAC-адресам.

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

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

Настройка ScanSettings

ScanSettings объясняют Android как сканировать устройства. Там есть ряд настроек, которые можно задать, ниже полный пример:

ScanMode

Безусловно, это самый важный параметр. Определяет метод и время сканирования в Bluetooth стеке. Такая операция требует много энергии и необходим контроль над этим процессом, чтобы не разрядить батарею телефона быстро. Есть 4 режима работы, в соответствии с руководством Nordics и официальной документацией:

SCAN_MODE_LOW_POWER . В этом режиме Android сканирует 0.5с, потом делает паузу на 4.5с. Поиск может занять относительно длительное время, зависит от того насколько часто устройство посылает пакет advertisement данных.

SCAN_MODE_BALANCED . Время сканирования: 2с, время паузы: 3с, «компромиссный» режим работы.

SCAN_MODE_LOW_LATENCY . В этом случае, Android сканирует непрерывно, что очевидно требует больше энергозатрат, при этом получаются лучшие результаты сканирования. Режим подходит если вы хотите найти свое устройство как можно быстрее. Не стоит использовать для длительного сканирования.

SCAN_MODE_OPPORTUNISTIC . Результаты будут получены, если сканирование выполняется другими приложениями! Строго говоря, это вообще не гарантирует, что обнаружится ваше устройство. Стек Android использует этот режим в случае долгого сканирования, для понижения качества результатов (см. ниже «Непрерывное сканирование»).

Callback Type

Эта настройка контролирует как будет вызываться callback со ScanResult в соответствии с заданными фильтрами, есть 3 варианта:

Читайте также:  What is google account manager android

CALLBACK_TYPE_ALL_MATCHES . Callback будет вызывать каждый раз, при получении advertisement пакета от устройств. На практике — каждые 200-500мс будет срабатывать сallback, в зависимости от частоты отправки advertisement пакетов устройствами.

CALLBACK_TYPE_FIRST_MATCH . Callback сработает один раз для устройства, даже если оно далее будет снова посылать advertisement пакеты.

CALLBACK_TYPE_MATCH_LOST . Callback будет вызван, если получен первый advertisement пакет от устройства и дальнейшие advertisement пакеты не обнаружены. Немного странное поведение.

В практике обычно используются настройка CALLBACK_TYPE_ALL_MATCHES или CALLBACK_TYPE_FIRST_MATCH . Правильный тип зависит от конкретного случая. Если не знаете — используйте CALLBACK_TYPE_ALL_MATCHES , это дает больше контроля при получении callback, если вы останавливаете сканирование после получения нужных результатов — фактически это CALLBACK_TYPE_FIRST_MATCH .

Match mode

Настройка того, как Android определяет «совпадения».

MATCH_MODE_AGGRESSIVE . Агрессивность обуславливается поиском минимального количества advertisement пакетов и устройств даже со слабым сигналом.

MATCH_MODE_STICKY . В противоположность, этот режим требует большего количества advertisement пакетов и хорошего уровня сигнала от устройств.

Я не тестировал эти настройки подробно, но я в основном использую MATCH_MODE_AGGRESSIVE , это помогает быстрее найти устройства.

Number of matches

Параметр определяет сколько advertisement данных необходимо для совпадения.

MATCH_NUM_ONE_ADVERTISEMENT . Одного пакета достаточно.

MATCH_NUM_FEW_ADVERTISEMENT . Несколько пакетов нужно для соответствия.

MATCH_NUM_MAX_ADVERTISEMENT . Максимальное количество advertisement данных, которые устройство может обработать за один временной кадр.

Нет большой необходимости в таком низкоуровневом контроле. Все что вам надо — быстро найти свое устройство, обычно используются первые 2 варианта.

Report delay

Задержка для вызова сallback в миллисекундах. Если она больше нуля, Android будет собирать результаты в течение этого времени и вышлет их сразу все в обработчике onBatchScanResults . Важно понимать что onScanResult не будет вызываться. Обычно применяется, когда есть несколько устройств одного типа и мы хотим дать пользователю выбрать одно из них. Единственная проблема здесь — предоставить информацию пользователю для выбора, это должен быть не только MAC-адрес (например имя устройства).

Важно: есть известный баг для Samsung S6 / Samsung S6 Edge, когда все результаты сканирования имеют один и тот же RSSI (уровень сигнала) при задержке больше нуля.

Кеширование Android Bluetooth стека

В результате процесса сканирования вы получаете список BLE устройств и при этом данные устройств «кешируются» в Bluetooth стеке. Там хранится основная информация: имя, MAC-адрес, тип адреса (публичный, случайный), тип устройства (Classic, Dual, BLE) и т.д. Android нужны эти данные, чтобы подключится к устройству быстрее. Он кеширует все устройства, которые видит при сканировании. Для каждого из них записывается небольшой файл с данными. Когда вы пытаетесь подключиться к устройству, стек Android ищет соответствующий файл, чтобы прочитать данные для подключения. Важный момент — одного MAC-адреса недостаточно для успешного подключения к устройству!

Очистка кеша

Bluetooth кеш, как и любой другой, не существует вечно, есть 3 ситуации, когда он очищается:

Выключение и включение системного переключателя Bluetooth

Очистка данных приложения (в ручном режиме в настройках телефона)

Это достаточно неудобный момент для разработчиков, потому что телефон часто перезагружается, пользователь может включать-выключать самолетный режим. Есть еще различия между производителями телефонов, например на некоторых телефонах Samsung, кеш не очищался при выключении Bluetooth.

Это значит, что нельзя полагаться на данные об устройстве из BT кеша. Есть небольшой трюк, он поможет узнать закешировано ли устройство или нет:

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

Непрерывное сканирование?

Вообще хорошая практика – избегать непрерывного сканирования потому что, это очень энергоемкая операция, а пользователи любят, когда батарея их смартфона работает долго. Если вам действительно нужно постоянное сканирование, например при поиске BLE-маячков, выберите настройки сканирования с низким потреблением и ограничивайте время сканирования, например когда приложение находится только на переднем плане (foreground), либо сканируйте с перерывами.

Плохая новость в том, что Google в последнее время ограничивает (неофициально) непрерывное сканирование:

c Android 8.1 сканирование без фильтров блокируется при выключенном экране. Если у вас нет никаких ScanFilters , Android приостановит сканирование, когда экран выключен и продолжит, когда экран снова будет включен. Комментарии от Google. Это очевидно очередной способ энергосбережения от Google.

c Android 7 вы можете сканировать только в течение 30 минут, после чего Android меняет параметры на SCAN_MODE_OPPORTUNISTIC . Очевидное решение, перезапускать сканирование с периодом менее, чем 30 мин. Посмотрите commit в исходном коде.

с Android 7 запуск и останов сканирования более 5 раз за 30 секунд временно отключает сканирование.

Непрерывное сканирование в фоне

Google значительно усложнил сканирование на переднем плане. Для фонового режима вы столкнетесь с еще большими трудностями! Новые версии Android имеют лимиты на работу служб в фоновом режиме, обычно после 10 минут работы, фоновый сервис прекращает свою работу принудительно. Посмотрите возможные решения этой проблемы:

Проверка разрешений (permissions)

Есть еще несколько важных моментов, прежде чем мы закончим статью. Для начала сканирования нужны системные разрешения (permissions):

Убедитесь, что все разрешения одобрены, или запросите их у пользователя. Разрешение ACCESS_COARSE_LOCATION Google считает «опасным» и для него требуется обязательное согласие пользователя.

Прим. переводчика, в моем проекте для корректной работы с BLE потребовалось еще 2 разрешения: ACCESS_FINE_LOCATION (для API ACCESS_BACKGROUND_LOCATION обсуждение на Stackoverflow.

В итоге полный список разрешений включая версию Android10:

После получения всех нужный разрешений, нужно проверить включен Bluetooth, если нет — используйте Intent для запуска запроса на включение:

Заключение

Мы научились запускать сканирование BLE устройств с учетом жизненного цикла Activity (Fragment / Service), использовать фильтры и различные настройки сканирования, также узнали все нужные разрешения (permissions) для удачного запуска сканирования и особенности работы Android-Bluetooth кеша. В следующей статье мы погрузимся глубже в процесс подключения и отключения к устройствам.

Источник

Оцените статью