BLE Scan in background service? - android

I am trying to run a background service which runs all the time and scans for BLE advertisements.
I started a service. It works as expected and runs all the time. When restarting the background service (after closing the activity), I am starting the BLE scan:
mBluetoothLeScanner = BluetoothAdapter.getDefaultAdapter().getBluetoothLeScanner();
ScanFilter.Builder builder = new ScanFilter.Builder();
builder.setManufacturerData(0x0590,new byte[]{});
ScanFilter filter = builder.build();
filters = new ArrayList<ScanFilter>();
filters.add( filter );
ScanSettings settings = new ScanSettings.Builder()
.setScanMode( ScanSettings.SCAN_MODE_LOW_LATENCY )
.build();
// mBluetoothLeScanner.stopScan(mScanCallback);
mBluetoothLeScanner.startScan(filters, settings, mScanCallback);
It works for 30-50 seconds. After that time, the scan callback does not send advertisement data.
Do you have any idea why it is stopped and what can I do? Is iBeacon or Eddystone a better solution? My intention is to continuesly listen for a message from a BLE device in the background while the app is closed. I am using android 8.1 with Xiaomi Redmi 5.

After android 8 google add some Background limits you can read it
As i know you have two option one is use JobScheduler an other way use ForgroundServices(I use this)

Related

BLE scan is not working when screen is off on Android 8.1.0

I am using pixel with latest android 8.1.0 update.
I am facing issue related to BLE advertisement scanning. Whenever I turned off the screen(i.e power button press) my scanning will stop.
it will restart immediately after turn on the screen.
I have checked latest code for BLE. google newly introduce this feature (Reference Link).
Is there any way to skip this part, I mean scan should not stop regardless of the screen on or off.
As of Android 8.1, unfiltered bluetooth scans are blocked when the screen is turned off. While it is surprising for such a dramatic change to be made in a minor release of Android, this is certainly an intended change based on the comments in the commit:
Stop unfiltered BLE scans when the screen goes off.
The workaround is to use a ScanFilter with all scans. The new 8.1 operating system code simply verifies that any scans active when the screen is off have at least one scan filter. If those conditions are met the scan results are delivered as in Android 8.0.x and earlier.
In order to set up such a scan, you must use the APIs introduced in Android 5.0 and create a ScanFilter with each scan. Below is a filter that will find manufacturer advertisements for any device from Apple with manufacturer ID 0x004c (this will include iBeacons):
ScanFilter.Builder builder = new ScanFilter.Builder();
builder.setManufacturerData(0x004c, new byte[] {});
ScanFilter filter = builder.build();
Similarly, if you are interested in GATT Service advertisements (like the kind used with Eddystone beacons) you can search for a GATT Service UUID with a filter like this:
ScanFilter.Builder builder = new ScanFilter.Builder();
String serviceUuidString = "0000feaa-0000-1000-8000-00805f9b34fb";
String serviceUuidMaskString = "FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF";
ParcelUuid parcelUuid = ParcelUuid.fromString(serviceUuidString);
ParcelUuid parcelUuidMask = ParcelUuid.fromString(serviceUuidMaskString);
builder.setServiceUuid(parcelUuid, parcelUuidMask);
ScanFilter filter = builder.build();
If needed, you can add multiple filters to a single scan, and any that match will return results. The only real limitation here is that you must know all of the manufacturer codes or all of the GATT Service UUIDs that you might match up front, at least when scanning with the screen off.
You start your scan with code like this:
bluetoothAdapter.getBluetoothLeScanner().startScan(filters, settings, scanCallback);
EDIT: It is also possible to do this with an empty ScanFilter that looks like this:
ScanFilter.Builder builder = new ScanFilter.Builder();
ScanFilter filter = builder.build();
If you use such a scan filter, it will match any advertising packet, and still allow detections with the screen off on Android 8.1, effectively giving you the same behavior on Android 8.0.x and earlier.
EDIT 2: On the Galaxy Note 9 with Android 8.1 and perhaps other Samsung devices with 8.1, scans are blocked with the screen off even with an empty scan filter. Scans are allowed with the screen off with a non-empty scan filter as described above.
I faced the same issue. I had Scan filters in order to scan BLE devices even if the screen were locked. But on Samsung devices it didn't work, so I search on Samsung forum and I discovered Knox SDK (https://seap.samsung.com/sdk/knox-android).
And it was the solution of my problem. All you have to do is add it to your app, create a license and activate it and finally use this method addPackageToBatteryOptimizationWhiteList to unlock the scan when the Samsung device screen is lock.
Obviously not, unless they missed something. But it will still work in the background if you have scan filters, which you should have anyway. So is it really an issue?
in android 11
scanFilter can't being null
you need to set something then will working
like:
List<ScanFilter> filterList = new ArrayList<>();
filterList.add(new ScanFilter.Builder().setDeviceAddress(address).build());
BluetoothAdapter.getBluetoothLeScanner().startScan(filterList, scanSettings, scanCallback);

Android Wear: BleLEScan is draining my battery

I am developing an app which needs data from the wear.
One of the type of data is BLE.
I am using BleLeScan:
BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
if (Build.VERSION.SDK_INT >= 21) {
mLEScanner = mBluetoothAdapter.getBluetoothLeScanner();
settings = new ScanSettings.Builder()
.build();
filters = new ArrayList<ScanFilter>();
}
mLEScanner.startScan(filters, settings, mScanCallback);
BLELeScan is draining my wear's battery. I know this because i tested with BLE scan and without it. The difference is huge, without BLE scan battery consumption per hour is about 3%, whereas with BLE scan it is >16%.
How can I optimize battery consumtion for BLELeScan? I need to run the scan continuously.
You should set a scan filter to match only what you are looking for. Otherwise the cpu will be woken up when not needed.
Also note that power consumption depends highly on which Android device and Bluetooth chip you have.
If you are only looking for a particular paired device to connect to, consider connecting directly to it with autoConnect = true.
Apparently, as pointed out in answers and comments, BLE scan consumes a lot of battery power. How much does it consumes? It will depend on the device.
I optimized battery consumption of the application by running the scan for 12sec/min and then stopping the scan for next 48sec. After 48sec I am restarting the scan.
Note, I am not making any supporting object null, I am just stopping the scan after 12sec.
This approach helped me save battery consumption by more than 50%.

BLE Advertise 32-bit Service-UUID in Android Oreo

I have a small app which just performs a BLE Advertising.
The app runs on a Nexus 5x with Android 8.0
This is the code to start BLE advertising:
private fun startAdvertising() {
val serviceUuid = ParcelUuid.fromString("DAB5D1DC-0000-1000-8000-00805F9B34FB")
val data = AdvertiseData.Builder()
.setIncludeTxPowerLevel(false)
.setIncludeDeviceName(false)
.addServiceUuid(serviceUuid)
.build()
val settings = AdvertiseSettings.Builder()
.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH)
.setConnectable(true)
.build()
bluetoothLeAdvertiser!!.startAdvertising(settings, data, advertiserCallback)
}
The advertising starts, but the payload is wrong. On Pre-Android 8.0 devices we receive the correct service-uuid when scan for this messages with a 2nd device:
32-bit Service-UUID: 0xDAB5D1DC
But when I start the advertisements on my Nexus 5x with Android 8.0, I receive an incorrect service-uuid:
32-bit Service-UUID: 0x0000D1DC
For the BLE scanning part I use the nrf Connect app from playstore.
Everything works as expected, if I advertise a common 128-bit Service UUID and not an 32-bit one.
Are there any changes for Android 8.0 regarding my issue?
Update 2017-08-28:
Same issue on a Nexus 6P. Created an issue: https://issuetracker.google.com/issues/65099899
Use only ParcelUuid.fromString("DAB5D1DC")
this will make youre packet smaller.
youre problem is probably because youre advertise packet too big i think the max is 32 bytes.
Create Constants
public static final UUID serviceUuid = UUID.fromString("DAB5D1DC-0000-1000-8000-00805F9B34FB");
and than
val data = AdvertiseData.Builder()
.setIncludeTxPowerLevel(false)
.setIncludeDeviceName(true)
.addServiceUuid(new ParcelUuid(Constants.serviceUuid))
.build()

Scanning using BluetoothLeScanner calls onScanResult multiple times for the same device

I'm implementing a simple advertise + scan functionality using BLE on android, and for some reason I get a lot of calls to the onScanResult callback passing the same device.
For advertising:
//Advertise settings build
AdvertiseSettings.Builder builder = new AdvertiseSettings.Builder();
builder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY);
builder.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH);
builder.setConnectable(true);
AdvertiseSettings advSettings = builder.build();
//Advertise data build
AdvertiseData.Builder advDataBuilder = new AdvertiseData.Builder();
advDataBuilder.addServiceUuid(ParcelUuid.fromString(SFGattAttributes.SERVICE));
AdvertiseData advertiseData = advDataBuilder.build();
//Start Advertising
bluetoothLeAdvertiser.startAdvertising(advSettings, advertiseData, advertiseData, new BLEAdvertiserCallback());
For scanning:
BluetoothManager bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
bluetoothAdapter = bluetoothManager.getAdapter();
bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
bluetoothLeScanner.startScan(getScanFilters(), getScanSettings(), new BLEScanCallback());
The only difference each time seems to be a difference in the rssi value.
Is there a way to avoid this multiple calls?
This is actually a feature and can be for used ranging a (advertising) BLE device. There are also (older) devices that don't get multiple scan results for a specific device during a scan cycle. This then causes problems for ranging other devices including BLE beacons. The multiple calls also let you know (over time) that the device is still reachable/accessible.
So if you don't want the multiple calls just ignore the calls for known devices (MAC addresses). It can not be deactivated.
Keep in mind that many devices (especially phones) change their mac address. Some even every 2 minutes. It's not easy to map the new mac address to the old device (old mac address). You have to handle the behaviour accordingly.

Android BLE reconnection very slow

Background:
I have a BLE peripheral with two modes: "Application" and "Bootloader". In both modes, the device advertises with the same MAC address.
To switch from one mode to the other, the BLE peripheral must reboot itself. In doing so, it has to disconnect any active BLE connection.
The BLE peripheral only stays in Bootloader mode for about 5 seconds. If nobody connects to it within that window, it switches to Application mode.
The Problem:
Android takes a very long time to reconnect to the BLE device, long enough that I'm missing the 5 second window. The raw code has a few layers down to the BluetoothGATT and BluetoothAdapter layers, but the sequence of calls boils down to:
BluetoothGattCharacteristic c = mCharacteristics.get(POWER_STATE_UUID);
c.setValue(SHUTDOWN_VALUE);
mBluetoothGatt.writeCharacteristic(c);
// Signalled by BluetoothGattCallback.onCharacteristicWrite
bleWriteCondition.await();
mBluetoothGatt.disconnect();
// Wait for the underlying layer to confirm we're disconnected
while( mConnectionState != BluetoothProfile.STATE_DISCONNECTED ) {
// Signalled by BluetoothGattCallback.onConnectionStateChange
bleStateCondition.await();
}
mBluetoothGatt.connect();
while (mConnectionState != BluetoothProfile.STATE_CONNECTED) {
// Signalled by BluetoothGattCallback.onConnectionStateChange
bleStateCondition.await();
if (bleStateCondition.stat != 0) {
break;
}
}
Am I going about this entirely the wrong way? I've tried calling close() on the BluetoothGatt instance, then generating a new one with BluetoothDevice.connectGatt, but I get the same extremely slow behavior.
I'm testing on a Samsung Galaxy S4, API level 21.
The problem here is that the gatt connect call issues a background connection request. It can take quite a long time for this call to result in a connection. A description of the two types of connection request is here : Direct vs Background connections
The absolute fastest way to get a connection is to do a scan and upon finding your device issue a direct connection request to it. As the scan has just found it, you know it is there and the connection will complete quickly. This is more complicated than your example code, but will be most effective given your small window. A scan is the most aggressive way to find a device. However, if you already have the device object, you could just call a direct connection request on the device.
Scans are issued using code like this :
scanner = bluetoothAdapter.getBluetoothLeScanner();
settings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.build();
filters = new ArrayList<ScanFilter>();
ScanFilter uuidFilter = new ScanFilter.Builder()
.setServiceUuid(YOUR_SERVICE_UUID).build();
filters.add(uuidFilter);
scanner.startScan(filters, settings, myScanCallback);
Upon finding your device (using the scan callback), issue a direct connection request via this method call :
myGatt = myDevice.connectGatt(this, false, myGattCallback);
The key part being the parameter of false. The connection request will time out in around 30s if the device is not found.

Categories

Resources