BLE 4.0 getting the broadcast data from device to phone - android

I have two devices. one Android phone with API level more than 18 and other is blue-tooth device 4.0.
Devices are successfully connected to each other.
Now flow of command is as follow:
a. Send the "hello" text to blue-tooth device.
UUID uuid = UUID.fromString("18cda784-4bd3-4370-85bb-bfed91ec86af");
BluetoothGattCharacteristic selectedChar = selectedGattService.getCharacteristic(uuid);
mBluetoothLeService.setCharacteristicNotification(selectedChar, true);
boolean flag = selectedChar.setValue("");
mBluetoothLeService.writeCharacteristic(selectedChar);
In this case I am getting hello through the GATT reciver. what is the meaning of this.
registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());
private static IntentFilter makeGattUpdateIntentFilter() {
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED);
intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED);
intentFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED);
intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE);
intentFilter.addAction(BluetoothLeService.EXTRA_DATA);
return intentFilter;
}
b. bluetooth device will perform some operations
auto done by bluetooth device
c. Result of operation is sent to android phone
Brodcated by device.
For this I used notification.
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
boolean enabled) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
return;
}
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID
.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
System.out.println("nov7 descriptordescriptor " + descriptor);
if (descriptor != null) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
}
}
I am not getting any data. Any idea please.

saw your email, I think you are somehow connected via Bluetooth classic, but are then attempting to 'chat' on BTLE protocol.
That's the problem.
There is almost no way an Android 4.0 device has BTLE.
Even if it has an BTLE chip(there was some early Motorola phones with BTLE - you had to import a .jar from Motorola Inc.), it wouldn't use the Android BTLE API you seem to use.
So to make a long story short, you should either be using Bluetooth Classic (SPP) with the normal BluetoothSocket, or be using two Android BTLE devices.
Here is how to check if the devices have BTLE:\
If you want to declare that your app is available to BLE-capable devices only, include the following in your app's manifest:
However, if you want to make your app available to devices that don't support BLE, you should still > include this element in your app's manifest, but set required="false". Then at run-time you can determine BLE availability by using PackageManager.hasSystemFeature():
// Use this check to determine whether BLE is supported on the device.
// Then you can selectively disable BLE-related features.
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
finish();
}

Two things I see that you should be doing.
BluetoothGatt has its own setCharacteristicNotification method. In addition to writing the characteristic descriptor to enable notifications, you need to call that method to enable notifications. Think of it as writing the descriptor enables notifications on the BLE device and setCharacteristicNotification enables it on the Android device.
So in your setCharacteristicNotification method above I would add the following:
// I'm assuming you have access to the BluetoothGatt object in your BluetoothGattService object
gatt.setCharacteristicNotification(characteristic, true);
You shouldn't be trying to write any data to the characteristic until you have received confirmation that the descriptor was written. That means you need to wait until you get the callback to onDescriptorWrite in your implementation of BluetoothGattCallback.

Related

Android BLE how can app hijack OS pairing process

I was working with a BLE device, which needs to set the device time during pairing. Writing to the time characteristic in any other time does not effectively set the time.
Currently I am using the Android OS's Bluetooth Manager for pairing. And the progress of pairing is notified to my app through broadcast intent.
public void onReceive(Context context, Intent intent) {
....
switch (action) {
case BluetoothDevice.ACTION_BOND_STATE_CHANGED:
if(state == BluetoothDevice.BOND_BONDED){
//Write to the Date-Time Characteristic
}
else if(state==BluetoothDevice.BOND_BONDING){
}
else if(state==BluetoothDevice.BONE_NONE){
}
....
}
...
}
My question is how to inject code to the position commented above to complete the Date-Time setting? Apparently, the Android OS Bluetooth Manager does not set the time during its entire pairing process. Does Android allow two applications (OS Bluetooth Manager and my application) write to the remote gatt within a single connection session?
It may not possible for you to write the data-time i.e. the characteristic during the pairing procedure(well if you do not need pairing that should be fine). Reason is that you may first get service and get characteristic handle then write it, this may not happen with the same time of the pairing; it depends your remote device's security level setting.
My question is how to inject code to the position commented above to complete the Date-Time setting?
You can register a broadcast receiver to receive the bonding event.
Does Android allow two applications (OS Bluetooth Manager and my application) write to the remote gatt within a single connection session?
Sure, because you are using the same BluetoothAdapter :-)

Pairing to a Bluetooth Low Energy device in Android

Is it possible to automatically connect to Bluetooth Low Energy (BLE) devices?
The Android documentation indicates that the [BluetoothDevice.connectGatt()](https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback)) has a autoConnect parameter:
boolean indicating whether to automatically connect to the BLE device
as soon as it becomes available
However, to call this, you need a BluetoothDevice first. AFAIK the only way to get this is by scanning available devices. Setting up a scan every time to connect to a device doesn't seem like a desirable way. Also, I tried using nRF Control Master Panel to connect to my peripheral using the autoConnect = true, but this does not connect to the device. Connecting without the autoConnect however does make it connect, and I've managed to read and write data from and to my peripheral this way with success.
The general way in Bluetooth to have two devices paired. However, searching for my BLE device and using BluetoothDevice.createBond() does not seem to work. In my ACTION_BOND_STATE_CHANGED-callback, the EXTRA_BOND_STATE and EXTRA_PREVIOUS_BOND_STATE just go from BOND_BONDING to BOND_NONE and back. I don't read out an error or anything - so maybe I'm missing something here. Here's the callback:
private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
final int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR);
final int prevState = intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, BluetoothDevice.ERROR);
Log.e(TAG, "prevState " + prevState + ", state " + state);
}
}
};
So this type of bonding does not seem to work.
My question is: am I doing something wrong for pairing or the autoConnect? Or is how I currently have it working the only correct way? It seems like a real pain (and battery-drain) to have to scan for devices every time, see if the device is there, if so read data and check back tomorrow, otherwise check back in an hour or so. The point of Bluetooth is that it should pair directly whenever it is near, isn't it?
It does work without rescan. You do not need pairing at all. Just call BluetoothGatt.connect() again for gatt object you aquired from first connection.
You will receive onConnectionStateChange event in your BluetoothGattCallback as soon as ble device will be available again. If you use autoconnect option, you don't even need to call BluetoothGatt.connect() method. Just monitor your cllback, and don't forget to close BluetoothGatt with close() if you don't see any connection for too long.
And yes, to obtain first connection you should scan for ble devices with BluetoothAdapter.startLeScan, not the common bluetooth devices scan.

Why the App doesn't reconnect to the BLE device when set autoConnect to true in Android?

I am develop in Android and BLE. I want the App automatic reconnect to the BLE device after the BLE device disconnect but come back in the range and advertising.
I use the following code to connect to the BLE device:
public void connect(final String address) {
// TODO Auto-generated method stub
Log.w(TAG, "BluetoothLeService Connect function.");
if(mBluetoothAdapter == null || address == null){
Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
//return false;
}
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
mBluetoothGatt = device.connectGatt(this, true, mGattCallback);
}
I have set the AutoConnect to the true , but it didn't reconnect when the BLE device has disconnect and come back in the range.
Why the App doesn't reconnect to the BLE device when set autoConnect to true in Android?
Did I missing something ?
Thanks in advance.
The auto connect parameter determines whether to actively connect to the remote device (or) rather passively scan and finalize the connection when the remote device is in range.
But this does not mean that a peripheral that's been disconnected for days then reappears will be reconnected.
Generally, the first ever connection to a device should be direct (autoConnect set to false) and subsequent connections to known devices should be invoked with the autoConnect parameter set to true.
Also please note, the auto connect will only work when the device is still broadcasting. If not, then it will not work.
I would prefer that you re-connect manually when the device is disconnected. If in case you do end up following this, you would need a marker to determine whether the device was actually disconnected without the user consent.
If true then unbind/unregister your service/broadcast receiver and connect again using the device address which you must have saved previously.
As per my experimentation with the BLE devices it has different behavior in different builds like Kitkat and Lollipop. Even I have observed, using ScanCallback is not so reliable introduced in API level 24.
For auto connect to work the BLE device must be active.
For me i had to support kitkat and lollipop so while connecting gatt i called as:
if(Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
gatt = device.connectGatt(this, true, executor);
} else {
gatt = device.connectGatt(this, false, executor);
}
Now auto connect is working for me in both Lollipop and Kitkat.

Android does not make the BLE findme device beep

I'm trying to implement the application which will communicate with BLE findme device. I have the one of these devices, but have some problem with it. Using iPhone I have tested this device with bleTools application and this app works correctly, i.e. I have managed to read all device's characterictics and send the characteristics to make the device beep. But using Android (Nexus 5) I could only read the device's characteristics, but cannot make the device beep.
My code is:
private static final UUID IMMEDIATE_ALERT_SERVICE =
UUID.fromString("00001802-0000-1000-8000-00805f9b34fb");
private static final UUID IMMEDIATE_ALERT_LEVEL =
UUID.fromString("00002a06-0000-1000-8000-00805f9b34fb");
...
public void beep(DeviceData device) {
BluetoothGatt gatt = mConnectedDevices.get(device.getDeviceAddress());
BluetoothGattService bluetoothGattService = gatt.getService(IMMEDIATE_ALERT_SERVICE);
if (bluetoothGattService == null) {
return;
}
BluetoothGattCharacteristic characteristic =
bluetoothGattService.getCharacteristic(IMMEDIATE_ALERT_LEVEL);
if (characteristic == null) {
return;
}
byte[] arrayOfByte = new byte[1];
arrayOfByte[0] = (byte) 0x01;
characteristic.setValue(arrayOfByte);
gatt.writeCharacteristic(characteristic);
}
The callback method returns Ok:
#Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if (characteristic.getUuid().toString().equals(IMMEDIATE_ALERT_LEVEL.toString())) {
//TODO: use device address to identify the device-receiver
Message msg = new Message();
msg.what = MSG_PARAM_WRITTEN;
msg.obj = (status == BluetoothGatt.GATT_SUCCESS);
mHandler.sendMessage(msg);
}
}
but nothing happens on the device side.
Can anyone explain me what I'm doing wrong or maybe give some advice what should I do?
And again, I can read the device characteristics, but cannot write them to the device.
Unlike iOS, Android has quite a few undocumented tricks with Bluetooth. I'm assuming you are using the standard Bluetooth library included in Android 4.3 and later. If you using other libraries like Samsung or Broadcom, the results could be different.
Because I do not have a findeme device I cannot confirm anything. But I have worked with both classic and low energy Bluetooth energy on Android for a while now. My advice is to go through the complete process of scan, discover services and read/write characteristics.
startLeScan
onLeScanCallBack connect to the device
onConnect discoverServices
onServicesDiscovered get all characteristics
check the properties on each characteristic
if you can read it, go ahead and do that
after that is done, you can then write the characteristic and listen for the onCharacteristicWrite event. You may get the beep then. If not, you'll need to go back to the iOS project and trace every bit that is sent to and received from the findme device. That sounds difficult but it's really just a matter of reading bytes inside of the right delegates. Document that. Then go back and recreate the bit sequence on the Android side. Again, capture all the traffic to and from the findme device. If you can get the bit sequence to match, you'll have success.
There's one more very important thing to know about Android BLE. The writes must be sequential. By that I mean, if you write a characteristic, you must wait for the onCharacteristic event to fire before you can write another one. This is not documented on the Android developer site. The best way to implement this functionality is to use a LinkedList as a work queue.
Good luck and ping me if you have questions.

Unable to accept incoming Bluetooth connection in Android

Maybe similar to the unanswered thread here, but we have an Android application (tested on multiple handsets and multiple Android 2.1+ versions) which needs to listen for and accept connections from a remote bluetooth device. The remote device is a digital pen, but the main point is that the pen is paired with the phone and then sends data via SPP, which uses OBEX, which uses RFComm so all that should be fine.
Currently the application works by allowing the Android device to receive the OBEX payload and then get the app to look in the bluetooth folder to pick up the payload, but we want the application to be able to talk directly to the remote device. Keep in mind the remote connects to the android phone, the phone does not connect to the pen.
Our test code is based on the sample BluetoothChat application available in the Android samples, but essentially adapter.listenUsingRfcommWithServiceRecord never gets called and the best that we see in the Motorola Defy+ DDMS logs is:
INFO/BtOppRfcommListener(2577): Accepted connectoin from 00:07:CF:55:94:FB
INFO/BtOpp Service(2577): Start Obex Server
DEBUG/Obex ServerSession(2577): java.io.IOException: Software caused connection abort
This appears to show that the connection is accepted by Android but not made available to the application. The UUID used is the same UUID used in the JME version of the same application and was provided by the pen supplier.
The most common mistake we android developers do while copying BluetoothChat app code is we don't follow the flow of that app.
The common errors while implementing this code, like "Service Discovery Failed", "Software caused connection abort", "Connection aborted by peer", "Unable to start Service Discovery" , "Connection Lost" is the outcome of neglecting the flow.
I have faced all these issues while implementing the BluetoothChat code in my app and the solution was to correct the flow.
For example in my case :
OnCreate:
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
OnStart:
if(this.mChatService == null)
this.mChatService = new BluetoothChatService(this, mHandler);
OnResume:
if ((mChatService != null) && (mBluetoothAdapter.isEnabled())) {
// Only if the state is STATE_NONE, do we know that we haven't started already
if (mChatService.getState() == BluetoothChatService.STATE_NONE) {
// Start the Bluetooth chat services
mChatService.start();
}
}
OnDestroy:
if(mTransferService != null) mTransferService.stop();
onActivityResult:
case REQUEST_CONNECT_DEVICE:
// When DeviceListActivity returns with a device to connect
if (resultCode == Activity.RESULT_OK) {
if (mChatService.getState() == BluetoothChatService.STATE_NONE) {
// Start the Bluetooth chat services
mChatService.start();
}
if(mChatService .getState() != BluetoothChatService.STATE_CONNECTED){
String address = data.getExtras().getString(ViewContactList.EXTRA_DEVICE_ADDRESS);
// Get the BLuetoothDevice object
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
device = mBluetoothAdapter.getRemoteDevice(address);
// Attempt to connect to the device
mChatService.connect(device);
}
else{
if(messageString.length() > 0)
MyCurrentActivity.this.sendMessage(messageString);
}
In simple words, implement the code in such a way both devices are in mState = STATE_LISTEN , before you will call
mChatService.connect(device);
In our case the only solution we found which could reliably accept Bluetooth OPP data from a remote device was to use the 'hidden' Android API. If you check the Android source, there are additional methods defined which are annotated #hide which excludes them from the exported Android jar and the API docs.
Using these methods should be avoided if possible and are not guaranteed to be consistent across Android versions.
There are two ways to include these in your code:
use reflection
include an additional development JAR at compile time which exposes these methods, but make sure this JAR is not packaged with your application.
We opted for #1 as our requirements were minimal.
Access the Method
Class<?>[] args = new Class[] { int.class };
Method listenMethod = BluetoothAdapter.class.getMethod(listenMethod, args);
Where the method name is one of (in order of preference) listenUsingEncryptedRfcommOn, listenUsingRfcommOn or listenUsingUnencrytptedRfcommOn
Create a server socket
BluetoothAdapter target = *your adapter, probably the default one*;
BluetoothServerSocket sock = (BluetoothServerSocket) (listenMethod.invoke(target, new Object[] { channel }));
Where in our case the OPP channel is 12
Fin
Provided you manage to get a BluetoothServerSocket back (exception handling is omitted, your mileage may vary, user discretion advised, no warrantee provided etc etc) you should be able to use it to listen for incoming OPP data.

Categories

Resources