There seems to be a lot of "hidden features" regarding Bluetooth scanning on Android.
For starters there is a "30s limit" (Android 7.0 ble scan no result). Then you have to set a ScanFilter in background mode otherwise you get no results (can't find a reference for this one).
Most recently I discovered that I don't get any scan results when I enable scanning using the following scan mode with the screen turned off (after 30s or so) on Android 10 (I have observed this behavior on Google Pixel 3 and Google Pixel 4, it works fine on older Androids):
/**
* Perform Bluetooth LE scan in balanced power mode. Scan results are returned at a rate that
* provides a good trade-off between scan frequency and power consumption.
*/
public static final int SCAN_MODE_BALANCED = 1;
There is another scan mode (which I have not tried yet):
/**
* Perform Bluetooth LE scan in low power mode. This is the default scan mode as it consumes the
* least power. This mode is enforced if the scanning application is not in foreground.
*/
public static final int SCAN_MODE_LOW_POWER = 0;
Question: Should I interpret this comment "is enforced" as enabling scanning with something other than SCAN_MODE_LOW_POWER will give me no scan results? Can somebody confirm this?
I will investigate further on my own, but it takes time...
Note: I have a foreground service and I can see in the ADB logs that the scanner is enabled / disabled periodically. But I don't get any scan results...
Update: I've now made sure to use SCAN_MODE_LOW_POWER when in background mode but still I get no results. I have no idea what's going on.
Update 2: I tried running an older version of the app (not compiled for Android 10) and that worked fine
Update 3: I disabled battery optimizations for the app just in case. This did not help:
Go to Settings > Apps > Your app > Advanced > Battery > Battery optimization
Change view to All apps
Search for your app
Choose Not optimized
Android 10 requires new permissions in order to get BLE scans to return results. Unfortunately, the scan will simply not return results instead of logging a warning. The new permissions are ACCESS_FINE_LOCATION instead of ACCESS_COARSE_LOCATION required for sdk versions 23 through 28 and if you need to perform BLE scans while the app is in the background (even if you have a foreground service) you will need ACCESS_BACKGROUND_LOCATION. You must list these permissions in the manifest and prompt the user to grant them at runtime. There's a similar question here with a little more info in the answers: Android 10 not working with BLE Bluetooth scanning
Related
I am working on a part of an indoor localization project. I need my Flutter application scan and get Mac addresses and RSSI values of Wifi and Bluetooth devices. I am using flutter_blue for bluetooth scans and wifi_scan for Wifi scans. The application works well while in the foreground but scans are failing when the application is in background.
What I have tried so far:
Tried to change Bluetooth scan mode from ScanMode.lowLatency to ScanMode.lowPower and ScanMode.opportunistic for the startScan() method for flutter_blue.
flutter_background. I managed to do some tasks in background but wifi and bluetooth did not work.
flutter_background_service the same situation
flutter_foreground_plugin the same situation
flutter_foreground_task the same situation
workmanager did not try it because it says it can trigger the bg operation per every 15 minutes but I am scanning per every 6 seconds.
Is there any way to achieve this? Aren't there any package with the option backgroundMode: true? I don't know the natives. Don't know Kotlin too.
According to the Android Developer documentation, ScanMode.lowPower is the only possible mode for background scanning. You also need to request location access for the background (documentation here and here).
Consider switching to Flutter Blue Plus instead of Flutter Blue; Flutter Blue Plus is the currently-maintained version. This pull request on GitHub shows that Flutter Blue cannot run in the background. The pull request has never been merged.
After a lot of things have been tried, I just tried to get GPS location too.
Used this package: location
And execute location.enableBackgroundMode(enable: true) as it is stated in readme, then my wifi and bluetooth scans starts working in background too.
It's Ridicoulus
I'm developing a tracking application and I need to prevent users from turning off the basic sensors used to determine the location. I can not modify the devices ROM or have root access (or at least it would be very desirable to had not), but I thought of using the Device Administration API to perform these functions through the Profile Owner or Device Owner modes. I'm basically looking for a method to block these functions in Android settings.
I'm unsure about whether this is possible and how to do it, I have not found examples in GitHub for applications that have implemented this. Could anyone give me a light, some example or specific documentation?
I tried to follow these three documentations, without success in finding a solution to this specific feature:
https://source.android.com/devices/tech/admin
https://developer.android.com/guide/topics/admin/device-admin
https://developers.google.com/android/management/introduction
This is an excerpt from what I've been trying:
setUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, true);
setUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS, active);
setUserRestriction(UserManager.DISALLOW_CONFIG_BLUETOOTH, active);
private void setUserRestriction(String restriction, boolean disallow){
if (disallow) {
mDevicePolicyManager.addUserRestriction(mAdminComponentName, restriction);
} else {
mDevicePolicyManager.clearUserRestriction(mAdminComponentName,
restriction);
}
}
DISALLOW_CONFIG_BLUETOOTH
Added in API level 18
public static final String DISALLOW_CONFIG_BLUETOOTH
Specifies if a user is disallowed from configuring bluetooth. This does not restrict the user from turning bluetooth on or off. The default value is false.
This restriction doesn't prevent the user from using bluetooth. For disallowing usage of bluetooth completely on the device, use DISALLOW_BLUETOOTH.
This restriction has no effect in a managed profile.
Key for user restrictions.
Type: Boolean
You cannot prevent them from turning GPS, WIFI and Bluetooth off. What you can do is have an implementation as below or use this library.
https://github.com/KI-labs/gps-permission-checks-livedata
You can't, obviously for security reasons. If you want to achive something like that you'll probably need to modify the devices ROM. You should create a BroadcastReceiver and keep tracking Internet and Bluetooth connection changes, than you can properly handle it when user disconnect them pausing the service, showing a dialog, finishing the application or whatever you need to do.
It would be pretty weird if an app could have some control of user settings, imagine if you install an app, then suddently you can't disable wi-fi anymore until you unistall it. You can't do that for a good reason
Preventing bluetooth/wifi disconnection will also prevent usage of aircraft mode, that is a security issue bounded in the ROM and not overridable.
As suggested above your option is to monitor for wifi/bluetooth/gps deactivations and prompt the user with an alert.
By the way, GPS is not affected by aircraft mode, as it's a pure receiver and doesn't make active transmissions. In that case GPS will be always active and collecting informations (if active and the phone is not in power save mode, aka relying on wifi location). I suggest you to check if the user activated aircraft mode, in order to be less annoying with your alerts (air mode is mandatory in same situations, and should be considered "legal" by your application, and maybe less critical than an user voluntary disconnection
In simple words, You cannot, but you can listen to when wifi is enabled/connected, and you can prompt a dialog stating the reason.
This way it gives the user a more concise grip on what needs to be done.
Just a suggestion
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'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;
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.