Android BLE onCharacteristicChanged() using notify not triggered - java

I made an app that communicates with an device via BLE. I wrote everything that was required for it's communication. The device receives the sent value via characteristic.setValue() , but doesn't return feedback in the method onCharacteristicChanged as is supposed to, in despite that has the descriptor set for notification. For instance i send "GTN" and should get "GTN deviceId" (like on iOS), but it calls just onCharacteristicWrite(). Checked also what properties the characteristic does support. It supports PROPERTY_NOTIFY & PROPERTY_WRITE_NO_RESPONSE. I ran out of ideas why it doesn't want to get that callback message. Can someone check and try to tell me if I made some big mistakes while sending/subscribing ??
MainActivity.java
public class MainActivity extends AppCompatActivity {
private static final int REQUEST_LOCATION_PERMISSION = 1;
private static final int REQUEST_ENABLE_BLUETOOTH = 2;
private static final int REQUEST_ENABLE_LOCATION = 3;
private static final int BLUETOOTH_ENABLED = -1;
private static final int LOCATION_ENABLED = -1;
private static final int MANUFACTURER_ID = xxxxx;
private static final UUID SERVICE =xxxxxxxxxxxxx;
private static final UUID CHARACTERISTIC =xxxxxxxxxxxxxxxxx;
private static final UUID DESCRIPTOR = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
private final static String TAG = "BLE";
private byte[] mfrData;
private BluetoothManager bluetoothManager;
private BluetoothAdapter bluetoothAdapter;
private BluetoothGatt bluetoothGatt;
private BluetoothLeScanner bluetoothLeScanner;
private BluetoothGattService customService;
private BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
if (newState == BluetoothProfile.STATE_CONNECTED) {
gatt.discoverServices();
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
BluetoothGattService service = gatt.getService(SERVICE);
BluetoothGattCharacteristic characteristic = service.getCharacteristic(CHARACTERISTIC);
gatt.setCharacteristicNotification(characteristic, true);
enableDataNotifications(gatt, characteristic);
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
Log.d(TAG, "onCharacteristicRead: ");
}
#Override
public void onCharacteristicWrite(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
Log.d(TAG, "onCharacteristicWrite: ");
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
Log.d(TAG, "onCharacteristicChanged: ");
}
#Override
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorRead(gatt, descriptor, status);
Log.d(TAG, "onDescriptorRead: ");
}
#Override
public void onDescriptorWrite(final BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorWrite(gatt, descriptor, status);
Log.d(TAG, "onDescriptorWrite: ");
checkCharacteristicProperties(gatt.getService(SERVICE).getCharacteristic(CHARACTERISTIC));
new Thread(new Runnable() {
#Override
public void run() {
writeDataToCharCommand("GTN", gatt);
}
}).start();
}
#Override
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
super.onReliableWriteCompleted(gatt, status);
Log.d(TAG, "onReliableWriteCompleted: ");
}
#Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
super.onReadRemoteRssi(gatt, rssi, status);
Log.d(TAG, "onReadRemoteRssi: ");
}
#Override
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
super.onMtuChanged(gatt, mtu, status);
Log.d(TAG, "onMtuChanged: ");
}
};
private ScanCallback scanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
if (result.getScanRecord() != null && result.getScanRecord().getManufacturerSpecificData(MANUFACTURER_ID) != null) {
Log.d(TAG, "onScanResult: found device");
bluetoothLeScanner.stopScan(scanCallback);
boolean isBonded = result.getDevice().createBond();
if (isBonded) {
bluetoothGatt = result.getDevice().connectGatt(MainActivity.this, false, bluetoothGattCallback, BluetoothDevice.TRANSPORT_LE);
}
}
}
#Override
public void onBatchScanResults(List<ScanResult> results) {
super.onBatchScanResults(results);
Log.d(TAG, "onBatchScanResults: ");
}
#Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
Log.d(TAG, "onScanFailed: ");
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setupBluetoothData();
checkIfSupportedBluetooth();
checkLocationPermission();
}
private void checkCharacteristicProperties(BluetoothGattCharacteristic pChar) {
Log.d(TAG, "checkCharacteristicProperties: PROPERTY_READ " + ((pChar.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) != 0));
Log.d(TAG, "checkCharacteristicProperties: PROPERTY_NOTIFY " + ((pChar.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0));
Log.d(TAG, "checkCharacteristicProperties: PROPERTY_BROADCAST " + ((pChar.getProperties() & BluetoothGattCharacteristic.PROPERTY_BROADCAST) != 0));
Log.d(TAG, "checkCharacteristicProperties: PROPERTY_EXTENDED_PROPS " + ((pChar.getProperties() & BluetoothGattCharacteristic.PROPERTY_EXTENDED_PROPS) != 0));
Log.d(TAG, "checkCharacteristicProperties: PROPERTY_INDICATE " + ((pChar.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0));
Log.d(TAG, "checkCharacteristicProperties: PROPERTY_SIGNED_WRITE " + ((pChar.getProperties() & BluetoothGattCharacteristic.PROPERTY_SIGNED_WRITE) != 0));
Log.d(TAG, "checkCharacteristicProperties: PROPERTY_WRITE " + ((pChar.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) != 0));
Log.d(TAG, "checkCharacteristicProperties: PROPERTY_WRITE_NO_RESPONSE " + ((pChar.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) != 0));
}
private void setupBluetoothData() {
bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
bluetoothAdapter = bluetoothManager.getAdapter();
}
private void checkIfSupportedBluetooth() {
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
finish();
}
if (bluetoothAdapter == null) {
Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();
finish();
}
}
private void checkLocationPermission() {
if (!hasGrantedLocationPermission()) {
requestLocationPermission();
} else {
checkIfBluetoothEnabled();
}
}
private boolean hasGrantedLocationPermission() {
int result = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION);
return result == PackageManager.PERMISSION_GRANTED;
}
private void requestLocationPermission() {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_LOCATION_PERMISSION);
}
private void checkIfBluetoothEnabled() {
if (!bluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BLUETOOTH);
} else {
displayLocationSettingsRequest();
}
}
private void displayLocationSettingsRequest() {
GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API).build();
googleApiClient.connect();
LocationRequest locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setInterval(10000);
locationRequest.setFastestInterval(10000 / 2);
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder().addLocationRequest(locationRequest);
builder.setAlwaysShow(true);
PendingResult<LocationSettingsResult> result = LocationServices.SettingsApi.checkLocationSettings(googleApiClient, builder.build());
result.setResultCallback(new ResultCallback<LocationSettingsResult>() {
#Override
public void onResult(#NonNull LocationSettingsResult locationSettingsResult) {
final Status status = locationSettingsResult.getStatus();
switch (status.getStatusCode()) {
case LocationSettingsStatusCodes.SUCCESS: {
scanForDevices();
break;
}
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED: {
try {
status.startResolutionForResult(MainActivity.this, REQUEST_ENABLE_LOCATION);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
break;
}
case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE: {
break;
}
}
}
});
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQUEST_ENABLE_BLUETOOTH: {
if (resultCode == BLUETOOTH_ENABLED) {
displayLocationSettingsRequest();
} else {
Toast.makeText(this, "Restart app", Toast.LENGTH_SHORT).show();
}
break;
}
case REQUEST_ENABLE_LOCATION: {
if (resultCode == LOCATION_ENABLED) {
scanForDevices();
} else {
Toast.makeText(this, "Restart app", Toast.LENGTH_SHORT).show();
}
break;
}
default: {
break;
}
}
}
private void scanForDevices() {
Log.d(TAG, "scanForDevices: ");
Toast.makeText(this, "Scanning", Toast.LENGTH_SHORT).show();
ScanSettings scanSettings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.build();
ScanFilter scanFilter = new ScanFilter.Builder()
.setManufacturerData(0x0361, mfrData)
.build();
bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
bluetoothLeScanner.startScan(Collections.singletonList(scanFilter),
scanSettings, scanCallback);
}
private String translate(byte[] value) throws UnsupportedEncodingException {
return new String(value, StandardCharsets.UTF_8);
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case REQUEST_LOCATION_PERMISSION: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
checkIfBluetoothEnabled();
} else {
Toast.makeText(this, "Restart app", Toast.LENGTH_SHORT).show();
}
break;
}
default: {
break;
}
}
}
private void writeDataToCharCommand(String data, BluetoothGatt gatt) {
Log.d(TAG, "writeDataToCharCommand: ");
gatt.getService(SERVICE).getCharacteristic(CHARACTERISTIC).setValue(data);
gatt.writeCharacteristic(gatt.getService(SERVICE).getCharacteristic(CHARACTERISTIC));
}
private void enableDataNotifications(BluetoothGatt gatt, BluetoothGattCharacteristic gattCharacteristic) {
Log.d(TAG, "enableDataNotifications: ");
boolean enabled = gatt.setCharacteristicNotification(gattCharacteristic, true);
if (enabled) {
BluetoothGattDescriptor descriptor = gattCharacteristic.getDescriptor(DESCRIPTOR);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor);
}
}
}

i have notification work flawlessly with BLE.
i created this method to mark a characteristics notification enabled.
public void markCharForNotification(BluetoothGattCharacteristic readableChar) {
mBluetoothGatt.setCharacteristicNotification(readableChar, true);
List<BluetoothGattDescriptor> listDescr = readableChar.getDescriptors();
BluetoothGattDescriptor descriptor = listDescr.get(0);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
}
hope it helps

You might want to run the checkCharacteristicProperties on the notify characteristic also.
Anyway, the correct way to enable notifications is to check the properties first and enable notify or indicate, based on those properties:
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID);
int properties = characteristic.getProperties();
if (descriptor != null) {
if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
} else if ((properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) > 0) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
}
gatt.writeDescriptor(descriptor);
Hope this helps.

Related

Android - Bluetooth receiver not working on advertising device

This is my first app working with Bluetooth in Androind and I'm having a kinda unique problem: the bluetooth receiver isn't working on the device that is advertising the bluetooth service.
I have tested the application in 2 phones at the same time (I'll call them phone A and B to explain better). At first I start the advertising with the phone A, then I start the discovery with the phone B and finally I press the button in phone B to send data. This button should first start the Gatt connection and if it's working it should then broadcast a message that confirms the connection. To see it I have used a Log in the Broadcast receiver but the result I get is this messsagge appearing only in the logcat of the phone B but not on the one of phone A.
I have looked at a lot of examples and post on Stackoverflow but I can't seem to find the solution to this problem.
So I really can't find what the real problem here is. Maybe I'm just using badly the Bluetooth classes or I just lack knowledge. In any case, here there is all the code of the MainActivity as it is the only class of this simple project.
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private TextView mText;
private Button mAdvertiseButton;
private Button mDiscoverButton;
private Button mSendButton;
private String TAG = "INFOBLERESULTS";
private BluetoothLeScanner mBluetoothLeScanner;
private BluetoothDevice bluetoothDevice;
private Handler mHandler = new Handler();
private ScanCallback mScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
Log.d(TAG, result.getDevice().getAddress());
super.onScanResult(callbackType, result);
if( result == null
|| result.getDevice() == null
|| TextUtils.isEmpty(result.getDevice().getAddress()) )
return;
StringBuilder builder = new StringBuilder( result.getDevice().getAddress() );
builder.append("\n").append(result.getDevice().getName());
//builder.append("\n").append(new String(result.getScanRecord().getServiceData(result.getScanRecord().getServiceUuids().get(0)), Charset.forName("UTF-8")));
mText.setText(builder.toString());
bluetoothDevice = result.getDevice();
bluetoothLeService = new BluetoothLeService();
bluetoothLeService.setAddress(result.getDevice().getAddress());
}
#Override
public void onBatchScanResults(List<ScanResult> results) {
super.onBatchScanResults(results);
}
#Override
public void onScanFailed(int errorCode) {
Log.e( TAG, "Discovery onScanFailed: " + errorCode );
super.onScanFailed(errorCode);
}
};
private BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
private boolean connected = false;
//Inner classes
class BluetoothLeService extends Service {
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";
private static final int STATE_DISCONNECTED = 0;
private static final int STATE_CONNECTED = 2;
private int connectionState;
public Context ctx;
protected BluetoothGatt bluetoothGatt;
protected final BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
Log.i(TAG, "GATT status = "+ status + " newState = " + newState);
if(status == BluetoothGatt.GATT_SUCCESS){
if (newState == BluetoothProfile.STATE_CONNECTED) {
// successfully connected to the GATT Server
connectionState = STATE_CONNECTED;
broadcastUpdate(ACTION_GATT_CONNECTED);
bluetoothGatt = gatt;
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
// disconnected from the GATT Server
connectionState = STATE_DISCONNECTED;
broadcastUpdate(ACTION_GATT_DISCONNECTED);
gatt.close();
}
}else{
gatt.close();
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
} else {
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}
#Override
public void onCharacteristicRead(
BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status
) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
}
#Override
public void onCharacteristicChanged(
BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic
) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
};
private Binder binder = new LocalBinder();
private String address = "";
public void setAddress(String address) {
this.address = address;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return binder;
}
#Override
public boolean onUnbind(Intent intent) {
close();
return super.onUnbind(intent);
}
private void close() {
if (bluetoothGatt == null) {
return;
}
bluetoothGatt.close();
bluetoothGatt = null;
}
public boolean connect() {
if (bluetoothAdapter == null || this.address == null || this.address.equals("")) {
Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
return false;
}
try {
final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address);
bluetoothGatt = device.connectGatt(MainActivity.this.getApplicationContext(), false, gattCallback);
Log.d(TAG,"GATT "+ bluetoothGatt);
return true;
} catch (IllegalArgumentException exception) {
Log.w(TAG, "Device not found with provided address.");
return false;
}
}
private void broadcastUpdate(final String action) {
final Intent intent = new Intent(action);
Log.i(TAG, intent + "");
MainActivity.this.getApplicationContext().sendBroadcast(intent);
}
private void broadcastUpdate(final String action, BluetoothGattCharacteristic characteristic) {
final Intent intent = new Intent(action);
sendBroadcast(intent);
}
public List<BluetoothGattService> getSupportedGattServices() {
if (bluetoothGatt == null) return null;
return bluetoothGatt.getServices();
}
public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
if (bluetoothGatt == null) {
Log.w(TAG, "BluetoothGatt not initialized");
return;
}
bluetoothGatt.readCharacteristic(characteristic);
}
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,boolean enabled) {
if (bluetoothGatt == null) {
Log.w(TAG, "BluetoothGatt not initialized");
return;
}
bluetoothGatt.setCharacteristicNotification(characteristic, enabled);
}
class LocalBinder extends Binder {
public BluetoothLeService getService() {
return BluetoothLeService.this;
}
}
}
private BluetoothLeService bluetoothLeService = new BluetoothLeService();
private final ServiceConnection serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
bluetoothLeService = ((BluetoothLeService.LocalBinder) service).getService();
if (bluetoothLeService != null) {
if (!bluetoothAdapter.isEnabled()) {
Log.e(TAG, "Unable to initialize Bluetooth");
}
else{
bluetoothLeService.connect();
Log.i(TAG, "Service connected");
}
}
}
#Override
public void onServiceDisconnected(ComponentName name) {
bluetoothLeService = null;
}
};
private final BroadcastReceiver gattUpdateReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
Log.d(TAG, "RECEIVED " + action);
if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
connected = true;
//Log.d(TAG, "CONNECTED");
} else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
connected = false;
//Log.d(TAG, "DISCONNECTED");
}
}
};
private final ActivityResultLauncher<Intent> someActivityResultLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
#Override
public void onActivityResult(ActivityResult result) {
Log.i("RESULT", result.getResultCode() + "");
setup();
}
});
private void setup() {
bluetoothLeService.ctx = this.getApplicationContext();
Log.d("APPLICATIONCONTEXT", bluetoothLeService.ctx + "");
mDiscoverButton.setOnClickListener(this);
mAdvertiseButton.setOnClickListener(this);
mSendButton.setOnClickListener(this);
mBluetoothLeScanner = BluetoothAdapter.getDefaultAdapter().getBluetoothLeScanner();
if (!BluetoothAdapter.getDefaultAdapter().isMultipleAdvertisementSupported()) {
Toast.makeText(this, "Multiple advertisement not supported", Toast.LENGTH_SHORT).show();
mAdvertiseButton.setEnabled(false);
mDiscoverButton.setEnabled(false);
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull #org.jetbrains.annotations.NotNull String[] permissions, #NonNull #org.jetbrains.annotations.NotNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 2) {
final LocationManager manager = (LocationManager) getSystemService( Context.LOCATION_SERVICE );
if(!manager.isProviderEnabled(LocationManager.GPS_PROVIDER)){
Intent enableLocationIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
someActivityResultLauncher.launch(enableLocationIntent);
}
else{
setup();
}
}
}
#RequiresApi(api = Build.VERSION_CODES.M)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mText = (TextView) findViewById( R.id.text );
mDiscoverButton = (Button) findViewById( R.id.discover_btn );
mAdvertiseButton = (Button) findViewById( R.id.advertise_btn );
mSendButton = (Button) findViewById( R.id.send_btn );
this.requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, 2);
}
#Override
protected void onResume() {
super.onResume();
MainActivity.this.getApplicationContext().registerReceiver(gattUpdateReceiver, makeGattUpdateIntentFilter());
}
#Override
protected void onPause() {
super.onPause();
MainActivity.this.getApplicationContext().unregisterReceiver(gattUpdateReceiver);
}
#Override
public void onClick(View v) {
Log.d(TAG, getString( R.string.ble_uuid ));
if( v.getId() == R.id.discover_btn ) {
discover();
} else if( v.getId() == R.id.advertise_btn ) {
advertise();
//MainActivity.this.getApplicationContext().registerReceiver(gattUpdateReceiver, makeGattUpdateIntentFilter());
} else if (v.getId() == R.id.send_btn){
//MainActivity.this.getApplicationContext().unregisterReceiver(gattUpdateReceiver);
send();
}
}
public void advertise(){
BluetoothLeAdvertiser advertiser = BluetoothAdapter.getDefaultAdapter().getBluetoothLeAdvertiser();
AdvertiseSettings settings = new AdvertiseSettings.Builder().setTimeout(0)
.setAdvertiseMode( AdvertiseSettings.ADVERTISE_MODE_BALANCED )
.setTxPowerLevel( AdvertiseSettings.ADVERTISE_TX_POWER_HIGH )
.setConnectable( true )
.build();
ParcelUuid pUuid = ParcelUuid.fromString( getString( R.string.ble_uuid ) ) ;
AdvertiseData data = new AdvertiseData.Builder()
.addServiceUuid( pUuid ).setIncludeDeviceName(false)
.build();
AdvertiseCallback advertisingCallback = new AdvertiseCallback() {
#Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
Log.d(TAG, "START ADVERTISING");
super.onStartSuccess(settingsInEffect);
}
#Override
public void onStartFailure(int errorCode) {
Log.e( TAG, "Advertising onStartFailure: " + errorCode );
super.onStartFailure(errorCode);
}
};
advertiser.startAdvertising( settings, data, advertisingCallback );
}
public void discover(){
ScanFilter filter = new ScanFilter.Builder()
.setServiceUuid( ParcelUuid.fromString( getString(R.string.ble_uuid ) ) )
.build();
List<ScanFilter> filters = new ArrayList<>();
filters.add( filter );
ScanSettings settings = new ScanSettings.Builder()
.setScanMode( ScanSettings.SCAN_MODE_BALANCED )
.build();
mBluetoothLeScanner.startScan(filters, settings, mScanCallback);
Log.d(TAG, "Discovery started");
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
Log.d(TAG, "Discovery stopped");
mBluetoothLeScanner.stopScan(mScanCallback);
}
}, 10000);
}
public void send(){
Log.d(TAG, "START CONNECTIONG GATT");
mBluetoothLeScanner.stopScan(mScanCallback);
//boundGatt();
connectGatt();
}
public void boundGatt(){
Intent gattServiceIntent = new Intent(MainActivity.this.getApplicationContext(), BluetoothLeService.class);
bindService(gattServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE);
}
public void connectGatt(){
bluetoothLeService.connect();
}
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);
return intentFilter;
}
}
Your Peripheral (phone A) does only advertisement, but BluetoothGattServer is not set up. That could be a reason of the described behavior - advertisement & scanning work, but connection doesn't.
BluetoothGatt + BluetoothGattCallback are for Central role (you call it phone B).
BluetoothGattServer + BluetoothGattServerCallback are for Peripheral (phone A).
Notes:
Connection from Central side (phone B) looks good, because when advertised device found, you get it using bluetoothAdapter.getRemoteDevice(address), then you call device.connectGatt
To transfer some data, you will need to add BluetoothGattService with BluetoothGattCharacteristic to your BluetoothGattServer - example of setup in Kotlin
Example project on github: BLEProof - it's in Kotlin, 2 apps communicate with each other: Central and Peripheral, all code in MainActivity.kt

There is an issue to connected Bluetooth in foregroud sevice android

in this code its works in the background but not working in the foreground I want to add a foreground service that implements Bluetooth connectivity and GATT connection as well. please help me in this code
in this code its works in the background but not working in the foreground I want to add a foreground service that implements Bluetooth connectivity and GATT connection as well. please help me in this code
in this code its works in the background but not working in the foreground I want to add a foreground service that implements Bluetooth connectivity and GATT connection as well. please help me in this code
public static BluetoothGattCharacteristic colorCharacteristic;
private HashMap<String, BluetoothGatt> gattHash = new HashMap<String, BluetoothGatt>();
private BluetoothManager bluetoothManager;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothGatt mGatt;
private boolean isConnected;
#Override
public IBinder onBind(Intent intent) {
initAdapter();
return kBinder;
}
#Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
private String address;
public void initBluetoothDevice(final String address, final Context context) {
this.address = address;
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
if(isConnected())return;
if(null!=mGatt){
refreshDeviceCache(mGatt);
mGatt=null;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mGatt = device.connectGatt(context, false, bleGattCallback,BluetoothDevice.TRANSPORT_LE);
} else {
mGatt = device.connectGatt(context, false, bleGattCallback);
}
if (mGatt == null) {
System.out.println(device.getAddress() + "gatt is null");
}
// this.mContext=context;
// reconnect(true);
}
public String getDeviceAddress() {
return this.address;
}
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, final int rssi,
final byte[] scanRecord) {
if (device.getAddress().equals(address) && Math.abs(rssi) < 90) {
});
}
}
};
private void initAdapter() {
if (bluetoothManager == null) {
bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
if (bluetoothManager == null) {
return;
}
}
mBluetoothAdapter = bluetoothManager.getAdapter();
}
public void disconnect() {
NeedReconnect = false;
if (mGatt == null)
return;
mGatt.disconnect();
}
public void disconnect(String address) {
ArrayList<BluetoothGatt> gatts = new ArrayList<BluetoothGatt>();
for (BluetoothGatt gatt : arrayGatts) {
if (gatt != null && gatt.getDevice().getAddress().equals(address)) {
gatts.add(gatt);
// gatt.disconnect();
gatt.close();
// gatt = null;
}
}
arrayGatts.removeAll(gatts);
}
public class LocalBinder extends Binder {
public BleService getService() {
return BleService.this;
}
}
private int discoverCount;
private Object ob = new Object();
private BluetoothGattCallback bleGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,
int newState) {
String action = null;
Log.i(TAG, "onConnectionStateChange: status"+ status+" newstate "+newState);
if (newState == BluetoothProfile.STATE_CONNECTED) {
if (status == 133) {
mGatt.close();
mGatt = null;
return;
}
action = ACTION_GATT_CONNECTED;
try {
gatt.discoverServices();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
isConnected=false;
Log.i(TAG, "onConnectionStateChange: "+ACTION_GATT_DISCONNECTED);
if (mGatt != null) {
mGatt.close();
mGatt = null;
}
queues.clear();
if(!NeedReconnect) {
action = ACTION_GATT_DISCONNECTED;
broadcastUpdate(action);
}
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
// if (mGatt == null)
// return;
if (status == BluetoothGatt.GATT_SUCCESS) {
String address = gatt.getDevice().getAddress();
String name = mBluetoothAdapter.getRemoteDevice(address)
.getName();
setCharacteristicNotification(true);
/* if (gatt != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
gatt.requestMtu(512);
}else{
setCharacteristicNotification(true);
}*/
discoverCount = 0;
} else {
// mGatt = null;
Log.w("servieDiscovered", "onServicesDiscovered received: "
+ status);
}
}
#Override
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
super.onMtuChanged(gatt, mtu, status);
if (BluetoothGatt.GATT_SUCCESS == status) {
setCharacteristicNotification(true);
}else {
gatt.requestMtu(153);
}
}
public void onCharacteristicRead(BluetoothGatt gatt,
android.bluetooth.BluetoothGattCharacteristic characteristic,
int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic, gatt
.getDevice().getAddress());
} else {
}
}
public void onDescriptorWrite(BluetoothGatt gatt,
BluetoothGattDescriptor descriptor, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
//MyLog.i("onDescriptorWrite");
// NeedReconnect = true;
isConnected=true;
// SharePreferenceUtils.setSpString(SharePreferenceUtils.KEY_ADDRESS,gatt.getDevice().getAddress());
broadcastUpdate(ACTION_GATT_onDescriptorWrite);
}else{
Log.i(TAG, "onDescriptorWrite: failed");
}
}
;
public void onCharacteristicChanged(BluetoothGatt gatt,
android.bluetooth.BluetoothGattCharacteristic characteristic) {
if (mGatt == null)
return;
Log.i(TAG, "onCharacteristicChanged: " + ResolveData.byte2Hex(characteristic.getValue()));
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic, gatt
.getDevice().getAddress());
// SendData.sendBus(ACTION_DATA_AVAILABLE, characteristic.getValue());
}
public void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
nextQueue();
} else {
// MyLog.i("status" + status);
}
}
;
};
public boolean refreshDeviceCache(BluetoothGatt gatt) {
try {
BluetoothGatt localBluetoothGatt = gatt;
Method localMethod = localBluetoothGatt.getClass().getMethod(
"refresh", new Class[0]);
if (localMethod != null) {
boolean bool = ((Boolean) localMethod.invoke(
localBluetoothGatt, new Object[0])).booleanValue();
return bool;
}
} catch (Exception localException) {
Log.e("s", "An exception occured while refreshing device");
}
return false;
}
private void broadcastUpdate(String action) {
BleData bleData = new BleData();
bleData.setAction(action);
RxBus.getInstance().post(bleData);
//Intent intent = new Intent(action);
//sendBroadcast(intent);
}
private void broadcast update(String action,
BluetoothGattCharacteristic characteristic, String mac) {
// Intent intent = new Intent(action);
byte[] data = characteristic.getValue();
BleData bleData = new BleData();
bleData.setAction(action);
bleData.setValue(data);
RxBus.getInstance().post(bleData);
}
public void readValue(BluetoothGattCharacteristic characteristic) {
if (mGatt == null) return;
mGatt.readCharacteristic(characteristic);
}
public void writeValue(byte[] value) {
if (mGatt == null||value==null) return;
BluetoothGattService service = mGatt.getService(SERVICE_DATA);
if (service == null) return;
BluetoothGattCharacteristic characteristic = service.getCharacteristic(DATA_Characteristic);
if (characteristic == null) return;
if (value[0] ==(byte) 0x47) {
NeedReconnect = false;
}
characteristic.setValue(value);
Log.i(TAG, "writeValue: "+ ResolveData.byte2Hex(value));
mGatt.writeCharacteristic(characteristic);
}
public void setCharacteristicNotification(boolean enable) {
// TODO Auto-generated method stub
if (mGatt == null) return;
BluetoothGattService service = mGatt.getService(SERVICE_DATA);
if (service == null) return;
BluetoothGattCharacteristic characteristic = service.getCharacteristic(NOTIY_Characteristic);
if (characteristic == null) return;
mGatt.setCharacteristicNotification(characteristic, enable);
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
BluetoothGattDescriptor descriptor = characteristic
.getDescriptor(NOTIY);
if (descriptor == null) {
//MyLog.e("setCharacteristicNotification descriptor=null,所以不能发送使能数据");
return;
}
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
if (mGatt == null)
return;
mGatt.writeDescriptor(descriptor);
}
#Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
//MyLog.i("servicer destory");
}
Queue<byte[]> queues=new LinkedList<>();
public void offerValue(byte[]value) {
queues.offer(value);
}
public void nextQueue(){
final Queue<byte[]> requests=queues;
byte[]data=requests!=null?requests.poll():null;
writeValue(data);
}
public boolean isConnected(){
return this.isConnected;
}
}

Communication between Android and bluetooth devices

I'd like to connect the Android application and sensors with bluetooth and display sensor data on the application, but I do not know where to change.
Is the data of the sensor stored in the character stick?
If so, how do you display the stored data?
The equipment you are using is the SONY MESH button.
Android ver 5.0.2
Android Studio ver 2.3.1
MainActivity.java
private final static int SDKVER_LOLLIPOP = 21;
private final static int MESSAGE_NEW_RECEIVEDNUM = 0;
private final static int MESSAGE_NEW_SENDNUM = 1;
private final static int REQUEST_ENABLE_BT = 123456;
private BluetoothManager mBleManager;
private BluetoothAdapter mBleAdapter;
private boolean mIsBluetoothEnable = false;
private BluetoothLeScanner mBleScanner;
private BluetoothGatt mBleGatt;
private BluetoothGattCharacteristic mBleCharacteristic;
private TextView mTxtReceivedNum;
private TextView mTxtSendNum;
private String meshdata = String.valueOf(0x00020103);
private String mStrReceivedNum = "";
private String mStrSendNum = "";
private static final String SERVICE_UUID = "72C90001-57A9-4D40-B746-534E22EC9F9E";
private static final String CHARACTERISTIC_UUID = "72C90003-57A9-4D40-B746-534E22EC9F9E";
private static final String CHARACTERISTIC_CONFIG_UUID = "00002902-0000-1000-8000-00805f9b34fb";
private Random mRandom = new Random();
private Timer mTimer;
private SendDataTimer mSendDataTimer;
private final LeScanCallback mScanCallback = new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
runOnUiThread(new Runnable() {
#Override
public void run() {
mBleGatt = device.connectGatt(getApplicationContext(), false, mGattCallback);
}
});
}
};
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState)
{
if (newState == BluetoothProfile.STATE_CONNECTED) {
gatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
if (mBleGatt != null)
{
mBleGatt.close();
mBleGatt = null;
}
mIsBluetoothEnable = false;
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status)
{
if (status == BluetoothGatt.GATT_SUCCESS) {
BluetoothGattService service = gatt.getService(UUID.fromString(SERVICE_UUID));
if (service != null)
{
mBleCharacteristic = service.getCharacteristic(UUID.fromString(CHARACTERISTIC_UUID));
if (mBleCharacteristic != null) {
mBleGatt = gatt;
boolean registered = mBleGatt.setCharacteristicNotification(mBleCharacteristic, true);
BluetoothGattDescriptor descriptor = mBleCharacteristic.getDescriptor(
UUID.fromString(CHARACTERISTIC_CONFIG_UUID));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBleGatt.writeDescriptor(descriptor);
mIsBluetoothEnable = true;
}
}
}
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic)
{
if (CHARACTERISTIC_UUID.equals(characteristic.getUuid().toString().toUpperCase()))
{
mStrReceivedNum = characteristic.getStringValue(0);
mBleHandler.sendEmptyMessage(MESSAGE_NEW_RECEIVEDNUM);
}
}
};
private Handler mBleHandler = new Handler()
{
public void handleMessage(Message msg)
{
switch (msg.what)
{
case MESSAGE_NEW_RECEIVEDNUM:
mTxtReceivedNum.setText(mStrReceivedNum);
break;
case MESSAGE_NEW_SENDNUM:
mTxtSendNum.setText(mStrSendNum);
break;
}
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mIsBluetoothEnable = false;
mTxtReceivedNum = (TextView) findViewById(R.id.received_num);
mTxtSendNum = (TextView) findViewById(R.id.send_num);
mBleManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBleAdapter = mBleManager.getAdapter();
mTimer = new Timer();
mSendDataTimer = new SendDataTimer();
mTimer.schedule(mSendDataTimer, 500, 1000);
if ((mBleAdapter == null)
|| (! mBleAdapter.isEnabled())) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
else
{
this.scanNewDevice();
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQUEST_ENABLE_BT:
if ((mBleAdapter != null)
|| (mBleAdapter.isEnabled())) {
this.scanNewDevice();
}
break;
}
}
private void scanNewDevice()
{
if (Build.VERSION.SDK_INT >= SDKVER_LOLLIPOP)
{
this.startScanByBleScanner();
}
else
{
mBleAdapter.startLeScan(mScanCallback);
}
}
#TargetApi(SDKVER_LOLLIPOP)
private void startScanByBleScanner()
{
mBleScanner = mBleAdapter.getBluetoothLeScanner();
mBleScanner.startScan(new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
result.getDevice().connectGatt(getApplicationContext(), false, mGattCallback);
}
#Override
public void onScanFailed(int intErrorCode)
{
super.onScanFailed(intErrorCode);
}
});
}
public class SendDataTimer extends TimerTask{
#Override
public void run() {
if(mIsBluetoothEnable)
{
mStrSendNum = String.valueOf(mRandom.nextInt(1000));
mBleHandler.sendEmptyMessage(MESSAGE_NEW_SENDNUM);
mBleCharacteristic.setValue(meshdata);
mBleGatt.writeCharacteristic(mBleCharacteristic);
}
}
}
#Override
protected void onDestroy()
{
mIsBluetoothEnable = false;
if(mBleGatt != null) {
mBleGatt.close();
mBleGatt = null;
}
super.onDestroy();
}
Related places
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic)
{
if (CHARACTERISTIC_UUID.equals(characteristic.getUuid().toString().toUpperCase()))
{
mStrReceivedNum = characteristic.getStringValue(0);
mBleHandler.sendEmptyMessage(MESSAGE_NEW_RECEIVEDNUM);
}
}
Related places
private Handler mBleHandler = new Handler()
{
public void handleMessage(Message msg)
{
switch (msg.what)
{
case MESSAGE_NEW_RECEIVEDNUM:
mTxtReceivedNum.setText(mStrReceivedNum);
break;
case MESSAGE_NEW_SENDNUM:
mTxtSendNum.setText(mStrSendNum);
break;
}
}
};
Related places
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mIsBluetoothEnable = false;
mTxtReceivedNum = (TextView) findViewById(R.id.received_num);
mTxtSendNum = (TextView) findViewById(R.id.send_num);
Related places
public class SendDataTimer extends TimerTask{
#Override
public void run() {
if(mIsBluetoothEnable)
{
mStrSendNum = String.valueOf(mRandom.nextInt(1000));
mBleHandler.sendEmptyMessage(MESSAGE_NEW_SENDNUM);
mBleCharacteristic.setValue(mStrSendNum);
mBleGatt.writeCharacteristic(mBleCharacteristic);
}
}
}
activity_main.xml
<TextView
android:id="#+id/received_num"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="40sp"
android:gravity="right"
/>
<TextView
android:id="#+id/send_num"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="40sp"
/>

Android BLE onCharacteristicChanged not triggering

I have an medical device(named as SnoreCoach), I have created an android app and did Bluetooth Low Energy connectivity with this device. Everything (connecting to device, writing data to characteristics etc.) is working fine except OnCharacteristicChanged is not getting triggered.
Following is my BluetoothGattCallback:
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
if (newState == BluetoothProfile.STATE_CONNECTED) {
broadcastUpdate(ACTION_GATT_CONNECTED);
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
broadcastUpdate(ACTION_GATT_DISCONNECTED);
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.d(TAG, "onServicesDiscovered - success");
broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
}
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.d(TAG, "onCharacteristicRead - success");
broadcastUpdate(ACTION_DATA_AVAILABLE);
}
}
#Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.d(TAG, "onCharacteristicWrite - success");
if (writeType.equals("unPair")) {
mBluetoothGatt.close();
mBluetoothGatt = null;
currentlyConnectedPeripheral = null;
}
}
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
Log.d(TAG, "onCharacteristicChanged");
broadcastUpdate(ACTION_DATA_AVAILABLE);
}
#Override
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
super.onReliableWriteCompleted(gatt, status);
Log.d(TAG, "onReliableWriteCompleted");
}
#Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
super.onReadRemoteRssi(gatt, rssi, status);
Log.d(TAG, "onReadRemoteRssi");
}
#Override
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
super.onMtuChanged(gatt, mtu, status);
Log.d(TAG, "onMtuChanged");
}
};
Following is the "broadcastUpdate" function called from above BluetoothGattCallback:
private void broadcastUpdate(final String action) {
final Intent intent = new Intent(action);
sendBroadcast(intent);
}
In my calling activity (SettingsActivity) I have following broadcast receiver:
private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
switch (action) {
case BLEService.ACTION_GATT_CONNECTED:
displayToast("Connected");
Log.d(TAG, "Connected");
mBleService.discoverServices();
break;
case BLEService.ACTION_GATT_DISCONNECTED:
displayToast("Disconnected");
break;
case BLEService.ACTION_GATT_SERVICES_DISCOVERED:
Log.d(TAG, "Services Discovered");
displayToast("Services Discovered");
mBleService.enableNotification();
break;
case BLEService.ACTION_DATA_AVAILABLE:
displayToast("Data Received");
break;
}
}
};
And below is my "enableNotification" functions:
public void enableNotification() {
BluetoothGattService snorecoachService = mBluetoothGatt.getService(SNORECOACH_SERVICE_UUID);
if (snorecoachService == null) {
Log.d(TAG, "snorecoach Service not found!");
return;
}
BluetoothGattCharacteristic bodyMovementChar = snorecoachService.getCharacteristic(BODY_MOVEMENT_UUID);
if (bodyMovementChar == null) {
Log.d(TAG, "charateristic not found!");
return;
}
mBluetoothGatt.setCharacteristicNotification(bodyMovementChar, true);
BluetoothGattDescriptor desc = bodyMovementChar.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_UUID);
desc.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
mBluetoothGatt.writeDescriptor(desc);
}
Following is the flow :
1) On connection, I send an broadcast as "connected" to mGattUpdateReceive, here I then start service discovery.
2) OnServiceDiscovery, I send an broadcast as "Services Discovered" to mGattUpdateReceive, here I call "enable notofication" function to set "setCharacteristicNotification", which writes the required descriptor too.
I have tried all possible options I found from other stackOverflow questions, But I don't know what I am doing wrong that the onCharacteristic event is not getting triggered.
I have spent more than 2 days for solving this but no luck, so any help would be greatly appreciated.
If it is notifications you want rather than indications, try to change
desc.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
to
desc.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);

Android BLE for Heart Rate receiving data from Product

I am developing a code for heart rate detection using BLE. I am able to connect to the gatt server, and even broadcast updates. But I can't seem to detect any heart rate. Am I missing something?
Here'y my codes.
public class BleActivity extends Activity {
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";
private BluetoothDevicesAdapter bluetoothDevicesAdapter;
private BluetoothAdapter mBluetoothAdapter;
private Handler mHandler = new Handler();
private static final long SCAN_PERIOD = 5000;
private ProgressDialog progressDialog;
private BluetoothGatt mGatt;
#Bind(R.id.bleDeviceListView)
ListView listView;
#TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ble);
ButterKnife.bind(this);
bluetoothDevicesAdapter = new BluetoothDevicesAdapter(getApplicationContext());
listView.setAdapter(bluetoothDevicesAdapter);
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
}
#OnClick(R.id.scanButton)
public void onScanClicked() {
scanLeDevice();
}
#OnItemClick(R.id.bleDeviceListView)
public void onItemClicked(int position) {
connectToDevice((BluetoothDevice) bluetoothDevicesAdapter.getItem(position));
}
#Override
protected void onPause() {
super.onPause();
unregisterReceiver(mGattUpdateReceiver);
}
#Override
protected void onResume() {
super.onResume();
registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());
//CONFIRMS IF BLUETOOTH IS ENABLED
if (!mBluetoothAdapter.isEnabled()) {
if (!mBluetoothAdapter.isEnabled()) {
GenericDialogBox.EnableBluetoothBox(BleActivity.this, "Notice", "This app requires bluetooth. Enable?", mBluetoothAdapter);
}
}
}
private void scanLeDevice() {
bluetoothDevicesAdapter.clear();
bluetoothDevicesAdapter.notifyDataSetChanged();
progressDialog = new ProgressDialog(BleActivity.this);
progressDialog.setCancelable(false);
progressDialog.setMessage("Scanning Devices");
progressDialog.show();
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
mBluetoothAdapter.stopLeScan(leScanCallback);
progressDialog.hide();
mBluetoothAdapter.stopLeScan(leScanCallback);
progressDialog.dismiss();
progressDialog = null;
}
}, SCAN_PERIOD);
mBluetoothAdapter.startLeScan(leScanCallback);
}
private BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
runOnUiThread(new Runnable() {
#Override
public void run() {
bluetoothDevicesAdapter.addDevice(device);
bluetoothDevicesAdapter.notifyDataSetChanged();
}
});
}
};
public void connectToDevice(BluetoothDevice device) {
if (mGatt == null) {
mGatt = device.connectGatt(this, false, btleGattCallback);
progressDialog = new ProgressDialog(BleActivity.this);
progressDialog.setMessage("Connecting to Gatt Server.");
progressDialog.setCancelable(false);
progressDialog.show();
}
}
private final BluetoothGattCallback btleGattCallback = new BluetoothGattCallback() {
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
#Override
public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) {
String intentAction;
Log.e("onConnectionStateChange", "Status: " + status);
switch (newState) {
case BluetoothProfile.STATE_CONNECTED:
intentAction = ACTION_GATT_CONNECTED;
Log.e("gattCallback", "STATE_CONNECTED");
progressDialog.dismiss();
progressDialog = null;
broadcastUpdate(intentAction);
mGatt.discoverServices();
break;
case BluetoothProfile.STATE_DISCONNECTED:
intentAction = ACTION_GATT_DISCONNECTED;
Log.e("gattCallback", "STATE_DISCONNECTED");
BluetoothDevice mDevice = gatt.getDevice();
mGatt = null;
broadcastUpdate(intentAction);
break;
default:
Log.e("gattCallback", "STATE_OTHER");
}
}
#Override
public void onServicesDiscovered(final BluetoothGatt gatt, final int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
List<BluetoothGattService> services = gatt.getServices();
BluetoothGattCharacteristic therm_char;
for (int i = 0; i < services.size(); i++) {
therm_char = services.get(i).getCharacteristics().get(0);
if(therm_char.getUuid().equals(SampleGattAttributes.HEART_RATE_MEASUREMENT)){
gatt.setCharacteristicNotification(therm_char, true);
BluetoothGattDescriptor descriptor = therm_char.getDescriptor(
UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mGatt.writeDescriptor(descriptor);
}
}
broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
Log.e("gattCallback", "Service discovered" + gatt.getServices() + " Status: " + status);
} else {
Log.e("BluetoothServices: ", "onServicesDiscovered received: " + status);
}
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
}
};
private void broadcastUpdate(final String action) {
final Intent intent = new Intent(action);
sendBroadcast(intent);
}
private void broadcastUpdate(final String action,
final BluetoothGattCharacteristic characteristic) {
final Intent intent = new Intent(action);
// This is special handling for the Heart Rate Measurement profile. Data
// parsing is carried out as per profile specifications.
if (UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT).equals(characteristic.getUuid())) {
int flag = characteristic.getProperties();
int format = -1;
if ((flag & 0x01) != 0) {
format = BluetoothGattCharacteristic.FORMAT_UINT16;
Log.d("Bluetooth Service: ", "Heart rate format UINT16.");
} else {
format = BluetoothGattCharacteristic.FORMAT_UINT8;
Log.d("Bluetooth Service: ", "Heart rate format UINT8.");
}
final int heartRate = characteristic.getIntValue(format, 1);
Log.d("Bluetooth Service: ", String.format("Received heart rate: %d", heartRate));
intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));
} else {
// For all other profiles, writes the data formatted in HEX.
final byte[] data = characteristic.getValue();
if (data != null && data.length > 0) {
final StringBuilder stringBuilder = new StringBuilder(data.length);
for(byte byteChar : data)
stringBuilder.append(String.format("%02X ", byteChar));
intent.putExtra(EXTRA_DATA, new String(data) + "\n" +
stringBuilder.toString());
}
}
sendBroadcast(intent);
}
private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (ACTION_GATT_CONNECTED.equals(action)) {
Toast.makeText(getApplicationContext(), "Connected to Service.", Toast.LENGTH_SHORT).show();
invalidateOptionsMenu();
} else if (ACTION_GATT_DISCONNECTED.equals(action)) {
Toast.makeText(getApplicationContext(), "Connected to Service.", Toast.LENGTH_SHORT).show();
} else if (ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
//Update Connection State
} else if (ACTION_DATA_AVAILABLE.equals(action)) {
Toast.makeText(getApplicationContext(), intent.getStringExtra(EXTRA_DATA), Toast.LENGTH_SHORT).show();
}
}
};
private static IntentFilter makeGattUpdateIntentFilter() {
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_GATT_CONNECTED);
intentFilter.addAction(ACTION_GATT_DISCONNECTED);
intentFilter.addAction(ACTION_GATT_SERVICES_DISCOVERED);
intentFilter.addAction(ACTION_DATA_AVAILABLE);
return intentFilter;
}
And this is the sampleAttributes Class.
public class SampleGattAttributes {
private static HashMap<String, String> attributes = new HashMap();
public static String HEART_RATE_MEASUREMENT = "00002a37-0000-1000-8000-00805f9b34fb";
public static String CLIENT_CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb";
static {
// Sample Services.
attributes.put("0000180d-0000-1000-8000-00805f9b34fb", "Heart Rate Service");
attributes.put("0000180a-0000-1000-8000-00805f9b34fb", "Device Information Service");
// Sample Characteristics.
attributes.put(HEART_RATE_MEASUREMENT, "Heart Rate Measurement");
attributes.put("00002a29-0000-1000-8000-00805f9b34fb", "Manufacturer Name String");
}
public static String lookup(String uuid, String defaultName) {
String name = attributes.get(uuid);
return name == null ? defaultName : name;
}
}
And also is there a way to log if the device is searching for any changes from time to time?
Thank you!

Categories