I am writing an app that scans for Bluetooth beacons when the app is in the foreground and the background. I have the foreground part figured out, but I do not know what to do for the background part, especially in android 8.0 and above where the system does not let the app run for longer that 15 minutes in the background.
The app needs to scan for beacons and get their mac address and UUIDs. Also, it should get the scan response because there is some information there that I need to decode and save. I have used the guide here to implement the foreground scanning using BluetoothLeScanner. As for background, I tried changing the scan mode to LOW_POWER, but the OS kills the app after around 15 minutes. Please note that I do not want a foreground service with a constant notification and I am fine with the scanning only running at intervals of ~15 minutes.
Many suggested the Android Beacon Library, but I could not find the required beaconLayout for the type of beacons that we use, Kontakt Beacon Pro BP16-3, and so Beacon Library does not detect them.
The information I need from the beacons include the unique ID and battery percentage. See [here] (https://support.kontakt.io/hc/en-gb/articles/206294004-How-to-check-the-battery-level-on-your-beacons) for the details of where they are in the scan response.
I would appreciate any help with writing code to search for beacons in the background that would work on any Android version from 6.0 and up, or help with using Beacon Library with the beacon that I mentioned above.
EDIT: Unique ID & Bluetooth from scan response
When I use BLEScanner to scan for beacons, I can use ScanResult::getScanRecord() to get the scanRecord object. Then, I use the getServiceData() method to get a byte array whose first 4 bytes represent the unique ID in ASCII, the next two bytes are the firmware version in ASCII, and finally the last byte is the battery percentage in hexadecimal. I even confirmed the battery level with the official Kontakt app and so I am sure that it is correct.
When I use the Beacon Library, I could not find an easy way of getting a parsed version of the scan response. Instead, I have to use a NonBeaconLeScanCallback to get the byte array. Then, the byte array turns out to be
[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].
It seems that the bytes from index 46-49 indicate the unique id in ASCII, in this case "DjEM". Moreover, the byte at index 52 is the battery percentage, in decimal; In this case, it is 68.
It seems that by using BLE scanner I can save a lot of headache in terms of parsing the unique ID and battery. However, it would be much harder to reliably implement background scanning. Hence, is there a way to combine the best of two, and get the bacon library to parse the unique ID and battery percentage?
EDIT2: Beacon library message when not recognizing my beacon
The beacon library is still failing to detect my beacon even though I use both the iBeacon and EddyStone beacon layouts. It prints the below in the 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
Thanks in advance.
According to the Kontakt.io docs linked, the battery level is also available in the scan response. (Note: the scan response is not always available when you detect a beacon packet, but it is often available -- it is provided by the Android OS and gets merged with the scan data when the scan response gets received.) In a raw Android scan result bye array, the scan response is simply tacked on at the end of the regular scan data.
When using the Android Beacon Library, the scan response is also available, the Android operating system takes the scan response and stores it in the BluetoothDevice#name field. (See here). The Android Beacon library, when it parses a beacon, copies that field into the Beacon#name field. So that information will be available to you as a string if you can parse a beacon and the device detects a scan response.
Two obstacles here:
Your Kontakt.io beacon does not appear to be advertising anything that is actually a beacon advertisement. You might need to configure it to advertise the iBeacon format or perhaps the Eddystone-UID format. Once you do that and you configure the Android Beacon Library with that layout, it will detect it. Note that the bytes shown: processing pdu type 16: 0201060d166afe0206010a64f456425a4d08094b6f6e74616b74000000000000000000000000000000000000000000000000000000000000000000000000 with startIndex: 5, endIndex: 16
This is not a matching Beacon advertisement.
do not correspond to iBeacon or Eddystone. This appears to be some kind of proprietary 16-bit GATT Service advertisement (AD type 0x16). It has a 16-bit service UUID of 0x0102 which corresponds to nothing in the Bluetooth SIG list of standard or custom 16-bit UUIDs. Your guess is as good as mine what it is!
The BluetoothDevice#name or the Beacon#name will be a String. You will need to convert this to bytes, and then parse out the battery level as Kontakt.io describes in its scan response document.