Android BLE Device Button press Alert in android - android

I am working with BLE device.I perform the code using android GATT and it scan device and also I send immediate alert in BLE device from mobile using writeCharacteristic and BLE device is beep.But now I want to Alert in android Mobile when I press BLE device button. That exactly I don't know how to do.
Thank You.

For this you have to listen for characteristics change. When you will press the BLE button, a characteristics must be change inside its hardware (set any flag value depends on hardware's firmware coding). When characteristics changed, characteristics method will called. You can perform required functionality there.
#Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic
characteristic)
{
Here we received ble button pressed event callback. Do stuff here.
}
For receiving characteristics change callback first you have to enable the notification like this.
BluetoothGattCharacteristic characteristic = gatt.getService(mServiceUuuid).getCharacteristic(mCharOneUuuid);
gatt.setCharacteristicNotification(characteristic, true);
List<BluetoothGattDescriptor> list = characteristic.getDescriptors();
for (int i = 0; i < list.size(); i++) {
BluetoothGattDescriptor desc = list.get(i);
desc.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(desc);
}

Related

Scanning for BLE devices in a foreground service

I am creating an app that can detect and exchange information with BLE devices similar to iBeacons. These devices send an advertisement every 1000ms.
Currently, I have a foreground service that runs some logic, which includes scanning for BLE devices periodically (e.g. once every ten seconds, I run the scanner for 2 seconds). Once I push the app to the background, however, the BLE Scanner changes behaviour, scanning very infrequently. From what I can understand, this is because the OS forces the scanner to use the low power scan mode.
The problem lies in the fact that the scan window is shorter than the BLE Device advertising intervals, so I tend to miss packets. Is there a way to ensure the scanning is able to run for at least this interval, perhaps via scan mode (low latency or balanced) or some other trick?
I am using a Samsung Galaxy Tab S2 running Android 7.0.
Edit: Added some sample code (Note: I am using Nativescript, so what you see here is a conversion of the Nativescript code to Java, to the best of my understanding.)
I start the service with
Intent intent = new Intent(this, com.my.service.MyService);
startService(intent);
Service class functions
#Override
public int onStartCommand(Intent intent, int flags, int startId){
Notification notif = new Notification.Builder(this)
.setContentInfo("My App")
.build()
this.startForeground(12312, notif);
runMyScanner();
this.super.onStartCommand(intent, flags, startId);
return Service.START_STICKY;
}
public void onDestroy(){
this.stopForeground(0); // Corresponds to a flag.
}
Scanner
private int mNumScanned = 0;
public void runMyScanner(){
List<ScanFilter> scanFilters = new ArrayList<>();
scanFilters.add(new ScanFilter.Builder().build())
ScanSettings scanSettings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.build()
ScanCallback scanCallback = new ScanCallback(){
#Override
public void onScanResult(){
mNumScanned++;
}
}
mAdapter.getBluetoothLeScanner().startScan(scanFilterList, scanSettings, scanCallback);
}
(Note: After scanning, I print out mNumScanned, which has allowed me to investigate the issue. In addition, I stop the scan some number of seconds after starting, but use the javascript method setTimeout() to do so)

React Native receiving BLE notifications in background

I am currently working on a prototype trying to listen to bluetooth LE notification while in background mode.
I am using this library https://github.com/innoveit/react-native-ble-manager to interact with BLE devices. The idea is to be able to register to a BLE service on a peripheral and get notified on characteristic changes while in background.
const deviceId = '8A:B0:C3:BA:11:55';
const service = '0ce0';
const characteristic = '0ce2';
componentDidMount() {
BleManager.start({
showAlert: false
});
}
onButtonPress() {
BleManager.connect(deviceId).then(() => {
BleManager.retrieveServices(deviceId).then(() => {
BleManager.startNotification(deviceId, service, characteristic);
});
});
}
handleUpdateValueForCharacteristic(data) {
console.log('characteristic update', data);
}
this.handlerUpdate = bleManagerEmitter.addListener(
'BleManagerDidUpdateValueForCharacteristic',
this.handleUpdateValueForCharacteristic
);
From my understanding, with this code, every time the characteristic change on the BLE device, the app will get notified even in background mode and handleUpdateValueForCharacteristic will be triggered.
Is is possible to have multiple app listening to the same service and characteristic on the same BLE device?
Will all these work while background and sleep mode?

Reading bluetooth low energy properties (reading, notifications) - how it works?

I'm analyzing a sample from android, which is explaining a bluetooth low energy usage on android. I've found the following code, which is setting a notification, but I can't get what is happening here with using a properties integer and conditions inside ifs. Could someone explain it a little bit?
Anyway, maybe you have some better source, which can explain a ble concept on android- what and how is working here? The official android tutorial is really poor, and bluetooth official page is giving almost nothing...
#Override
public boolean onChildClick(ExpandableListView parent, View v, int groupPosition,
int childPosition, long id) {
if (mGattCharacteristics != null) {
final BluetoothGattCharacteristic characteristic =
mGattCharacteristics.get(groupPosition).get(childPosition);
final int charaProp = characteristic.getProperties();
if ((charaProp | BluetoothGattCharacteristic.PROPERTY_READ) > 0) {
// If there is an active notification on a characteristic, clear
// it first so it doesn't update the data field on the user interface.
if (mNotifyCharacteristic != null) {
mBluetoothLeService.setCharacteristicNotification(
mNotifyCharacteristic, false);
mNotifyCharacteristic = null;
}
mBluetoothLeService.readCharacteristic(characteristic);
}
if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
mNotifyCharacteristic = characteristic;
mBluetoothLeService.setCharacteristicNotification(
characteristic, true);
}
return true;
}
return false;
}
You could try reading the Bluetooth specification (https://www.bluetooth.com/specifications/adopted-specifications, Core Version 5.0), vol 3 part G. Just note that Android abstracts away the attribute handles.
What most people do however is writing an app designed for a particular hardware, i.e. the gatt db is assumed to be known.
In order to get objects that represents the services, characteristics and descriptors, call gatt.discoverServices(). The result is returned asynchronously using the onServicesDiscovered() callback. This should be done each time a device gets connected (in the onConnectionStateChange callback when newState is GATT_CONNECTED).
To write to a characteristic, first set the value on the BluetoothGattCharacteristic object using the setValue method, then call gatt.writeCharacteristic(characteristic). When the operation is complete, onCharacteristicWrite will be called.
A read operation works in similar way; call gatt.readCharacteristic(characteristic) and the result is ready when the onCharacteristicRead is called. Use getValue() on the characteristic object to get the value.
For notifications to work you must first write BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE (0x0001) to the client characteristic configuration descriptor so that the peripheral will start deliver notifications. You must also tell the Android Bluetooth stack to forward the notifications to your app by calling setCharacteristicNotification.
Note that you can only have one outstanding operation at a time (read/write), meaning you must wait for the callback before you issue a new request.
If you know the hardware you're dealing with you normally don't need to inspect the characteristic properties (characteristic.getProperties()). The bitmask is described in Bluetooth Core V 5.0 specification, Vol 3, Part G, section 3.3.1.1 and describes what features are enabled for each characteristic (for example read, write, notifications).
How to deal with conversion between 16-bit and 128-bit UUIDs is shown in the Bluetooth Core V 5.0 specification, Vol 3, Part B, section 2.5.1. Note that Android's client libraries only use 128-bit UUIDs.

How to detect when a BLE device is not in range anymore?

I use a LeScanCallback (can not use the newer scan methods because I'm developing for api 18. Not that it matters, since the android 5.0+ apis don't offer this functionality either) to detect when a nearby BLE device is detected:
private BluetoothAdapter.LeScanCallback bleCallback = new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(BluetoothDevice bluetoothDevice, int i, byte[] bytes) {
discoveredDevices.add(bluetoothDevice);
}
};
I am not pairing or connecting with the devices because that's not required, I simply want to see which devices are nearby.
I'm trying to make a service that, every 5 mins or so, calls a webserver to update which devices are nearby at that moment.
Tricky part is that the android device will be moving, so a bluetooth device that is nearby right now, might not be in 5 mins. In that case I need to remove it from discoveredDevices.
Ideally, I would like to receive a callback when a bluetooth device was in range before, but is not anymore. This callback doesn't exist though.
(I'm aware of the android.bluetooth.device.action.ACL_CONNECTED and android.bluetooth.device.action.ACL_DISCONNECTED broadcasts, but those are for when you connect to a bluetooth device, which I don't want.)
An option is to do a fresh scan every 5 mins, but you can't tell when all nearby devices have been discovered, so you would have to do a timed scan, e.g. scan for 5 seconds and then send the collected data to the webservice.
This sounds dirty and risky because you can never know for sure all nearby devices were discovered within the allotted time, so I would very much like to avoid doing it like that.
Is there another way to do this?
Edit
Some devices continuously report discovery of nearby bluetooth devices, even if they were already discovered before. If that functionality was universal I could solve my problem, however this is device specific.
My phone's bluetooth adapter for example only discovers nearby devices once. Some other devices I have tested with do continuously report the same nearby devices, but not all devices do, so I can't rely on that unfortunately.
This sounds dirty and risky because you can never know for sure all nearby devices were discovered within the allotted time, so I would very much like to avoid doing it like that.
That sounds like a reasonable assumption, but it's wrong.
Bluetooth low energy works in a particular way and BLE devices have some limits. For instance, they have a fixed range of possible advertising frequencies, ranging from 20 milliseconds to 10.24 seconds, in steps of 0.625 milliseconds. See here and here for more detailed information.
This means that it can take at most 10.24 seconds before a device will broadcast a new advertisement package. BLE devices generally, if not always, provide a way for their owner to adjust their advertising frequency, so the frequency can of course vary.
In cases where you are periodically collecting data about nearby devices, like yours, it is fine to use a scan with a fixed time limit, save that data somewhere, restart the scan, collect new data, compare with old data --> get results.
For example, if a device was found in scan 1 but not in scan 2, you can conclude that the device was in range, but is not anymore.
Same goes for the other way around: if a device was found in scan 4 but not in scan 3, it is a newly discovered device.
Finally, if a device was found in scan 5, was not found in scan 6, but was again found in scan 7, it is rediscovered and can be handled as such if need be.
Because I'm answering my own question here, I'll add the code that I used to implement this.
I have the scanning done in a background service, and communicate to other parts of the app using BroadcastReceivers. Asset is a custom class of mine that holds some data. DataManager is a custom class of mine that - how did you guess it - manages data.
public class BLEDiscoveryService extends Service {
// Broadcast identifiers.
public static final String EVENT_NEW_ASSET = "EVENT_NEW_ASSET ";
public static final String EVENT_LOST_ASSET = "EVENT_LOST_ASSET ";
private static Handler handler;
private static final int BLE_SCAN_TIMEOUT = 11000; // 11 seconds
// Lists to keep track of current and previous detected devices.
// Used to determine which are in range and which are not anymore.
private List<Asset> previouslyDiscoveredAssets;
private List<Asset> currentlyDiscoveredAssets;
private BluetoothAdapter bluetoothAdapter;
private BluetoothAdapter.LeScanCallback BLECallback = new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(BluetoothDevice bluetoothDevice, int i, byte[] bytes) {
Asset asset = DataManager.getAssetForMACAddress(bluetoothDevice.getAddress());
handleDiscoveredAsset(asset);
}
};
#Override
public void onCreate() {
super.onCreate();
BluetoothManager manager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
bluetoothAdapter = manager.getAdapter();
previouslyDiscoveredAssets = new ArrayList<>();
currentlyDiscoveredAssets = new ArrayList<>();
handler = new Handler();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Start scanning.
startBLEScan();
// After a period of time, stop the current scan and start a new one.
// This is used to detect when assets are not in range anymore.
handler.postDelayed(new Runnable() {
#Override
public void run() {
performRepeatingTask();
// Repeat.
handler.postDelayed(this, BLE_SCAN_TIMEOUT);
}
}, BLE_SCAN_TIMEOUT);
// Service is not restarted if it gets terminated.
return Service.START_NOT_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
handler.removeCallbacksAndMessages(null);
stopBLEScan();
super.onDestroy();
}
private void startBLEScan() {
bluetoothAdapter.startLeScan(BLECallback);
}
private void stopBLEScan() {
bluetoothAdapter.stopLeScan(BLECallback);
}
private void handleDiscoveredAsset(Asset asset) {
currentlyDiscoveredAssets.add(asset);
// Notify observers that we have a new asset discovered, but only if it was not
// discovered previously.
if (currentlyDiscoveredAssets.contains(asset) &&
!previouslyDiscoveredAssets.contains(asset)) {
notifyObserversOfNewAsset(asset);
}
}
private void performRepeatingTask() {
// Check if a previously discovered asset is not discovered this scan round,
// meaning it's not in range anymore.
for (Asset asset : previouslyDiscoveredAssets) {
if (!currentlyDiscoveredAssets.contains(asset)) {
notifyObserversOfLostAsset(asset);
}
}
// Update lists for a new round of scanning.
previouslyDiscoveredAssets.clear();
previouslyDiscoveredAssets.addAll(currentlyDiscoveredAssets);
currentlyDiscoveredAssets.clear();
// Reset the scan.
stopBLEScan();
startBLEScan();
}
private void notifyObserversOfNewAsset(Asset asset) {
Intent intent = new Intent();
intent.putExtra("macAddress", asset.MAC_address);
intent.setAction(EVENT_NEW_ASSET);
sendBroadcast(intent);
}
private void notifyObserversOfLostAsset(Asset asset) {
Intent intent = new Intent();
intent.putExtra("macAddress", asset.MAC_address);
intent.setAction(EVENT_LOST_ASSET);
sendBroadcast(intent);
}
}
This code is not perfect and might even be buggy, but it will at least give you an idea or example of how this can be implemented.
I can recommend this approach:
Use Map<BluetoothDevice, Long> structure to store the discovered devices, where Long is the time of detection of the device (can be System.currentTimeMillis() for example).
Then in your service (as far as I understand from the question there will be implemented some kind of repeated task) just extract actual devices based on the time of their detection.
And you are absolutely right, there are no guarantee that all nearby devices were discovered within the allotted time. Especially this is actual for the Android devices.
iOS devices in it's turn have another issue - they can change their BluetoothDevice's adress in runtime without apparent external cause.
Hope this will help you to save the time during debugging.
Edit
As a result of research of this topic found this discussion on code.google.com
Issue is still open and seems that it is related to the hardware features and can't be fixed programmatically. Moreover, it seems that bug will remains on problem devices even after a system updates.
So restarting the scan periodically might be acceptable workaround for this case.

How to get access to the BluetoothPairingDialog.java in android?

Is there any way to forcefully click on "pair button" whenever the Bluetooth pairing dialog appears?
I don't know how to get access to the pairing dialog, but I was able to "force" pairing in the following way:
1) register a BroadcastReceiver for the action:
android.bluetooth.device.action.PAIRING_REQUEST
2) once the action is received, "force" the PIN using reflection:
String DEVICE_PIN = "12345";
final BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (intent.getAction().equals("android.bluetooth.device.action.PAIRING_REQUEST")) {
byte[] pin = (byte[]) BluetoothDevice.class.getMethod("convertPinToBytes", String.class).invoke(BluetoothDevice.class, ARDUINO_PIN);
BluetoothDevice.class.getMethod("setPin", byte[].class).invoke(device, pin);
}
It worked for me on GB and ICS (don't know if it works on newer releases).

Categories

Resources