Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detecting whether a BLE device is connectable on Android

I am working on a project for configuring beacons. A certain amount of time after being powered on, a beacon becomes unconfigurable until it is power-cycled. In order to show a list of the configurable beacons, I am looking at certain characteristics (Bluetooth device name, certain manufacturer data in the advertising packet). I also need to know if it is "connectable", i. e. if the PDU Type in the BLE advertising packet for the device indicates that it is connectable. I've searched the Android Bluetooth classes high and low, both in Android 4.X and 5.X and haven't been able to find anything that will tell me this information.

I realize that one way to determine the beacon connectability is to connect up to it, e. g.: device.connectGatt(...). However, I've seen it take over two minutes sometimes before a callback to onConnectionStateChange comes back with STATE_DISCONNECTED. Also, there may be many of these beacons in an environment, and connecting up to every single one that might be configurable would be inefficient.

The iOS equivalent of this attribute can be found in the advertisementData dictionary under the key CBAdvertisementDataIsConnectable in the CBCentralManagerDelegate callback method centralManager:didDiscoverPeripheral:advertisementData:RSSI.

So, the question is: is there a way on Android to determine whether or not a BLE device is "connectable" from advertising data or scan result or ... ?

like image 747
mharper Avatar asked Feb 12 '15 01:02

mharper


People also ask

What is the use of BLE in Android?

(Android Developers Doc) This allows Android applications to communicate with BLE devices that have stricter power requirements, such as proximity sensors, heart rate monitors, fitness devices, home Automation System, Medical services and the Automotive industry.

How do I find BLE devices in Java?

To find BLE devices, you use the startScan () method. This method takes a ScanCallback as a parameter. You must implement this callback, because that is how scan results are returned.

How do I scan for BLE devices?

To find BLE devices, you use the startScan () method. This method takes a ScanCallback as a parameter. You must implement this callback, because that is how scan results are returned. Because scanning is battery-intensive, you should observe the following guidelines: As soon as you find the desired device, stop scanning.

What is devicescanactivity in the BLE app?

In the following example, the BLE app provides an activity ( DeviceScanActivity) to scan for available Bluetooth LE devices and display them in a list to the user. The following snippet shows how to start and stop a scan:


2 Answers

UPDATE: AS of the finalized APIs in the Android O SDK, the ScanResult class (itself added as of Android 5.0) now has the isConnectable() method. Detecting connectable advertisements is possible only on Android 8.0+. See here for more info: https://developer.android.com/reference/android/bluetooth/le/ScanResult.html#isConnectable()

Prior to Android 8.0, unfortunately it is not possible.

A connectable advertisement is determined by the PDU Header byte 0. You can see this in the example structure below:

d6 be 89 8e # Access address for advertising data (this is always the same fixed value)
40 # Advertising Channel PDU Header byte 0.  Contains: (type = 0), (tx add = 1), (rx add = 0)
24 # Advertising Channel PDU Header byte 1.  Contains:  (length = total bytes of the advertising payload + 6 bytes for the BLE mac address.)
05 a2 17 6e 3d 71 # Bluetooth Mac

The problem is on devices prior to Anroid 8.0, the Android scanning APIs give you no access to these headers. You get exactly three fields in the callback from Android 4.x:

onLeScan(BluetoothDevice device, rssi, byte[] scan data)

The scan data byte array starts after the header bytes mentioned above. And from what I can see of the BluetoothDevice definition, none of the fields or methods tell you if it is a connectable advertisement -- the class is just a container for the bluetooth mac address with methods to exercise functions on the bluetooth stack. And there are no methods in IBluetooth.aidl which is the private interface to the bluetooth stack (and what BluetoothDevice calls to get its info) that can get this flag.

It appears that this information is not passed up to the Java layer from the BlueDroid stack prior to Android 8.0.

like image 192
davidgyoung Avatar answered Sep 30 '22 16:09

davidgyoung


It should be possible since Nordic's nRF Master Control Panel does this.

After some digging I think I know how it does this. I'm not sure it's the right way to do it though.

I tried using the LE Advertiser and setting the device as connectable. In the Nordic app, a device is set as connectable depending on the bytes found at scanResult.getFlags().

I found that this code works for my devices:

int flags = scanResult.getScanRecord().getAdvertiseFlags();
if ((flags & 2) == 2) {
  //connectable
}
like image 44
user3934907 Avatar answered Sep 30 '22 16:09

user3934907