What to keep in mind when developing your BLE Android app

BLE is native supported on Android from v4.3. It has been continuously improved as Android evolving. It's getting more and more complex with additional features. It's important that a Bluetooth app should work smoothly on all Android versions, from 4.3 to the latest one. One may find it's tricky to keep track of all the issues from earlier Android versions and the changes in BLE APIs of different API levels.

To help you with this, our app developers have provided a note, discussing about most important changes and added features on newer Android version 5 and 6, including some testings we have done on different Android phones regarding BLE performance.

Some interesting topic:

  1. Required permissions for BLE app.

  2. Scanning: different scan mode, report delay, callback type, etc

  3. Connection: autoConnect, connection parameter update, MTU, number packets per connection parameters.

  4. GATT server, advertising, how to set it up.

This document is strongly recommended to whom developing Android apps:

BLE_on_Android_v1.0.1.pdf

(Updated 05Dec2016)

Question and issue, please post as a separated case and post the link here.

Anonymous
  • Include the pairing process too especially when you read characteristics that are encrypted.

  • Why do you think public addresses are super rare? Just because Nordic's softdevice defaults to a static random address doesn't mean other stacks do the same ;)

    I just assumed, that if you produce thousands of devices it is much easier to use random addresses then register them. But in fact i have no idea. Maybe for central devices it is better to ensure uniqueness, also don't know.

    And the simple answer to the Google employee's question "how could someone have a random address without scanning?" is simply that for example a static random address may definitely be known beforehand.

    Of course there are a lot of options, like simply saving it in the app from last connection. But from the stack perspective, after resetting Bluetooth, is a new, unknown one. The solution should be to add the address type flag, not assume anything. But for now, until it's fixed, something has to be decided or calculated heuristicaly.

    Very interesting finding about the random address and autoConnect. I'll take a deeper look on Monday. Have a pleasant weekend and thanks again. I feel sorry for those who will read this ;)

  • And here's my feedback on the feedback ;)

    "my source in Google says it's still a thing. How do you know it varies between builds? Are you talking about the different public 7.1-betas or something more internal?"

    When I test again I see that it happens on all Nougat devices I have (3: Nexus 5X, Nexus 6, Sony Xperia Z3). I now notice however that the address type used depends on autoConnect. If the white list is used, random address seems always be assumed. But for autoConnect=false, public address is assumed instead. In earlier Androids it was always public address. If Google says it should be public then it's a bug. This is the commit (2016-02-02) that changed this: android.googlesource.com/.../ and android-review.googlesource.com/. See the last else-block, i.e. under "not a known device, i.e. attempt to connect to device never seen before". Now, instead of always choosing public address it seems they do some heuristics assuming random address, but if it is neither a non-resolvable, resolvable or static random address, then it is certainly a public address. The problem is that the bitcheck is written incorrectly to always return false, so random address is always chosen. That was however fixed by android.googlesource.com/.../ but that commit does not seem to be included in the latest nougat releases, so for now the address is always random.

    And the simple answer to the Google employee's question "how could someone have a random address without scanning?" is simply that for example a static random address may definitely be known beforehand.

    "... it most probably mean that it's a public address, despite the fact that public addresses are super rare, as I can imagine"

    Why do you think public addresses are super rare? Just because Nordic's softdevice defaults to a static random address doesn't mean other stacks do the same ;)

    "as far as I remember in a test I made using a BLE sniffer, when I was trying to connect to an advertising HTC One M8 phone form a Nexus with autoConnect=true, there was no Connection Req at all."

    An easier way to inspect such things are to simply check Android's Bluetooth HCI snoop log. There you see all LE Create Connection commands and exactly what scan parameters are being used. Kitkat had terrible default scan parameters for autoConnect btw, like 11.25 ms each second or so.

    ""In chapter 4.5. With Qualcomm Bluetooth chips, that is used in for example Nexus 5X, I've measured 12 packets per connection event." - wow, that's hell of a chip. Almost as good as Nordic's :)"

    Actually, Broadcom's chips (Nexus 5 for example) aren't actually that bad eihter. It is just the fact that by default Android sets both Minimum_CE_Length and Maximum_CE_Length to 0, and then the chip schedules (heuristically) to use max somewhere between 4 and 7 packets per event or so. If Maximum_CE_Length is set to something bigger, those chips manage to send packets during the whole connection event unless it overlaps with another connection. Some guys at Mediatek are working on a pull request changing this: android-review.googlesource.com/

  • Hi, thanks for the long comment. Here's my reply:

    1. I have fixed chapter numbering. It was broken when the document got the Nordic'y look&feel and no one noticed. Sorry!
    2. I had some problems with the URL with double slash as Word apparently "fixes" the URL to have just one, but "/-/" trick works. Also not quite my fault, except I did not notice. Also is fixed now.
    3. "In chapter 3, connecting to a BLE device using an address directly, as of Android Nougat it is no longer true that it will assume it is a public address. It varies between Android builds but it sometimes set the bit to random address." - my source in Google says it's still a thing. How do you know it varies between builds? Are you talking about the different public 7.1-betas or something more internal?
    4. "I wonder about the sentence "the logic being that random addresses can change any time, and if it is hardcoded, it must be a public one". Is that something someone at Google has confirmed or is that just an assumption? Actually I think this is an afterthought when they added BLE support since they didn't need to care about address type before. There are still other bugs where they mess up with the address type in their code..." - I got this information from Google and it seems logical. If an app wants to connect to a BLE device that has never (since Bluetooth restart) been scanned before and it knows the address, it most probably mean that it's a public address, despite the fact that public addresses are super rare, as I can imagine. Here's what I got:

    Message received 5 Oct 2016: Only the MAC address is used to identify a device (no type). When an address is passed to lower levels in C, the system tries to match it with a security record which contains the address type. If such record does not exist a PUBLIC address is assumed - how could someone have a random address without scanning? It's a known problem and fixing it is in our backlog. Currently the best way to connect is to scan and use the returned BluetoothDevice object. Mind, that the security record is forgotten when Bluetooth adapter is restarted (unless it's a PUBLIC address or the device is bonded).

    Since then I also got the information, that in OOB pairing using NFC the address type encoded in the NFC message is also ignored in Android 7.1. The fix has already been made on AOSP and will be available in Android 7.2, or whatever comes next.

    1. "Also, a third condition is that it also works without the need to scan before if the device is bonded (I've tested static random addresses and public addresses, not sure about random resolvable addresses)" - true, I added The device has a PUBLIC address or is bonded, or

    2. "It definitely attempts to connect to this device (with a HCI LE Create Connection). The only difference regarding that is that the address is provided by the white list instead of in the HCI LE Create Connection command. Also the standard configuration (which may be different between Android phone vendors) is to use more active scanning parameters when using autoConnect=false which leads to faster connection setups." - as far as I remember in a test I made using a BLE sniffer, when I was trying to connect to an advertising HTC One M8 phone form a Nexus with autoConnect=true, there was no Connection Req at all. However, this chapter based only on my experience so I will change it to what you wrote. Thank you.

    3. "There are more differences:.[...]" - let me just copy the whole paragraph to the PDF :) Although, as you wrote in the second URL, the race condition is now fixed.

    4. "Regarding older devices where overflow is not handled well, as well as on iOS, an easy workaround is to send each 15th packet or so as a Write With Response. Then you don't need to create any special acknowledgement logic on the peripheral side. It of course reduces speed a bit, but rather that than dropped packets." - Nice idea. Added to the PDF.

    5. "In chapter 4.5. With Qualcomm Bluetooth chips, that is used in for example Nexus 5X, I've measured 12 packets per connection event." - wow, that's hell of a chip. Almost as good as Nordic's :)

    6. "When referring to particular lines in Android source code, link to a specific commit rather than master. Otherwise the links will soon not be valid anymore. If you link to a file as a whole I guess you may link to a file in the master branch however." - true... usually I gave a commit link + link to the most recent version. But it is true they may change.

    7. "There are some race conditions in the Android SDK if you call methods too quickly after each other. See code.google.com/.../detail. If you need to have stable long-running connectivity to Android devices you should avoid restarting advertising immediately after a connection loss but rather wait a small time in between to prevent that the Android stack hangs. See code.google.com/.../detail - yes, adding delays help. If you check the source code for the Android DFU library I add a lot of those. For some devices it's enough to run a method from UI thread (I assume the small delay is enough, doesn't have to be the UI thread)

    Thanks again for your long response. I'll update the PDF next week.

  • Thanks a lot Emil for your detailed feedback !! I will forward your comment to our developer. We will udpate the document shortly and I will let you know when we do that.