6

I'm developing an android BLE app in which i try to establish a stable Connection between the app and a BLE device.

To achieve this i want my app to keep the BLE device data in shared preferences or somewhere.

After app close, i'd like my app to retrive this data and attemp a connection without scan for devices.

I prefer to avoid scanning every time becouse scan gives me a lot of problems and it takes time.

How can do it? Is there a way to save BLE data?

anticafe
  • 6,816
  • 9
  • 43
  • 74
L. Gangemi
  • 3,110
  • 1
  • 22
  • 47

2 Answers2

19

You need to store the Bluetooth Device Address (AB:CD:EF:01:02:03) of the device in for example shared preferences or in a sqlite db. Then use https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#getRemoteDevice(java.lang.String) when you later restart the app to retrieve a BluetoothDevice object. Once you have the BluetoothDevice object you can use the connectGatt method as usual.

There are some undocumented things you need to keep in mind however. Due to some horrible design flaws in Android's BLE API there is no way to tell it if you mean the given address is a Public Address or a Random Address. (You can read more about different address types at https://devzone.nordicsemi.com/question/43670/how-to-distinguish-between-random-and-public-gap-addresses/). The getRemoteDevice method should take an additional parameter "random address/public address" but it doesn't. Without the correct address type, the Bluetooth controller cannot connect to the device.

Android's BLE stack has some internal heuristics to "guess" if the address is public or random, but unfortunately that differs between Android versions and also if you use autoConnect=true or false. However if you have bonded the device (https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#createBond()) then it will store in its internal DB whether the given address is public or random. If the peripheral you want to connect to uses a random resolvable address it can handle that as well if you use bonding. Therefore I strongly suggest to use bonding.

If you don't use bonding you will need to scan before you connect to the device, since when you start a scan and a device is detected, Android's BLE stack will temporarily (until next restart of Bluetooth) remember the address type for an address.

Emil
  • 16,784
  • 2
  • 41
  • 52
  • 1
    you have enlightened me thanks, BLE is really hard to understand. – L. Gangemi Apr 19 '17 at 06:34
  • @L.Gangemi Thanks, this is very helpful. In the past year have you learned anything else about this random vs. public address issue. I am currently trying to connect to a device that uses random addressing but the device doesn't support bonding. Is scanning before connecting still the best/recommended approach? Any idea why Android has not fixed this problem yet? Do they just assume everyone who is using random addressing is also using bonding? – PICyourBrain Sep 20 '18 at 14:22
  • No they haven't fixed anything. The problem is that the original stack was probably created before BLE existed. In BT Classic there is only the public address type, so they choose to use BDADDR everywhere as identifier for a device through the whole stack. This is still how devices are tracked internally so a large part of the stack must be rewritten to support this, and I don't think the Android Bluetooth team prioritises this. – Emil Sep 20 '18 at 22:50
  • After device reboot, turn off/on Bluetooth or manual clear Bluetooth cache the device saved (MAC address saved in shared preferences or locla DB) won't connect. You need to scan again the device before try the direct connect. Follow this https://medium.com/@martijn.van.welie/making-android-ble-work-part-2-47a3cdaade07 – Giuseppe Laera Jan 11 '21 at 11:27
  • 1
    Just toggling Bluetooth on/off won't hurt. Clearing Bluetooth cache may however wipe all pairings, and hence connectGatt might fail due to incorrect address type. You should rather use getBondState() to first check if the device is still paired. If not, you should consider re-scan. – Emil Jan 11 '21 at 11:33
  • I check if BluetoothDevice.getType() is null in this case I start a scan with Mac addresses filter e after i connect with getRemoteDevice – Giuseppe Laera Jan 11 '21 at 11:43
3

I will continue on Emil's answer. Thanks @Emil for the 'big picture'.

  • Google API is flawed and you can't use getRemoteDevice() because it only understand 48bit address and do not know about all BLE address types.
  • You can't use Bonding if the device is dumb (anyone would be able to bond to it).

So your only solution is a Bluetooth scan.
However, since Android6 (Marshmallow) the user has to give location permission to apps using Bluetooth scanning. If you don't permit, it won't work.
So if you buy a Bluetooth thermometer (or heart-rate monitor or whatever), you have to permit the app to access location of your phone (or tablet). That's absurd!
@Emil call it a 'Google flaw'. You may also call it a 'Google machination'.

If I was a thermometer designer, I would add a push-button to accept bonding. It seems ridiculous because the button will only be used once.
[1]: https://www.polidea.com/blog/a-curious-relationship-android-ble-and-location/

user3435121
  • 633
  • 4
  • 13
  • 1
    Or why not any button at all? Just allow bonding all the time using "just works"? In case you actually don't have any sensitive data but just support bonding to solve this issue. – Emil Dec 09 '20 at 00:14
  • A thermometer do not normally have a push-button. That's why I wrote 'add a push-button' – user3435121 Dec 09 '20 at 20:02
  • I should have added another "not", i.e. why not skip button entirely and use "just works" pairing instead. – Emil Dec 09 '20 at 20:35