We are making an app that uses BLE advertisements to create first a list of our industrial devices. Then of course something else happens but this is not relevant for the question. What is important is that we need to receive BLE advertisements continuously.
There is a service-like object that starts scan ble, waits about 2 seconds then stops it.
After 500 milliseconds it starts again.
This continues endless.
Here below the code snippet to start the scan, for completeness, just standard code, as one can see:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
bluetoothLeScanner.startScan(null, new ScanSettings.Builder().
setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.setReportDelay(0)
.build(), bleScannerCallback);
ok = true;
} else {
ok = mBluetoothAdapter.startLeScan(bleScannerCallbackPreLollipop);
}
return ok;
the bleScannerCallback implements the callback to receive the onScanResult().
What I'm experiencing right now is:
with some device, for example Samsung S6 with Android 5.1.1, within each scan cycle all devices in the proximity are received, i.e. onScanResult() runs with all BLE advertisements.
With some other device, like Tablet Nexus 9 with Android 7.0, everything runs fine for about 20 seconds, then for about 17/18 seconds NO advertisement is delivered through onScanResult(), then again ok for about 20 seconds, then again a break for about 17/18 seconds.
This process repeats infinitely, even with some regularity, it does not even seem random...
Is this a known issue?
It seems to appear specially with Android >= 6, our Samsung S6 with Android 5.1.1 runs all the times perfectly.
Obviously all the required Android permissions are in place, elsewhere it would not even run once.
It seems to me more a "feature" on the Android device to save some battery, unfortunately we need to scan continuously, at least in some condition, not really 24/7 but even not with about 20 seconds break every 20 seconds.
In the case this is the way it shall go in the future, does anyone know any other solution that allows to scan BLE devices continuously? Independently on the Android version...anyway the newer versions like >= 6.
Related
I am trying to perform BLE scans in a foreground service (Android 12) after some start scan end stop scan I receive the error SCAN_FAILED_APPLICATION_REGISTRATION_FAILED, I cannot figure out what is the reason of the error.
I do startScan and stopScan in sequence (the scan is a long-running scan)
The app target SDK 32 so I have the new Bluetooth permissions.
This is the complete scenario:
I open the app and I start the foreground service
I close the application (the foreground service is still running)
The foreground service scan and connect to my device and then I stop scanning
I turn off my BLE device and the foreground service start to scan again
I turn on my ble device and the foreground service detect it and connect to it.
Repeat step 4 and suddenly the error SCAN_FAILED_APPLICATION_REGISTRATION_FAILED show.
I also notice this error in the logcat App 'com.sample.xxx' is scanning too much clientif (I look into AOSP source code and seems that this string is specific of Xiaomi ROM)
https://github.com/appersiano/TestingBackgroundConnection
P.S. Tested on Xiaomi Mi11 Lite 5g
How can I solve?
you should not scan more than 5 times in 30s.
GattService:
This constant defines the time window an app can scan multiple times.
Any single app can scan up to |NUM_SCAN_DURATIONS_KEPT| times during
this window.
Once they reach this limit, they must wait until their earliest recorded scan exits this window.
static final int NUM_SCAN_DURATIONS_KEPT = 5;
static final long EXCESSIVE_SCANNING_PERIOD_MS = 30 * 1000;
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.
I have an Asus P00A tablet (Android 7.0, API24) on which the BLE stops after some hours. (This affects any BLE app, not just my app using Android Beacon Library). Apps start working again if I manually switch off BLE then switch it back on.
The BluetoothMedic auto-fix system did not work for my tablet. It runs every 15 minutes but does not find a fault and so does not "power cycle" the Bluetooth. However, I hacked the BluetoothMedic class, adding this:
public void cycleBluetooth(Context context) {...}
and attached this to a button. I find this will restore BLE functionality. So I wondered what would happen if I unconditionally reset the BLE every 15 minutes. I added:
public static final int ALWAYS_RESET = 4;
and then call medic.enablePeriodicTests(context, BluetoothMedic.ALWAYS_RESET);
and add code inside BluetoothTestJob.onStartJob() which then calls BluetoothMedic.cycleBluetooth(). This behaves as expected and so far my app has run perfectly for 18 hours.
I am interested in any advice, such as:
1 Are there any tests other than the two in BluetoothMedic that I can run to detect that my tablet's Bluetooth has stopped? (I am happy to experiment).
2 Any comments on the hack I describe above? Should it be OK to unconditionally reset the Bluetooth every 15 minutes?
3 If the Bluetooth is reset ("power cycled") then is the rest of the Android Bluetooth Library OK with this? That is, will it carry on with monitoring and ranging that has been previously set up, or does the application code need to set take any action to get things going again? Note that this would apply to resets by the existing enablePowerCycleOnFailures() code as well as my ALWAYS_RESET hack above. (Maybe there are some crashes that could happen if the power cycling came at the wrong time?).
4 Could I suggest adding a callback so the application can learn if the Bluetooth has been cycled? Perhaps as a parameter to enablePowerCycleOnFailures()
5 I understand that background activities can be stopped by the OS, especially with Android 8. Would this also affect the regular 15 minute tests set up by enablePeriodicTests()?
The Android Beacon LIbrary's BluetoothMedic, as currently built, relies on the operating system's error code returned by a scan failure (or an advertising failure) to decide if the bluetooth stack is in a bad state warranting a power cycle.
For scans, if the onScanFailed callback is called with an error code of SCAN_FAILED_APPLICATION_REGISTRATION_FAILED which has the value of 2, the module considers it worthy of a power cycle..
For advertisements, if the onStartFailed callback is called with an error code of ADVERTISE_FAILED_INTERNAL_ERROR which has a value of 4, the module considers it worth of a power cycle..
These values were determined via experimentation, witnessing that on some devices, once an error callback is called with these values, bluetooth on the device would not work again without turning it off and back on. You can see the discussion of this in this thread.
You may want to see if there are other error codes on the Asus P00A that indicate a problem worthy of cycling bluetooth. To do this, wait for a failure, and see if attempts to start scanning call the onScanFailed callback with a distinct error code. If such error codes exist, this would be a better solution than cycling power to bluetooth regularly, as cycling power to bluetooth does break BLE GATT connections and the operation of bluetooth classic functions like speakers. The Android Beacon Library itself recovers from these power cycles just fine, although it will obviously not detect beacons until bluetooth is back on.
Because the BluetoothMedic uses the Android Job Scheduler for periodic tests, it is not affected by background limitations on Android 8+.
If you are interested in augmenting these functions in the library, please feel free to open an issue in the Github repo, and issue a Pull Request if you have code to share.
I have implemented a receiver within my app that continually searches using Bluetooth and updates the server every 10 seconds with the available devices. One piece of information it returns is the RSSI value of the device. The code I use to get this value is:
node.Rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE);
So this seemed to work fine at first, using my test phone (Galaxy S4) I got expected values such as -75 or -88. However I have now tried a different phone shown in the link below:
http://www.amazon.co.uk/Waterproof-smartphone-Dustproof-Shockproof-Capacitive/dp/B00EZNSUPU/ref=pd_sim_sbs_ce_3?ie=UTF8&refRID=0YYHYTW6J2AVZK7DJJDJ
(Sorry if I'm not allowed to post this here, I'm not 100% on the rules, if there's a problem I'll happily remove it)
When using this phone with the same build of my app, the values for the RSSI range from 150-200.
Does anyone have any idea why this might be happening? Another problem I'm having with the phone is that when Bluetooth is on my Wi-Fi connection will eventually disappear, again this doesn't happen with my Galaxy S4. Is this test phone really that bad? Or am I missing something..
I'm working on a research project which involves Bluetooth and the Android OS. I need to make Bluetooth discoverable indefinitely in order for the project to continue.
The Problem:
Android limits discoverability to 300 seconds.
I cannot ask the user every 300 seconds to turn discoverability back on as my application is designed to run in the background without disturbing the user.
As far as I am aware, there is no way to increase the time though Android's GUI. Some sources have called this a safety feature, others have called this a bug. There may be a bit of truth in both...
What I'm Trying / Have Tried:
I'm trying to edit a stable release of cyanogenmod to turn the discoverability timer off (it's possible; there's a configuration file that needs to have a single number changed). This isn't working because I'm having verification problems with the resulting package.
During the past week, I downloaded the cyanogenmod source code, changed a relevant class in the hope that it would make Bluetooth discoverable indefinitely, and tried to recompile. This did not work because (a) the repo is frequently changed, leading to an unstable code base which fails to compile (OR, it could be that I'm using it incorrectly; just because it looked like it was the code's fault in many instances doesn't mean I should blame it for all the problems I encountered!) and (b) the repo decides to periodically "ignore" me (but not always, as I have gotten the code base before!), replying to my synchronization/connection attempts with:
fatal: The remote end hung up unexpectedly
As you might imagine, the above two issues are problematic and very frustrating to deal with.
More Info:
I'm running Android 2.1 via cyanogenmod (v5 I believe). This means the phone is also rooted.
I have a developer phone, which means that the bootloader is unlocked.
My phone is an HTC Magic (32B).
The Big Question:
How can I make Bluetooth indefinitely discoverable on Android?
See the following link:
http://developer.android.com/guide/topics/wireless/bluetooth.html#ConnectingDevices
Specifically, the last sentence in the paragraph below:
Enabling discoverability
If you would like to make the local device discoverable to other devices, call startActivityForResult(Intent, int) with the ACTION_REQUEST_DISCOVERABLE action Intent. This will issue a request to enable discoverable mode through the system settings (without stopping your application). By default, the device will become discoverable for 120 seconds. You can define a different duration by adding the EXTRA_DISCOVERABLE_DURATION Intent extra. The maximum duration an app can set is 3600 seconds, and a value of 0 means the device is always discoverable.
So, this should work:
Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 0);
startActivity(discoverableIntent);
If you check out the BluetoothAdapter class
you will find the hidden method:
public void setDiscoverableTimeout(int timeout)
Now you only have to find out how to use it. You have to do a method invocation to do so.