Android BLE data reception issue - android

Im writing a BLE app for android as the title says, but when I select a device from my list of discovered devices (my embedded device), the app connects but no data shows up... unless I select it a second time. I know its connecting the first time because the device has an LED indicator but no data comes through until I select it again. Does anyone have any idea why that would be?
public class BluetoothDiscovery extends AppCompatActivity {
private String DEVICE = "Bluetooth Device";
private String COMMS = "Bluetooth Communication";
private int REQUEST_ENABLE_BT = 5;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothLeScannerCompat scanner;
private ScanSettings settings;
private UUID baseUUID = UUID.fromString("6e400001-b5a3-f393-e0a9-e50e24dcca9e"); // service UUID
private UUID txUUID = UUID.fromString("6e400002-b5a3-f393-e0a9-e50e24dcca9e"); // TX UUID characteristic
private UUID rxUUID = UUID.fromString("6e400003-b5a3-f393-e0a9-e50e24dcca9e"); // RX UUID characteristic
private ScanFilter scanFilter;
private BluetoothDevice device, mdevice;
private BluetoothGatt mGatt;
private boolean mScanning = false;
private ArrayList<deviceShowFormat> foundDevices = new ArrayList<>();
formattingAdapter BTadapter;
Button scanButton;
TextView fancyWords;
ListView deviceList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bluetooth_discovery);
BluetoothManager manager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = manager.getAdapter();
//mBluetoothAdapter.getBluetoothLeScanner();
//mBluetoothAdapter.getDefaultAdapter();//.getBluetoothLeScanner();
scanButton = findViewById(R.id.scanButt);
scanButton.setText(getString(R.string.notScanning));
fancyWords = findViewById(R.id.discoverText);
fancyWords.setText(getString(R.string.nonScanTitle));
deviceList = findViewById(R.id.deviceList);
BTadapter = new formattingAdapter(BluetoothDiscovery.this, foundDevices);
deviceList.setAdapter(BTadapter);
scanner = BluetoothLeScannerCompat.getScanner();
settings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_BALANCED).setReportDelay(1000).build();
scanFilter = new ScanFilter.Builder().setServiceUuid(new ParcelUuid(baseUUID)).build();
//scanner.startScan(Arrays.asList(scanFilter), settings, mScanCallback);
deviceList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#SuppressLint("LongLogTag")
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
scanner.stopScan(mScanCallback);
scanButton.setText(getString(R.string.notScanning));
deviceShowFormat mBTDevice = foundDevices.get(i);
BluetoothDevice Device = mBTDevice.get_device();
String deviceName = mBTDevice.get_device_name();
String deviceAddress = mBTDevice.get_device_address();
Log.i(DEVICE, "Selected device: " + Device.toString());
Log.i(DEVICE, "Selected device name: " + deviceName);
Log.i(DEVICE, "Selected device address: " + deviceAddress);
mBluetoothAdapter.getRemoteDevice(deviceAddress);
mGatt = Device.connectGatt(BluetoothDiscovery.this, false, mGattCallback);
}
});
}
private final no.nordicsemi.android.support.v18.scanner.ScanCallback mScanCallback = new no.nordicsemi.android.support.v18.scanner.ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
Log.i("onScanResult", "device detected");
device = result.getDevice();
String deviceName = device.getName();
String deviceAddress = device.getAddress();
Log.i(DEVICE, "Scanned device: " + device.toString());
Log.i(DEVICE, "Scanned device name: " + deviceName);
Log.i(DEVICE, "Scanned device address: " + deviceAddress);
foundDevices.add(new deviceShowFormat(device, deviceName, deviceAddress));
BTadapter.notifyDataSetChanged();
}
};
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
Log.i("onConnectionStateChange", "State Changed from: " + status + " to " + newState);
if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_CONNECTED){ // 2
//Toast.makeText(BluetoothDiscovery.this, "Attempting service discovery", Toast.LENGTH_SHORT).show();
//Log.i("onConnectionStateChange", "Attempting service discovery: " + mGatt.discoverServices());
gatt.discoverServices();
} else if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_DISCONNECTED){ // 0
Toast.makeText(BluetoothDiscovery.this, "Connection has been terminated?", Toast.LENGTH_SHORT).show();
} else if (status != BluetoothGatt.GATT_SUCCESS){
Log.i("GATT", "Unsuccessful");
gatt.disconnect();
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status){
super.onServicesDiscovered(gatt, status);
Log.i("onServicesDiscovered", "Hey, we found a service");
List<BluetoothGattService> services = gatt.getServices();
Log.i("SERVICE", "Services: " + services.toString());
BluetoothGattCharacteristic characteristic = services.get(4).getCharacteristics().get(0);
//BluetoothGattCharacteristic characteristic = gatt.getService(baseUUID).getCharacteristic(rxUUID);
gatt.setCharacteristicNotification(characteristic, true);
List<BluetoothGattDescriptor> describeMe = characteristic.getDescriptors();
Log.i("DESCRIPTORS", "Descriptors: " + describeMe.toString());
Log.i("DESCRIPTORS", "Descriptors: " + describeMe.get(1).getUuid().toString());
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(describeMe.get(0).getUuid());//UUID.fromString("00002902-0000-1000-8000-00805F9B34FB"));//describeMe.get(1).getUuid()
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor);
Log.i("ByeSERVICESDISCOVERED", "that");
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
Log.i("onCharacteristicChanged", "Entered");
byte[] dataInput = characteristic.getValue();
Log.i("MESSAGE", dataInput.toString());
try {
String data = new String(dataInput, "UTF-8");
// create list to contain data
Log.i("MESSAGE2", data);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
Log.i("onCharacteristicChanged", "Bye");
}
};
public void toggleScan(View view){
mScanning = !mScanning;
if(mScanning){
scanner.startScan(mScanCallback); //Arrays.asList(scanFilter) null, settings,
scanButton.setText(getString(R.string.scanInProgress));
fancyWords.setText(getString(R.string.ScanTitle));
} else {
scanner.stopScan(mScanCallback);
scanButton.setText(getString(R.string.notScanning));
//foundDevices.clear();
}
}
}
edit: Logs on first click
Logs on second click
Red marks my debugging logs

Related

BLE error : D/BluetoothLeScanner: Scan failed, reason: app registration failed

I am getting this error sometimes while trying to scan BLE in android studio, usually after a while of using my app but cerntainly not all the time.
I am using a class especially for BT connection, this is what's inside:
package com.omri.goniometer;
import android.app.Application;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.HandlerThread;
import android.util.Log;
import android.widget.Toast;
import android.os.Handler;
import java.util.List;
import java.util.UUID;
public class Bluetooth {
private final Context mContext;
private final MainActivity mActivity;
final String devcAddress = "D8:A0:1D:47:49:82";
// private static final UUID ServiceUUID = UUID.fromString("0000183B-0000-1000-8000-00805F9B34FB");
// private static final UUID CharUUID = UUID.fromString("00002A08-0000-1000-8000-00805F9B34FB");
private static final UUID ServiceUUID = UUID.fromString("da3a95de-467b-11ea-b77f-2e728ce88125");
private static final UUID CharUUID = UUID.fromString("da3a9836-467b-11ea-b77f-2e728ce88125");
private String scanDevcAddress = null;
private BluetoothAdapter mBTAdapter;
private boolean mScanning;
private static final long SCAN_PERIOD = 5000;
private BluetoothLeScanner mBluetoothLeScanner;
private BluetoothDevice BTdevice;
private BluetoothGatt mBTGatt;
private BluetoothGattCharacteristic mGattChar;
private int mConnectionState = STATE_DISCONNECTED;
private Short rollInt = 0;
private Short pitchInt = 0;
String rollString = "0";
String pitchString = "0";
private static final int STATE_DISCONNECTED = 0;
private static final int STATE_CONNECTING = 1;
private static final int STATE_CONNECTED = 2;
private static final int WAVE = 3;
private static final int BATTERY_UPDATE = 4;
String[] stringArray;
Handler mHandler;
public final static String ACTION_GATT_CONNECTED =
"com.example.bluetooth.le.ACTION_GATT_CONNECTED";
public final static String ACTION_GATT_DISCONNECTED =
"com.example.bluetooth.le.ACTION_GATT_DISCONNECTED";
public final static String ACTION_GATT_SERVICES_DISCOVERED =
"com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";
public final static String ACTION_DATA_AVAILABLE =
"com.example.bluetooth.le.ACTION_DATA_AVAILABLE";
public final static String EXTRA_DATA =
"com.example.bluetooth.le.EXTRA_DATA";
Bluetooth(Context context, MainActivity activity) {
this.mContext = context;
this.mActivity = activity;
mHandler = new Handler();
disconnect();
close();
// Initializes Bluetooth adapter.
final BluetoothManager bluetoothManager =
(BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
mBTAdapter = bluetoothManager.getAdapter();
mBluetoothLeScanner = mBTAdapter.getBluetoothLeScanner();
if (mBTAdapter == null) {
// Device does not support Bluetooth
Toast.makeText(mContext, "Bluetooth device not found!", Toast.LENGTH_SHORT).show();
} else {
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(mContext, "no BLE feature in phone", Toast.LENGTH_SHORT).show();
}
searchDevice();
//connect();
}
}
public void searchDevice() {
if (mBTAdapter.isEnabled()) {
Log.d("ADebugTag", "After check if BTAdapter is enabled");
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
scanLeDevice(true);
}
}, 5000);
} else {
Toast.makeText(mContext, "To begin please turn on bluetooth", Toast.LENGTH_SHORT).show();
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
mActivity.startActivityForResult(enableBtIntent, MainActivity.REQUEST_ENABLE_BT);
}
}
private void scanLeDevice(final boolean enable) {
if (enable) {
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
mScanning = false;
mBluetoothLeScanner.stopScan(leScanCallback);
}
}, SCAN_PERIOD);
mScanning = true;
Log.d("ADebugTag", "Just before scan");
mBluetoothLeScanner.startScan(leScanCallback);
} else {
mScanning = false;
mBluetoothLeScanner.stopScan(leScanCallback);
}
}
// Device scan callback.
private ScanCallback leScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
Log.d("ADebugTag", "Inside scan callback scan");
BTdevice = result.getDevice();
scanDevcAddress = result.getDevice().getAddress();
Log.d("ADebugTag", "scanned address: " + scanDevcAddress);
if (scanDevcAddress.equals(devcAddress)) {
mActivity.updatesStatus(MainActivity.DEVICE_FOUND,"","");
//Intent deviceFoundIntent = new Intent(mActivity,MainActivity.class);
scanLeDevice(false);
//Toast.makeText(mContext, "Scanning stopped", Toast.LENGTH_SHORT).show();
}
}
};
public boolean connect() {
mActivity.updatesStatus(MainActivity.STATE_CONNECTING,"","");
BTdevice = mBTAdapter.getRemoteDevice(devcAddress);
mBTGatt = BTdevice.connectGatt(mContext, false, mGattCallback);
if (mBTGatt == null){
Log.d("ADebugTag", "mBTGatt is null");
return false;
}
else {
try {
mGattChar = mBTGatt.getService(ServiceUUID).getCharacteristic(CharUUID);
}
catch (NullPointerException e){
Log.d("ADebugTag", "mGattChar is null\n" + e);
}
//setCharacteristicNotification(mGattChar, true);
return true;
}
}
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
String intentAction;
if (newState == BluetoothProfile.STATE_CONNECTED) {
intentAction = ACTION_GATT_CONNECTED;
mConnectionState = STATE_CONNECTED;
broadcastUpdate(intentAction);
mActivity.updatesStatus(MainActivity.STATE_CONNECTED,"","");
Log.d("ADebugTag", "Connected to GATT server.");
// Attempts to discover services after successful connection.
Log.d("ADebugTag", "Attempting to start service discovery:" + mBTGatt.discoverServices());
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
intentAction = ACTION_GATT_DISCONNECTED;
mConnectionState = STATE_DISCONNECTED;
mActivity.updatesStatus(MainActivity.STATE_DISCONNECTED,"","");
Log.d("ADebugTag", "Disconnected from GATT server.");
broadcastUpdate(intentAction);
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
if (mBTGatt ==null){
return;
}
else {
List<BluetoothGattService> services = getSupportedGattServices();
for (BluetoothGattService service : services) {
if (!service.getUuid().equals(ServiceUUID))
continue;
List<BluetoothGattCharacteristic> gattCharacteristics =
service.getCharacteristics();
// Loops through available Characteristics.
for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
if (!gattCharacteristic.getUuid().equals(CharUUID))
continue;
final int charaProp = gattCharacteristic.getProperties();
if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
setCharacteristicNotification(gattCharacteristic, true);
} else {
Log.d("ADebugTag", "Characteristic does not support notify");
}
}
}
}
} else {
Log.d("ADebugTag", "onServicesDiscovered received:" + status);
}
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
final byte[] dataInput = characteristic.getValue();
}
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
final byte[] dataInput = characteristic.getValue();
//Log.d("ADebugTag", "Data on changed: " + dataInput);
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
};
private void broadcastUpdate(final String action) {
final Intent intent = new Intent(action);
mContext.sendBroadcast(intent);
}
private void broadcastUpdate(final String action,
final BluetoothGattCharacteristic characteristic) {
final Intent intent = new Intent(action);
// For all other profiles, writes the data formatted in HEX.
if (characteristic == null) {
Log.d("ADebugTag", "characteristic is null in broadcastUpdate");
}
else {
final byte[] data = characteristic.getValue();
//Log.d("ADebugTag", "Data broadcast update: " + data);
if (data != null && data.length > 0) {
final StringBuilder stringBuilder = new StringBuilder(data.length);
for (byte byteChar : data)
stringBuilder.append(String.format("%X ", byteChar));
//stringBuilder.append(String.format("%d ", byteChar));
String ds = stringBuilder.toString();
intent.putExtra(EXTRA_DATA, new String(data) + "\n" + ds);
Log.d("ADebugTag", "Data complete string: " + ds);
stringArray = ds.split(" ");
rollInt = (short) Integer.parseInt(stringArray[3]+stringArray[2],16);
pitchInt = (short) Integer.parseInt(stringArray[1]+stringArray[0],16);
Log.d("ADebugTag", "Roll received:" + rollInt);
Log.d("ADebugTag", "Pitch received:" + pitchInt);
rollString = rollInt.toString();
pitchString = pitchInt.toString();
mActivity.updatesStatus(MainActivity.DATA_RECEIVED,rollString,pitchString);
//deciBattery = Integer.parseInt(bateryStringArray[0],16);
//updatesStatus(BATTERY_UPDATE);
}
mContext.sendBroadcast(intent);
}
}
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
boolean enabled) {
if (mBTAdapter == null || mBTGatt == null) {
Log.d("ADebugTag", "BluetoothAdapter not initialized in set char");
return;
}
else {
//mGattChar = mBTGatt.getService()
mBTGatt.setCharacteristicNotification(characteristic, enabled);
// BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CharUUID);
// descriptor.setValue(descriptor.ENABLE_NOTIFICATION_VALUE);
// mBTGatt.writeDescriptor(descriptor);
}
}
/**
* Disconnects an existing connection or cancel a pending connection. The disconnection result
* is reported asynchronously through the
* {#code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
* callback.
*/
public void disconnect() {
if (mBTAdapter == null || mBTGatt == null) {
Log.d("ADebugTag", "BluetoothAdapter not initialized");
return;
}
mBTGatt.disconnect();
}
public List<BluetoothGattService> getSupportedGattServices() {
if (mBTGatt == null) return null;
return mBTGatt.getServices();
}
/**
* After using a given BLE device, the app must call this method to ensure resources are
* released properly.
*/
public void close() {
if (mBTGatt == null) {
return;
}
mBTGatt.close();
mBTGatt = null;
}
}
Why is it not scanning? found a place that says you have to wait 5 seconds between D/BluetoothAdapter: STATE_ON and start scan but couldn't catch it and don't know if it's the issue at all.
Thanks.

android BLE server code should be running in background as a service

I have create BLE app which includes client and server app. The code is running successfully. Now i want to run server code as a service so that Bluetooth is open every time and it can be searched by client app whenever required. The code used for server is as below. Please guide me to use this code as a service.
public class ServerActivity extends AppCompatActivity {
private static final String TAG = "ServerActivity";
private ActivityServerBinding mBinding;
private Handler mHandler;
private Handler mLogHandler;
private List<BluetoothDevice> mDevices;
private Map<String, byte[]> mClientConfigurations;
private BluetoothGattServer mGattServer;
private BluetoothManager mBluetoothManager;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothLeAdvertiser mBluetoothLeAdvertiser;
// Lifecycle
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new Handler();
mLogHandler = new Handler(Looper.getMainLooper());
mDevices = new ArrayList<>();
mClientConfigurations = new HashMap<>();
mBluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
mBluetoothAdapter = mBluetoothManager.getAdapter();
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_server);
mBinding.restartServerButton.setOnClickListener(v -> restartServer());
}
#Override
protected void onResume() {
super.onResume();
// Check if bluetooth is enabled
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
// Request user to enable it
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivity(enableBtIntent);
finish();
return;
}
// Check low energy support
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
// Get a newer device
log("No LE Support.");
finish();
return;
}
// Check advertising
if (!mBluetoothAdapter.isMultipleAdvertisementSupported()) {
// Unable to run the server on this device, get a better device
log("No Advertising Support.");
finish();
return;
}
mBluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
GattServerCallback gattServerCallback = new GattServerCallback();
mGattServer = mBluetoothManager.openGattServer(this, gattServerCallback);
Bundle bundle = getIntent().getExtras();
String plate_name = bundle.getString("PLATE_NAME");
mBluetoothAdapter.setName(plate_name);
Toast.makeText(getApplicationContext(),"PLATE NAME "+plate_name,Toast.LENGTH_LONG).show();
#SuppressLint("HardwareIds")
String deviceInfo = "Device Info"
+ "\nName: " + mBluetoothAdapter.getName()
+ "\nAddress: " + mBluetoothAdapter.getAddress();
mBinding.serverDeviceInfoTextView.setText(deviceInfo);
setupServer();
startAdvertising();
}
#Override
protected void onPause() {
super.onPause();
stopAdvertising();
stopServer();
}
// GattServer
private void setupServer() {
BluetoothGattService service = new BluetoothGattService(SERVICE_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY);
// Write characteristic
BluetoothGattCharacteristic writeCharacteristic = new BluetoothGattCharacteristic(CHARACTERISTIC_ECHO_UUID,
BluetoothGattCharacteristic.PROPERTY_WRITE,
// Somehow this is not necessary, the client can still enable notifications
// | BluetoothGattCharacteristic.PROPERTY_NOTIFY,
BluetoothGattCharacteristic.PERMISSION_WRITE);
// Characteristic with Descriptor
BluetoothGattCharacteristic notifyCharacteristic = new BluetoothGattCharacteristic(CHARACTERISTIC_TIME_UUID,
// Somehow this is not necessary, the client can still enable notifications
// BluetoothGattCharacteristic.PROPERTY_NOTIFY,
0, 0);
BluetoothGattDescriptor clientConfigurationDescriptor = new BluetoothGattDescriptor(CLIENT_CONFIGURATION_DESCRIPTOR_UUID,
BluetoothGattDescriptor.PERMISSION_READ | BluetoothGattDescriptor.PERMISSION_WRITE);
clientConfigurationDescriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
notifyCharacteristic.addDescriptor(clientConfigurationDescriptor);
service.addCharacteristic(writeCharacteristic);
service.addCharacteristic(notifyCharacteristic);
mGattServer.addService(service);
}
private void stopServer() {
if (mGattServer != null) {
mGattServer.close();
}
}
private void restartServer() {
stopAdvertising();
stopServer();
setupServer();
startAdvertising();
}
// Advertising
private void startAdvertising() {
if (mBluetoothLeAdvertiser == null) {
return;
}
AdvertiseSettings settings = new AdvertiseSettings.Builder()
.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED)
.setConnectable(true)
.setTimeout(0)
.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_LOW)
.build();
ParcelUuid parcelUuid = new ParcelUuid(SERVICE_UUID);
AdvertiseData data = new AdvertiseData.Builder()
.setIncludeDeviceName(true)
.addServiceUuid(parcelUuid)
.build();
mBluetoothLeAdvertiser.startAdvertising(settings, data, mAdvertiseCallback);
}
private void stopAdvertising() {
if (mBluetoothLeAdvertiser != null) {
mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback);
}
}
private AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback() {
#Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
log("Peripheral advertising started.");
}
#Override
public void onStartFailure(int errorCode) {
log("Peripheral advertising failed: " + errorCode);
}
};
// Notifications
private void notifyCharacteristicTime(byte[] value) {
notifyCharacteristic(value, CHARACTERISTIC_TIME_UUID);
}
private void notifyCharacteristic(byte[] value, UUID uuid) {
mHandler.post(() -> {
BluetoothGattService service = mGattServer.getService(SERVICE_UUID);
BluetoothGattCharacteristic characteristic = service.getCharacteristic(uuid);
log("Notifying characteristic " + characteristic.getUuid().toString()
+ ", new value: " + StringUtils.byteArrayInHexFormat(value));
characteristic.setValue(value);
// Indications require confirmation, notifications do not
boolean confirm = BluetoothUtils.requiresConfirmation(characteristic);
for (BluetoothDevice device : mDevices) {
if (clientEnabledNotifications(device, characteristic)) {
mGattServer.notifyCharacteristicChanged(device, characteristic, confirm);
}
}
});
}
private boolean clientEnabledNotifications(BluetoothDevice device, BluetoothGattCharacteristic characteristic) {
List<BluetoothGattDescriptor> descriptorList = characteristic.getDescriptors();
BluetoothGattDescriptor descriptor = BluetoothUtils.findClientConfigurationDescriptor(descriptorList);
if (descriptor == null) {
// There is no client configuration descriptor, treat as true
return true;
}
String deviceAddress = device.getAddress();
byte[] clientConfiguration = mClientConfigurations.get(deviceAddress);
if (clientConfiguration == null) {
// Descriptor has not been set
return false;
}
byte[] notificationEnabled = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
return clientConfiguration.length == notificationEnabled.length
&& (clientConfiguration[0] & notificationEnabled[0]) == notificationEnabled[0]
&& (clientConfiguration[1] & notificationEnabled[1]) == notificationEnabled[1];
}
// Gatt Server Actions
public void log(String msg) {
Log.d(TAG, msg);
mLogHandler.post(() -> {
mBinding.viewServerLog.logTextView.append(msg + "\n");
mBinding.viewServerLog.logScrollView.post(() -> mBinding.viewServerLog.logScrollView.fullScroll(View.FOCUS_DOWN));
if (msg.contains("Open")) {
ToneGenerator toneGen1 = new ToneGenerator(AudioManager.STREAM_MUSIC, 100);
toneGen1.startTone(ToneGenerator.TONE_CDMA_PIP, 150);
} else if (msg.contains("Close")) {
ToneGenerator toneGen1 = new ToneGenerator(AudioManager.STREAM_MUSIC, 100);
toneGen1.startTone(ToneGenerator.TONE_CDMA_PIP, 150);
toneGen1.startTone(ToneGenerator.TONE_CDMA_PIP, 300);
}
});
}
public void addDevice(BluetoothDevice device) {
log("Device added: " + device.getAddress());
mHandler.post(() -> mDevices.add(device));
}
public void removeDevice(BluetoothDevice device) {
log("Device removed: " + device.getAddress());
mHandler.post(() -> {
mDevices.remove(device);
String deviceAddress = device.getAddress();
mClientConfigurations.remove(deviceAddress);
});
}
public void addClientConfiguration(BluetoothDevice device, byte[] value) {
String deviceAddress = device.getAddress();
mClientConfigurations.put(deviceAddress, value);
}
public void sendResponse(BluetoothDevice device, int requestId, int status, int offset, byte[] value) {
mGattServer.sendResponse(device, requestId, status, 0, null);
}
public void notifyCharacteristicEcho(byte[] value) {
notifyCharacteristic(value, CHARACTERISTIC_ECHO_UUID);
}
// Gatt Callback
private class GattServerCallback extends BluetoothGattServerCallback {
#Override
public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
super.onConnectionStateChange(device, status, newState);
log("onConnectionStateChange " + device.getAddress()
+ "\nstatus " + status
+ "\nnewState " + newState);
if (newState == BluetoothProfile.STATE_CONNECTED) {
addDevice(device);
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
removeDevice(device);
}
}
// The Gatt will reject Characteristic Read requests that do not have the permission set,
// so there is no need to check inside the callback
#Override
public void onCharacteristicReadRequest(BluetoothDevice device,
int requestId,
int offset,
BluetoothGattCharacteristic characteristic) {
super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
log("onCharacteristicReadRequest " + characteristic.getUuid().toString());
if (BluetoothUtils.requiresResponse(characteristic)) {
// Unknown read characteristic requiring response, send failure
sendResponse(device, requestId, BluetoothGatt.GATT_FAILURE, 0, null);
}
// Not one of our characteristics or has NO_RESPONSE property set
}
// The Gatt will reject Characteristic Write requests that do not have the permission set,
// so there is no need to check inside the callback
#Override
public void onCharacteristicWriteRequest(BluetoothDevice device,
int requestId,
BluetoothGattCharacteristic characteristic,
boolean preparedWrite,
boolean responseNeeded,
int offset,
byte[] value) {
super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);
log("onCharacteristicWriteRequest" + characteristic.getUuid().toString()
+ "\nReceived: " + StringUtils.stringFromBytes(value));
if (CHARACTERISTIC_ECHO_UUID.equals(characteristic.getUuid())) {
sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null);
// Reverse message to differentiate original message & response
byte[] response = ByteUtils.reverse(value);
characteristic.setValue(response);
log("Sending: " + StringUtils.byteArrayInHexFormat(response));
notifyCharacteristicEcho(response);
}
}
// The Gatt will reject Descriptor Read requests that do not have the permission set,
// so there is no need to check inside the callback
#Override
public void onDescriptorReadRequest(BluetoothDevice device,
int requestId,
int offset,
BluetoothGattDescriptor descriptor) {
super.onDescriptorReadRequest(device, requestId, offset, descriptor);
log("onDescriptorReadRequest" + descriptor.getUuid().toString());
}
// The Gatt will reject Descriptor Write requests that do not have the permission set,
// so there is no need to check inside the callback
#Override
public void onDescriptorWriteRequest(BluetoothDevice device,
int requestId,
BluetoothGattDescriptor descriptor,
boolean preparedWrite,
boolean responseNeeded,
int offset,
byte[] value) {
super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value);
log("onDescriptorWriteRequest: " + descriptor.getUuid().toString()
+ "\nvalue: " + StringUtils.stringFromBytes(value));
if (CLIENT_CONFIGURATION_DESCRIPTOR_UUID.equals(descriptor.getUuid())) {
addClientConfiguration(device, value);
sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null);
}
}
#Override
public void onNotificationSent(BluetoothDevice device, int status) {
super.onNotificationSent(device, status);
log("onNotificationSent");
}
}
}
Use Foreground service with persistent notification. Your service will be keep on running.

Using BLE - Read GATT characteristics

I'm trying to read GATT characteristic values from a Bluetooth LE device (a Heart Rate bracelet). Its specs are:
Services
Characteristics
I have not yet figured out how to "read" the specifications and "translate" them into code.
I need to show on my app the heartbeats detected by the device. What is the way to read the GATT values? A code example would be much appreciated :)
Follow my actual source code.
SETUP THE BLUETOOT CONNECTION
private BluetoothAdapter mBluetoothAdapter;
private BluetoothGatt mBluetoothGatt;
private Handler mHandler;
private static final int REQUEST_ENABLE_BT = 1;
private static final long SCAN_PERIOD = 10000;
// ...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bluetooth);
mHandler = new Handler();
// BLE is supported?
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, "Bluetooth Low Energy non supportato", Toast.LENGTH_SHORT).show();
finish();
}
final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
// Bluetooth is supported?
if (mBluetoothAdapter == null) {
Toast.makeText(this, "Bluetooth non supportato", Toast.LENGTH_SHORT).show();
finish();
}
}
#Override
protected void onResume() {
super.onResume();
// Bluetooth is enabled?
if (!mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
scanLeDevice(true);
}
#Override
protected void onPause() {
super.onPause();
if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
scanLeDevice(false);
}
}
DISCOVER BLE DEVICES AND CONNECT WITH HEART RATE MONITOR
// Device scan callback.
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
runOnUiThread(new Runnable() {
#Override
public void run() {
Log.i(TAG, "Name: " + device.getName() + " (" + device.getAddress() + ")");
String deviceAddress = device.getAddress();
if (deviceAddress.equals("C0:19:37:54:9F:30")) {
connectToDevice(device);
}
}
});
}
};
public void connectToDevice(BluetoothDevice device) {
if (mBluetoothGatt == null) {
Log.i(TAG, "Attempting to connect to device " + device.getName() + " (" + device.getAddress() + ")");
mBluetoothGatt = device.connectGatt(this, true, gattCallback);
scanLeDevice(false);// will stop after first device detection
}
}
private final BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
Log.i(TAG, "Status: " + status);
switch (newState) {
case BluetoothProfile.STATE_CONNECTED:
Log.i(TAG, "STATE_CONNECTED");
//BluetoothDevice device = gatt.getDevice(); // Get device
gatt.discoverServices();
break;
case BluetoothProfile.STATE_DISCONNECTED:
Log.e(TAG, "STATE_DISCONNECTED");
break;
default:
Log.e(TAG, "STATE_OTHER");
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
List<BluetoothGattService> services = gatt.getServices();
Log.i(TAG, "Services: " + services.toString());
BluetoothGattCharacteristic bpm = services.get(2).getCharacteristics().get(0);
gatt.readCharacteristic(services.get(0).getCharacteristics().get(0));
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
// my attempt to read and print characteristics
byte[] charValue = characteristic.getValue();
byte flag = charValue[0];
Log.i(TAG, "Characteristic: " + flag);
//gatt.disconnect();
}
};
try with this inside gattCallback:
#Override
public synchronized void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
final byte[] dataInput = characteristic.getValue();
}
EDIT
And I think you are going to receive a byte with the hear rate data, use this function to get the int value:
public int unsignedByteToInt(byte b) {
return b & 0xFF;
}
And call it inside onCharacteristicChanged():
final byte[] dataInput = characteristic.getValue();
int hearRate = unsignedByteToInt(dataInput);
EDIT 2
Create a notification listener for the heart rate:
public void setHeartRateNotification(boolean enable){
String uuidHRCharacteristic = "YOUR CHARACTERISTIC";
BluetoothGattService mBluetoothLeService = null;
BluetoothGattCharacteristic mBluetoothGattCharacteristic = null;
for (BluetoothGattService service : mBluetoothGatt.getServices()) {
if ((service == null) || (service.getUuid() == null)) {
continue;
}
if (uuidAccelService.equalsIgnoreCase(service.getUuid().toString())) {
mBluetoothLeService = service;
}
}
if(mBluetoothLeService!=null) {
mBluetoothGattCharacteristic =
mBluetoothLeService.getCharacteristic(UUID.fromString(uuidHRCharacteristic));
}
else{
Log.i("Test","mBluetoothLeService is null");
}
if(mBluetoothGattCharacteristic!=null) {
setCharacteristicNotification(mBluetoothGattCharacteristic, enable);
Log.i("Test","setCharacteristicNotification:"+true);
}
else{
Log.i("Test","mBluetoothGattCharacteristic is null");
}
}
And set it onServiceDiscover inside gattCallback:
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
Log.i("Test", "onServicesDiscovered received: " + status);
setHeartRateNotification(true);
}

Writing data to Bluetooth LE characteristic in Android

Although similar questions have been asked but it's slightly different. I know how to pass data to a connected BLE device but I think I'm doing something wrong for which I need help.
The code below contains all the methods from my class that is extending BroadcastReceiver.
I scan and connect to a device specified by `PEN_ADDRESS`.
In `onServicesDiscovered` method I look for a service whose `UUID` contains `abcd`.
Then I loop through the characteristics of this services and look for three characteristics with specific strings in their `UUID`.
The third characteristic is a writable characteristic in which I'm trying to write data by calling the method `writeCharac(mGatt,writeChar1,123);`
The data `123` passed above is just a dummy data.
I debugged my code while trying writing to this characteristic but on putting breakpoints inside the writeCharac method, I found that the status value is false, indicating that the write was not successful.
Am I missing something here? Please do help!
public class BackgroundReceiverFire extends BroadcastReceiver {
Context context;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothGatt mGatt;
private BluetoothLeService mBluetoothLeService;
private boolean mScanning;
private final String TAG = "READING: ";
private BluetoothDevice mDevice;
private Handler mHandler;
private static final int REQUEST_ENABLE_BT = 1;
private final String PEN_ADDRESS = "FB:23:AF:42:5C:56";
// Stops scanning after 10 seconds.
private static final long SCAN_PERIOD = 10000;
public void onReceive(Context context, Intent intent) {
this.context = context;
Toast.makeText(context, "Started Scanning", LENGTH_SHORT).show();
initializeBluetooth();
startScan();
}
private void initializeBluetooth() {
mHandler = new Handler();
// Use this check to determine whether BLE is supported on the device. Then you can
// selectively disable BLE-related features.
// Initializes a Bluetooth adapter. For API level 18 and above, get a reference to
// BluetoothAdapter through BluetoothManager.
final BluetoothManager bluetoothManager =
(BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
// Checks if Bluetooth is supported on the device.
if (mBluetoothAdapter == null) {
Toast.makeText(this.context, "No Bluetooth", LENGTH_SHORT).show();
return;
}
}
private void startScan() {
scanLeDevice(true);
}
private void stopScan() {
scanLeDevice(false);
}
private void scanLeDevice(final boolean enable) {
if (enable) {
// Stops scanning after a pre-defined scan period.
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}, SCAN_PERIOD);
mScanning = true;
//Scanning for the device
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
if (device.getAddress().matches(PEN_ADDRESS)) {
connectBluetooth(device);
Toast.makeText(context, "Device Found: " + device.getAddress(), Toast.LENGTH_LONG).show();
}
}
};
private void connectBluetooth(BluetoothDevice insulinPen) {
if (mGatt == null) {
Log.d("connectToDevice", "connecting to device: " + insulinPen.toString());
mDevice = insulinPen;
mGatt = insulinPen.connectGatt(context, true, gattCallback);
scanLeDevice(false);// will stop after first device detection
}
}
private void enableBluetooth() {
if (!mBluetoothAdapter.isEnabled()) {
mBluetoothAdapter.enable();
}
scanLeDevice(true);
}
private void disableBluetooth() {
if (mBluetoothAdapter.isEnabled()) {
mBluetoothAdapter.disable();
}
}
private final BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
Log.i("onConnectionStateChange", "Status: " + status);
switch (newState) {
case BluetoothProfile.STATE_CONNECTED:
gatt.discoverServices();
break;
case BluetoothProfile.STATE_DISCONNECTED:
Log.e("gattCallback", "STATE_DISCONNECTED");
Log.i("gattCallback", "reconnecting...");
BluetoothDevice mDevice = gatt.getDevice();
mGatt = null;
connectBluetooth(mDevice);
break;
default:
Log.e("gattCallback", "STATE_OTHER");
break;
}
}
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
mGatt = gatt;
List<BluetoothGattService> services = mGatt.getServices();
Log.i("onServicesDiscovered", services.toString());
Iterator<BluetoothGattService> serviceIterator = services.iterator();
while(serviceIterator.hasNext()){
BluetoothGattService bleService = serviceIterator.next();
if(bleService.getUuid().toString().contains("abcd")){
//Toast.makeText(context,"Got the service",Toast.LENGTH_SHORT);
BluetoothGattCharacteristic readChar1 = bleService.getCharacteristics().get(0);
for (BluetoothGattDescriptor descriptor : readChar1.getDescriptors()) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
mGatt.writeDescriptor(descriptor);
mGatt.setCharacteristicNotification(readChar1, true);
}
//mGatt.readCharacteristic(readChar1);
BluetoothGattCharacteristic readChar2 = bleService.getCharacteristics().get(1);
for (BluetoothGattDescriptor descriptor : readChar2.getDescriptors()) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
mGatt.writeDescriptor(descriptor);
mGatt.setCharacteristicNotification(readChar2, true);
}
//mGatt.readCharacteristic(readChar2);
BluetoothGattCharacteristic writeChar1 = bleService.getCharacteristics().get(2);
for (BluetoothGattDescriptor descriptor : writeChar1.getDescriptors()) {
descriptor.setValue( BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
mGatt.writeDescriptor(descriptor);
mGatt.setCharacteristicNotification(writeChar1, true);
}
writeCharac(mGatt,writeChar1,123);
}
}
//gatt.readCharacteristic(therm_char);
}
public void writeCharac(BluetoothGatt gatt, BluetoothGattCharacteristic charac, int value ){
if (mBluetoothAdapter == null || gatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
/*
BluetoothGattCharacteristic charac = gattService
.getCharacteristic(uuid);
*/
if (charac == null) {
Log.e(TAG, "char not found!");
}
int unixTime = value;
String unixTimeString = Integer.toHexString(unixTime);
byte[] byteArray = hexStringToByteArray(unixTimeString);
charac.setValue(byteArray);
boolean status = mGatt.writeCharacteristic(charac);
if(status){
Toast.makeText(context,"Written Successfully",Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(context,"Error writing characteristic",Toast.LENGTH_SHORT).show();
}
}
public byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len/2];
for(int i = 0; i < len; i+=2){
data[i/2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16));
}
return data;
}
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic
characteristic, int status) {
Log.i("onCharacteristicRead", characteristic.toString());
String characteristicValue = characteristic.getValue().toString();
Log.d("CHARACTERISTIC VALUE: ", characteristicValue);
gatt.disconnect();
}
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic
characteristic) {
String value = characteristic.getValue().toString();
Log.d(TAG,value);
}
};
Although the BLE API is asynchronous in nature, the actual signal transmission is inevitably synchronous. You have to wait for the previous connect/write/read call to callback before starting any connecting/writing/reading operation.
In your code onServicesDiscovered(BluetoothGatt gatt, int status) function, you called mGatt.writeDescriptor(descriptor) twice before trying to write the characteristic. The API will refuse to start your write request as it is being busy writing the descriptor, and return false for your mGatt.writeCharacteristic(charac) call.
So just wait for the writeDescriptor to callback before calling writeCharacteristic. This nature is not well documented but you can find some source here and here.
Thanks #reTs and #pooja for suggestions. The issue was due to these two lines
mGatt = nullconnectBluetooth(mDevice);
in STATE_DISCONNECTED. I had figured it out but don't remember the exact reason. Posting it just in case it is helpful.

BLuetooth Gatt Callback not working with new API for Lollipop

I currently have a method which writes to the BLE devices to beep it. My Bluetooth Callback goes as follows :
ReadCharacteristic rc = new ReadCharacteristic(context, ds.getMacAddress(), serviceUUID, UUID.fromString(myUUID), "") {
#Override
public void onRead() {
Log.w(TAG, "callDevice onRead");
try{Thread.sleep(1000);}catch(InterruptedException ex){}
WriteCharacteristic wc = new WriteCharacteristic(activity, context, getMacAddress(), serviceUUID, UUID.fromString(myUUID), ""){
#Override
public void onWrite(){
Log.w(TAG, "callDevice onWrite");
}
#Override
public void onError(){
Log.w(TAG, "callDevice onWrite-onError");
}
};
// Store data in writeBuffer
wc.writeCharacteristic(writeBuffer);
}
#Override
public void onError(){
Log.w(TAG, "callDevice onRead-onError");
}
};
rc.readCharacteristic();
My ReadCharacteristic implementation is as follows :
public class ReadCharacteristic extends BluetoothGattCallback {
public ReadCharacteristic(Context context, String macAddress, UUID service, UUID characteristic, Object tag) {
mMacAddress = macAddress;
mService = service;
mCharacteristic = characteristic;
mTag = tag;
mContext = context;
this.activity =activity;
final BluetoothManager bluetoothManager =
(BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
}
final private static String TAG = "ReadCharacteristic";
private Object mTag;
private String mMacAddress;
private UUID mService;
private UUID mCharacteristic;
private byte[] mValue;
private Activity activity;
private BluetoothAdapter mBluetoothAdapter;
private Context mContext;
private int retry = 5;
public String getMacAddress() {
return mMacAddress;
}
public UUID getService() {
return mService;
}
public UUID getCharacteristic() {
return mCharacteristic;
}
public byte[] getValue() { return mValue; }
public void onRead() {
Log.w(TAG, "onRead: " + getDataHex(getValue()));
}
public void onError() {
Log.w(TAG, "onError");
}
public void readCharacteristic(){
if (retry == 0)
{
onError();
return;
}
retry--;
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(getMacAddress());
if (device != null) {
Log.w(TAG, "Starting Read [" + getService() + "|" + getCharacteristic() + "]");
final ReadCharacteristic rc = ReadCharacteristic.this;
device.connectGatt(mContext, false, rc);
}
}
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
Log.w(TAG,"onConnectionStateChange [" + status + "|" + newState + "]");
if ((newState == 2)&&(status ==0)) {
gatt.discoverServices();
}
else{
Log.w(TAG, "[" + status + "]");
// gatt.disconnect();
gatt.close();
try
{
Thread.sleep(2000);
}
catch(Exception e)
{
}
readCharacteristic();
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
Log.w(TAG,"onServicesDiscovered [" + status + "]");
BluetoothGattService bgs = gatt.getService(getService());
if (bgs != null) {
BluetoothGattCharacteristic bgc = bgs.getCharacteristic(getCharacteristic());
gatt.readCharacteristic(bgc);
}
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
Log.w(TAG,"onCharacteristicRead [" + status + "]");
if (status == BluetoothGatt.GATT_SUCCESS) {
mValue = characteristic.getValue();
Log.w(TAG,"onCharacteristicRead [" + mValue + "]");
gatt.disconnect();
gatt.close();
onRead();
}
else {
gatt.disconnect();
gatt.close();
}
}
}
This current method works perfectly fine for devices running KitKat and below. But when I run the same function on Lollipop, it beeps the device a couple of times and then stops working. From then on wards, whenever I try to connect, it says the device is disconnected and gives me an error code of 257 in OnConnectionStateChanged method.
I also get this error whenever I call this method -
04-20 14:14:23.503 12329-12384/com.webble.xy W/BluetoothGatt﹕ Unhandled exception in callback
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.bluetooth.BluetoothGattCallback.onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)' on a null object reference
at android.bluetooth.BluetoothGatt$1.onClientConnectionState(BluetoothGatt.java:181)
at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:70)
at android.os.Binder.execTransact(Binder.java:446)
IS there anyone who has faced the same problem? I never encountered the object to be null when ever I tried debugging.
The problem has been reported to Google as Issue 183108: NullPointerException in BluetoothGatt.java when disconnecting and closing.
A workaround is to call disconnect() when you want to close BLE connection - and then only call close() in the onConnectionStateChange callback:
public void shutdown() {
try {
mBluetoothGatt.disconnect();
} catch (Exception e) {
Log.d(TAG, "disconnect ignoring: " + e);
}
}
private final BluetoothGattCallback mGattCallback =
new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt,
int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
try {
gatt.close();
} catch (Exception e) {
Log.d(TAG, "close ignoring: " + e);
}
}
}
Here is my full source code (a class doing normal scan, directed scan - and discovering services):
public class BleObject {
public static final String ACTION_BLUETOOTH_ENABLED = "action.bluetooth.enabled";
public static final String ACTION_BLUETOOTH_DISABLED = "action.bluetooth.disabled";
public static final String ACTION_DEVICE_FOUND = "action.device.found";
public static final String ACTION_DEVICE_BONDED = "action.device.bonded";
public static final String ACTION_DEVICE_CONNECTED = "action.device.connected";
public static final String ACTION_DEVICE_DISCONNECTED = "action.device.disconnected";
public static final String ACTION_POSITION_READ = "action.position.read";
public static final String EXTRA_BLUETOOTH_DEVICE = "extra.bluetooth.device";
public static final String EXTRA_BLUETOOTH_RSSI = "extra.bluetooth.rssi";
private Context mContext;
private IntentFilter mIntentFilter;
private LocalBroadcastManager mBroadcastManager;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothGatt mBluetoothGatt;
private BluetoothLeScanner mScanner;
private ScanSettings mSettings;
private List<ScanFilter> mScanFilters;
private Handler mConnectHandler;
public BleObject(Context context) {
mContext = context;
if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Log.d(TAG, "BLE not supported");
return;
}
BluetoothManager bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
if (mBluetoothAdapter == null) {
Log.d(TAG, "BLE not accessible");
return;
}
mIntentFilter = new IntentFilter();
mIntentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
mIntentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
mSettings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
mScanFilters = new ArrayList<ScanFilter>();
mConnectHandler = new Handler();
mBroadcastManager = LocalBroadcastManager.getInstance(context);
}
public boolean isEnabled() {
return (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled());
}
private ScanCallback mScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
processResult(result);
}
#Override
public void onBatchScanResults(List<ScanResult> results) {
for (ScanResult result: results) {
processResult(result);
}
}
private void processResult(ScanResult result) {
if (result == null)
return;
BluetoothDevice device = result.getDevice();
if (device == null)
return;
Intent i = new Intent(Utils.ACTION_DEVICE_FOUND);
i.putExtra(Utils.EXTRA_BLUETOOTH_DEVICE, device);
i.putExtra(Utils.EXTRA_BLUETOOTH_RSSI, result.getRssi());
mBroadcastManager.sendBroadcast(i);
}
};
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
if (gatt == null)
return;
BluetoothDevice device = gatt.getDevice();
if (device == null)
return;
Log.d(TAG, "BluetoothProfile.STATE_CONNECTED: " + device);
Intent i = new Intent(Utils.ACTION_DEVICE_CONNECTED);
i.putExtra(Utils.EXTRA_BLUETOOTH_DEVICE, device);
mBroadcastManager.sendBroadcast(i);
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.d(TAG, "BluetoothProfile.STATE_DISCONNECTED");
Intent i = new Intent(Utils.ACTION_DEVICE_DISCONNECTED);
mBroadcastManager.sendBroadcast(i);
// Issue 183108: https://code.google.com/p/android/issues/detail?id=183108
try {
gatt.close();
} catch (Exception e) {
Log.d(TAG, "close ignoring: " + e);
}
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (gatt == null)
return;
for (BluetoothGattService service: gatt.getServices()) {
Log.d(TAG, "service: " + service.getUuid());
for (BluetoothGattCharacteristic chr: service.getCharacteristics()) {
Log.d(TAG, "char: " + chr.getUuid());
}
}
}
};
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
switch (state) {
case BluetoothAdapter.STATE_TURNING_OFF: {
Log.d(TAG, "BluetoothAdapter.STATE_TURNING_OFF");
break;
}
case BluetoothAdapter.STATE_OFF: {
Log.d(TAG, "BluetoothAdapter.STATE_OFF");
Intent i = new Intent(Utils.ACTION_BLUETOOTH_DISABLED);
mBroadcastManager.sendBroadcast(i);
break;
}
case BluetoothAdapter.STATE_TURNING_ON: {
Log.d(TAG, "BluetoothAdapter.STATE_TURNING_ON");
break;
}
case BluetoothAdapter.STATE_ON: {
Log.d(TAG, "BluetoothAdapter.STATE_ON");
Intent i = new Intent(Utils.ACTION_BLUETOOTH_ENABLED);
mBroadcastManager.sendBroadcast(i);
break;
}
}
} else 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);
if (state == BluetoothDevice.BOND_BONDED &&
prevState == BluetoothDevice.BOND_BONDING) {
if (mBluetoothGatt != null) {
BluetoothDevice device = mBluetoothGatt.getDevice();
if (device == null)
return;
Intent i = new Intent(Utils.ACTION_DEVICE_BONDED);
i.putExtra(Utils.EXTRA_BLUETOOTH_DEVICE, device);
mBroadcastManager.sendBroadcast(i);
}
}
}
}
};
// scan for all BLE devices nearby
public void startScanning() {
Log.d(TAG, "startScanning");
mScanFilters.clear();
// create the scanner here, rather than in init() -
// because otherwise app crashes when Bluetooth is switched on
mScanner = mBluetoothAdapter.getBluetoothLeScanner();
mScanner.startScan(mScanFilters, mSettings, mScanCallback);
}
// scan for a certain BLE device and after delay
public void startScanning(final String address) {
Log.d(TAG, "startScanning for " + address);
mScanFilters.clear();
mScanFilters.add(new ScanFilter.Builder().setDeviceAddress(address).build());
// create the scanner here, rather than in init() -
// because otherwise app crashes when Bluetooth is switched on
mScanner = mBluetoothAdapter.getBluetoothLeScanner();
mScanner.startScan(mScanFilters, mSettings, mScanCallback);
}
public void stopScanning() {
Log.d(TAG, "stopScanning");
if (mScanner != null) {
mScanner.stopScan(mScanCallback);
mScanner = null;
}
mScanFilters.clear();
}
public void connect(final BluetoothDevice device) {
Log.d(TAG, "connect: " + device.getAddress() + ", mBluetoothGatt: " + mBluetoothGatt);
mConnectHandler.post(new Runnable() {
#Override
public void run() {
setPin(device, Utils.PIN);
mBluetoothGatt = device.connectGatt(mContext, true, mGattCallback);
}
});
}
private void setPin(BluetoothDevice device, String pin) {
if (device == null || pin == null || pin.length() < 4)
return;
try {
device.setPin(pin.getBytes("UTF8"));
} catch (Exception e) {
Utils.logw("setPin ignoring: " + e);
}
}
// called on successful device connection and will toggle reading coordinates
public void discoverServices() {
if (mBluetoothGatt != null)
mBluetoothGatt.discoverServices();
}
public boolean isBonded(BluetoothDevice device) {
Set<BluetoothDevice> bondedDevices = mBluetoothAdapter.getBondedDevices();
if (bondedDevices == null || bondedDevices.size() == 0)
return false;
for (BluetoothDevice bondedDevice: bondedDevices) {
Log.d(TAG, "isBonded bondedDevice: " + bondedDevice);
if (bondedDevice.equals(device)) {
Log.d(TAG, "Found bonded device: " + device);
return true;
}
}
return false;
}
public void startup() {
try {
mContext.registerReceiver(mReceiver, mIntentFilter);
} catch (Exception e) {
Log.d(TAG, "registerReceiver ignoring: " + e);
}
}
public void shutdown() {
Log.d(TAG, "BleObject shutdown");
try {
mContext.unregisterReceiver(mReceiver);
} catch (Exception e) {
Log.d(TAG, "unregisterReceiver ignoring: " + e);
}
try {
stopScanning();
} catch (Exception e) {
Log.d(TAG, "stopScanning ignoring: " + e);
}
try {
mBluetoothGatt.disconnect();
} catch (Exception e) {
Log.d(TAG, "disconnect ignoring: " + e);
}
mConnectHandler.removeCallbacksAndMessages(null);
}
}
On each BLE event it broadcasts intents via LocalBroadcastManager.
Thanks for pointing the problem Shashank.
I have looked at the Google example and followed what they recommend, it works like a charm with my device.
You should call the close() function in your onUnbind and the disconnect() when you need to, for example when you quit your application.

Categories

Resources