Understanding Android BLE ScanSettings parameters - android

The documentation around ScanSettings is very laconic and I'm having a hard time coming up with an optimal configurations.
My requirements dictate I should report a new advertisement packet that matches my filter criteria (filtered on service UUIDs) as soon as possible. Duplicate advertisement packets are of no use to me. The advertisement packets will change when the peripheral will register an event on its side and the advertisement packet is thus an opportunity for me to detect changes and act accordingly. I also need to know when the peripheral stopped advertising (powered off or not in range anymore).
I've tried to set the callback type like following:
setCallbackType(ScanSettings.CALLBACK_TYPE_FIRST_MATCH | ScanSettings.CALLBACK_TYPE_MATCH_LOST)
The documentation for this parameters says:
int CALLBACK_TYPE_ALL_MATCHES
Trigger a callback for every Bluetooth advertisement found that matches the filter criteria. If no filter is active, all advertisement packets are reported.
int CALLBACK_TYPE_FIRST_MATCH
A result callback is only triggered for the first advertisement packet received that matches the filter criteria.
int CALLBACK_TYPE_MATCH_LOST
Receive a callback when advertisements are no longer received from a device that has been previously reported by a first match callback.
I was hoping with this to get notified when the peripheral becomes available and then get notified when the peripheral is lost. However this doesn't trigger any scan results, at least not on my Pixel 2. So I just switched to use CALLBACK_TYPE_ALL_MATCHES and now I get all the advertisement packets. This is probably what I want anyways if I want to get updated packets from the same peripheral.
I looked at the setMatchMode parameters and it appears that this only configures the callbacks rate based on the signal strength:
int MATCH_MODE_AGGRESSIVE
In Aggressive mode, hw will determine a match sooner even with feeble signal strength and few number of sightings/match in a duration.
int MATCH_MODE_STICKY
For sticky mode, higher threshold of signal strength and sightings is required before reporting by hw
The setNumOfMatches claims to handle the number of advertisements per filter, but none of the documented parameters seem to make a difference, I still get a burst of packets from the same peripheral that didn't change, seemingly at about the same rate no matter the parameter.
To meet the part of my requirement where I need to determine if the peripheral stopped advertising (powered off or not in range) I came up with a timestamp of last seen packet from the peripheral and determine if the peripheral is lost or not when I don't see a packet for x amount of seconds. This was necessary because the CALLBACK_TYPE_FIRST_MATCH and CALLBACK_TYPE_MATCH_LOST don't seem to work at all.
This unfortunately means I get bursts of similar packets triggering a chain reaction in my code that I now have to deal with.
How can I configure the scanning so that I get notified only when the advertisement packet changes or the peripheral stopped advertising? Am I missing something here, or that's just not possible.

I've recently had to implement reacting to a device that stops advertising (updating a list of available BLE devices). The majority of my testing was also done on the Pixel 2.
I'm not sure you can receive a callback upon advertisement package change for a peripheral AND receive a callback when a peripheral stops advertising, although I have not tested this.
To receive a callback when the device stops advertising and stop the bombardment of callbacks for every packet, use :
Kotlin:
private val bleScanSettings =
ScanSettings.Builder().setCallbackType((ScanSettings.CALLBACK_TYPE_FIRST_MATCH or ScanSettings.CALLBACK_TYPE_MATCH_LOST)).build()
Java:
ScanSettings bleScanSettings = new ScanSettings().Builder.setCallbackType((ScanSettings.CALLBACK_TYPE_FIRST_MATCH | ScanSettings.CALLBACK_TYPE_MATCH_LOST)).build();
My code is in Kotlin, the Java code is what I think it should be and looks identical to yours. This configuration will trigger an error callback when scanning without a ScanFilter, could that be your original issue?
This configuration will trigger a callback upon receiving the first matching advertisement packet for a device as well as when a matched device stops advertising.
You could try to see if the matching callback is also triggered when the advertisement packet for a (previously matched) device changes. Otherwise, it seems impossible.

Set code like this:
new ScanSettings.Builder ()
.setScanMode (ScanSettings.SCAN_MODE_BALANCED)
.setCallbackType (ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.build (),...
This setting works on all devices. But each smartphone gives you a different error and this causes confusion.

Related

Obtaining RSSI from Scan Request / Scan Reply packets

I'm new to android development and I'm writing a APP as proof of concept for a research project using the BLE Google API. I have two android phones, one of which is advertising some beacon X that carries a scan response Y, while the second phone is scanning for advertisements. I would like to access the RSSI values corresponding to the ScanRequest and ScanReply packets that follow the advertisement.
During device to device communication, when the overwritten onScanCallback method is called on the second phone, I can only obtain a single RSSI value by calling result.getRssi() on the passed ScanResult. Furthermore, as expected, the corresponding ScanRecord provides two ServiceUuids by calling the record.getServiceUuids, where the first UUID corresponds to the advertisement itself, and the second one to the programmed scan reply.
Until now I haven't found any public methods to recover the RSSI of the ScanReply, is there some other way of accessing this second RSSI? And the same applies to the RSSI of the ScanRequest that is automatically sent by the listening device, is there a way of recovering it?
Thanks a lot in advance,
Ivan Morales
The RSSI in the scan callback already corresponds to the RSSI in the scan response packet, not the advertising packet. Android's Bluetooth stack throws away the RSSI value for the first packet. Why do you need it anyway? It should be more or less the same as the first packet since both packets are sent within such a short interval.
You can't get RSSI for a scan request. This info is not sent from controller to host if you read the HCI standard. Even if the Bluetooth stack would listen to "LE Scan Request Received event", that event doesn't include RSSI.

Different interval time between BLE device advertising and the callback from startScan of android phone

I use android phone to detect a beacon using startScan method, the interval time of beacon adversing is 800ms, but I got the beacon data from callback of startScan every 3~10 seconds. My expectation is I can get the data every 1 second.
Below is my codes. So is there any other ways to get my expectation. Or someone can explain why the interval time is not the same as the adversing time ?
ScanSettings.Builder builder = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY);
// scan filters has been set the right value.
bluetoothLeScanner.startScan(scanFilters, builder.build(), scanCallback);
Nothing about the code shown is a problem. I can confirm your expectation is met with equivalent code in other apps on other devices. This suggests a problem with :
Your BLE device not advertising as often as you expect
Your Android phone (possible but unlikely)
Some other aspect of your code that you did not show in your question.
Is you BLE device a beacon? If so, I suggest:
Install BeaconScope on the same phone, scan for your beacon, then check the PPS rating (Packets Per Second -- scan for at least 30 seconds until the measurement stabilizes). If you see a value of 0.1-0.3 then the problem is NOT with your code and must be (1) or (2). If you see a much higher PPS value, the problem is with some other aspect of your code not shown.
Use a second Android phone with BeaconScope as a transmitter and transmit a beacon at the 10 Hz default rate. Does your app get a packet every 100-200ms? If so, you have confirmed the problem is with your BLE device not transmitting as much as you expect.
If your BLE device is not a beacon you can at least perform the second test.
It's recommended to use 'SCAN_MODE_LOW_LATENCY' mode only hen the application is running in the foreground.
Incase you are in background, you can run a sticky foreground service and set a periodic frequency of 1 sec for your service.
Also scanning Ble for every 1 sec might be extremely heavy for the app.

Android Ble, Pheriperal Advertise status during the scan

In my application I need to start some BLE scans, get the results and then show them in a list or in a grid.
When i start the scan my device start to look for advertisements basing on which ScanFilters I wrote.
Every time a Pheriperal is found I the application trigger the scan callback method and i can add the new device into the list.
If i got an error the app trigger the callback method and i can tell the user about the broblem.
Example
Imagine the Scan going on for 30s.
At the moment it starts i get 3 Ble Pheriperal.
At second 15 one of them stop to advertise and turn bluetooth off.
At the end of the scan my list will have 3 Ble devices but i will not be able to connect to one of them.
How do I know which of them is the one which turned off?
Where do i get his status?
Thanks for the help.
You can try this, technically it is possible as per the documentation. in onScanResult callback, check for the callbackType parameter. If it is CALLBACK_TYPE_MATCH_LOST then get the device from the results and remove it from your cache.

BluetoothAdapter: How do I know if no BLE devices are found after startLeScan()?

I am developing an Android app to record the RSSI fluctuations from a BLE beacon. I use the startLeScan(BluetoothAdapter.LeScanCallback callback) method and from the callback get the RSSI value every time the beacon is discovered (till I have some MAX_NUM values). Then I call stopLeScan().
However, I also want to know whenever the app performs a scan but is unable to discover the beacon during this period. As I understand, this will result in the onLeScan() callback not being invoked. How can I do this?

Get BLE Scan without filter duplicate UUID

I'm writing an BLE application, where need to track if peripherals device is advertising or has stop.
I followed getting peripherals without duplications this and BLE Filtering behaviour of startLeScan() and I completely agree over here.
To make it feasible I kept timer which re-scan for peripherals after certain time (3 sec). But with new device available on market(with 5.0 update), some time re-scan take bit time to find peripherals.
Any suggestion or if anyone have achieved this?
Sounds like you're interested in scanning advertisements rather than connecting to devices. This is the "observer" role in Bluetooth Low Evergy, and corresponds to the "broadcaster" role more commonly known as a Beacon. (Bluetooth Core 4.1 Vol 1 Part A Section 6.2)
Typically you enable passive scanning, looking for ADV_IND packets broadcast by beacons. These may or may not contain a UUID. Alternatively, you can active scan by transmitting SCAN_REQ to which you may receive a SCAN_RSP. Many devices use different advertising content in ADV_IND and SCAN_RSP to increase the amount of information that can be broadcast - you could, for instance, fit a UUID128 into the ADV_IND followed by the Device Name in the SCAN_RSP. (Bluetooth Core 4.1 Vol 2 Part E Section 7.8.10)
Now you need to define "go away" - are you expecting the advertisements to stop or to fade away? You will get a Receive Signal Strength Indication "RSSI" with each advertisement (Bluetooth Core 4.1 Vol 2 Part E Section 7.7.65.2) - this is how iBeacon positioning works and there's plenty of support for beacon receivers in Android.
Alternatively you wait for N seconds for an advertisement that should be transmitted every T seconds where N>2T. The downside of the timed approach is that probably not receiving a beacon isn't the same as definitely receiving a weak beacon; to be sure you need N to be large and that impacts the latency between the broadcaster being switched off or moving out of range and your app detecting it.
One more thing - watch out that Advertising stops if something connects to a Peripheral (if you really are scanning for peripherals) another good reason to monitor RSSI.
First scenario: Bonded Devices
We know that if a bond is made, then most of the commercially available devices send directed advertisements in during re-connection. In situations such as this, according to BLE 4.0 specification, you cannot scan these devices on any BLE sniffer.
Second scenario: Connectable Devices
Peripheral devices are usually in this mode when they are initially in the reset phase. The central sends a connect initiator in response to an advertisement packet. This scenario offers you a lot of flexibility since you can play around with two predominant configuration options to alter connection time. These are: slavelatency on the peripheral and conninterval on the central. Now, I don't know how much effort it's going to take get it working on the Android platform, but if you use the Bluez BLE stack and a configurable peripheral such as a TI Sensor tag, then you can play around with these values.
Third scenario: Beacon devices
Since this is what your question revolves around, according to the BLE architecture, there are no parameters to play with. In this scenario, the central is just a dumb device left at the mercy of when a peripheral chooses to send it's beaconing signal.
Reference:
http://www.amazon.com/Inside-Bluetooth-Communications-Sensing-Library/dp/1608075796/ref=pd_bxgy_14_img_z
http://www.amazon.com/Bluetooth-Low-Energy-Developers-Handbook/dp/013288836X/ref=pd_bxgy_14_img_y
Edit: I forgot, have you tried setting the advertiser to non-connectable? That way you should be able to get duplicate scan results
I am dealing with a similar issue, that is, reliably track the RSSI values of multiple advertising devices over time.
It is sad, the most reliable way i found is not nice, rather dirty and battery consuming. It seems due to the number of android devices that handle BLE differently the most reliable.
I start LE scan, as soon as i get a callback i set a flag to stop and start scan again. That way you work around that DUPLICATE_PACKET filter issue since it resets whenever you start a fresh scan.
The ScanResults i dump into a sqlite db wich i shrink and evaluate once every x seconds.
It should be easy to adapt the shrinking to your use case, i.e. removing entries that are older than X, and then query for existance of a device to find out if you received a ScanResult in the last X seconds. However dont put that X value too low, as you must take into account that you still lose alot of advertisement packets on android LE scan, compared to a BLE scan on i.e. bluez..
Edit:
I can add some information i already found for speeding up the performance on Advertisement discovery. It involves modifying and compiling the bluedroid sources and root access to the device. Easiest would be building a full android yourself, i.e. Cyanogenmod.
When a LE scan is running, the bluetooth module sends the scan sesponse via HCI to the bluedroid stack. There various checks are done until it finally gets handed to the Java onScanResult(...) which is accessed via JNI.
By comparing the log of the hci data sent from the bluetooth module (can be enabled in /etc/bluetooth/bt_stack.conf) with debug output in the bluedroid stack aswell as the Java side i noticed that alot of advertisement packets are discarded, especially in some check. i dont really understand, beside that it has something to do with the bluedroid inquiry database
From the documentation of ScanResult we see that the ScanRecord includes the advertisement data plus the scan response data. So it might be that android blocks the report until it got the scan response data/ until it is clear there is no scan response data. This i could not verify, however a possibility.
As i am only interested in rapid updates on the RSSI of those packets, i simply commented that check out. It seems that way every single packet i get from the bluetooth moduly by hci is handed through to the Java side.
In file btm_ble_gap.c in function BOOLEAN btm_ble_update_inq_result(tINQ_DB_ENT *p_i, UINT8 addr_type, UINT8 evt_type, UINT8 *p)
comment out to_report = FALSE; in the following check starting on line 2265.
/* active scan, always wait until get scan_rsp to report the result */
if ((btm_cb.ble_ctr_cb.inq_var.scan_type == BTM_BLE_SCAN_MODE_ACTI &&
(evt_type == BTM_BLE_CONNECT_EVT || evt_type == BTM_BLE_DISCOVER_EVT)))
{
BTM_TRACE_DEBUG("btm_ble_update_inq_result scan_rsp=false, to_report=false,\
scan_type_active=%d", btm_cb.ble_ctr_cb.inq_var.scan_type);
p_i->scan_rsp = FALSE;
// to_report = FALSE; // to_report is initialized as TRUE, so we basically leave it to report it anyways.
}
else
p_i->scan_rsp = TRUE;

Categories

Resources