Android Wear: BleLEScan is draining my battery - android

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%.

Related

how to make BLE autoconnect to Bluetooth of android phone without pairing

I am having a Arduino with BLE which has to send some data to any/all android phones over Bluetooth in it's range. My android phone should have a app which i intend to make will notify about data received.
How can i make such android app which auto-connects to any nearby BLE , if found without pairing even for first time and exchange data. I mean how in any application i can implement auto-connect without key pairing.I found that setting autoconnect=true will do this task , but i am not sure.
Any help, even some resource i will refer and clear my doubts.
The pre-requisites and steps are (code snippets in Java):
HC-XX module or similar BLE-device on the Arduino-side set to security mode 1 and security level 1 (no security AND no pairing)
Android 4.3 (API level 18) with built-in platform support for Bluetooth Low Energy (BLE)
Check on the device (mobile) that BLE is enabled
// Ensures Bluetooth is available on the device and it is enabled. If not,
// displays a dialog requesting user permission to enable Bluetooth.
if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
Find the BLE device(s). You use the startLeScan() method. This method takes a BluetoothAdapter.LeScanCallback as a parameter. You must implement this callback, because that is how scan results are returned. Because scanning is battery-intensive, you should observe the following guidelines:
As soon as you find the desired device, stop scanning.
Never scan on a loop, and set a time limit on your scan. A device that was previously available may have moved out of range, and continuing to scan drains the battery.
If you want to scan for only specific types of peripherals, you can instead call startLeScan(UUID[], BluetoothAdapter.LeScanCallback), providing an array of UUID objects that specify the GATT services your app supports.
The first step in interacting with a BLE device is connecting to it— more specifically, connecting to the GATT server on the device. To connect to a GATT server on a BLE device, you use the connectGatt() method. This method takes three parameters: a Context object, autoConnect (boolean indicating whether to automatically connect to the BLE device as soon as it becomes available), and a reference to a BluetoothGattCallback.
// Here we set autoconnect to true
bluetoothGatt = device.connectGatt(this, true, gattCallback);
To sum up auto connect alone will not do the job as you want no pairing. So security mode 1 and security level 1 (no security at all) has to be set. So make sure by using software sided encryption/auto sign-in that no unauthorized persons use your device
Read more about BLE in Android in detail here
Read more about BLE security in detail here

Unlock Linux with Bluetooth low energy

Have you ever seen the apple watch unlock a Mac? The idea is amazing, but I don't want a smart watch because I already have a phone which has similar capabilities AFAIK.
Also, I moved from OSX to Linux recently :)
I don't know how the Apple watch manages to unlock the Mac. But I know what would be desirable from a user experience point of view:
Needs to unlock quicker than I type my password
Should work in absence of wifi/4G
Should be power efficient
RFID
RFID would be nice, but no laptops that I know embed RFID readers.
Bluetooth based proximity detection
The challenge with classic Bluetooth is the requirement to constantly scan for near devices to measure the signal strenght (RSSI) from which we can infer the proximity.
Unfortunately discovery hops and listens 40 channels. And anyway the phones stop broadcasting when screen is off for a while. This is not good enough. I know because I tried:
import collection.JavaConverters._
import tinyb._
object Listener extends App {
var running = true
val BT_ADDR = sys.env.getOrElse("BT_ADDR", "XX:XX:XX:XX:XX:XX")
val BT_RSSI_DBM_THRESHOLD = Integer.parseInt(sys.env.getOrElse("BT_RSSI_DBM_THRESHOLD", "-65")).toShort
val manager = BluetoothManager.getBluetoothManager
val lock = new Object
while (true) {
manager.getAdapters.forEach(a => {
a.setRssiDiscoveryFilter(BT_RSSI_DBM_THRESHOLD)
a.removeDevices()
})
System.err.println("scanning for " + BT_ADDR + " at minimum " + BT_RSSI_DBM_THRESHOLD + " dBm RSSI...")
manager.startNearbyDiscovery(
(device: BluetoothDevice) => {
if (BT_ADDR.equals(device.getAddress)) {
onProximity(device)
manager.stopNearbyDiscovery()
lock.synchronized(lock.notify())
}
else println(device.getName)
}
, 1000
, false
)
lock.synchronized(lock.wait())
}
}
I was looking at BTLE (Bluetooth Low Energy), and I'm having difficulty to understand the following:
Is there a way to establish from Linux a single low energy bluetooth connection to the Android phone which we can leave dormant all the time, and use it to wake the phone up and make it transmit some packets (so we can measure its RSSI power and infer proximity) on demand, only when strictly needed.
I.e. we'd limit transmissions to only these rare occasions:
Check when the user is away if we detect inactive mouse & keyboard for 1 minute,
Check if the user is near enough when GDM is active
No BT activity whatsoever otherwise
This approach is quick, energy efficient, and does not require network protocol, only some rare BT transmission.
But is this possible with Bluetooth LE? Any pointers to examples?
Yes this should be possible with Bluetooth Low Energy (with some caveats) as follows:-
You need a BlueZ script/C program to constantly scan for your Android device.
You need your phone's Bluetooth to always be turned on.
You will need to pair at least once so that your Linux machine recognizes the changeable Bluetooth address of your Android device (see referenced links).
The BlueZ script program should be written so that as soon as your Linux system goes to standby, the program is launched as a daemon or background process that just starts scanning for Android devices and read their RSSI values. If your device is found and the RSSI value indicates that it is within range, this process will signal the Linux OS to wake up.
The caveats:-
BLE is not ideal for positioning/locationing; you can probably detect
if you're a few metres away but it would be challenging to get an
accuracy of a few centimeters.
Your BlueZ script needs to be
constantly running as a daemon or background process, so if it is
somehow killed or is inactive when the device goes to sleep, this
will not work.
Bluetooth on your phone should be always on, which
shouldn't have a big impact on the battery life but is also not
recommended.
Some resources for you:-
Running Bluetooth applications in the background in Linux
Bluetooth Low Energy: A Primer
Getting Started with Bluetooth Low Energy
Introduction to BLE
Bluetooth LE Signal Strength in Linux
Should One Create a Bond with a Bluetooth LE Device
How to Detect Whether System is Going to Standby in Linux
Android Bluetooth Low Energy Overview
Using Bluetooth Low Energy in Linux Command Line
It will not be a straight forward process and you'll probably have to try and fail along the way, but it will be a learning experience and you should be able to achieve what you want in the end.
I hope this helps.

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);

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