BLE Scan not working in Android - android

I am trying to scan other Bluetooth devices in Android using below code.
But nothing is getting triggered and no devices are detected.
import android.annotation.TargetApi;
import android.app.Activity;
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.BluetoothProfile;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
#TargetApi(21)
public class MainActivity extends ActionBarActivity {
private BluetoothAdapter mBluetoothAdapter;
private int REQUEST_ENABLE_BT = 1;
private Handler mHandler;
private static final long SCAN_PERIOD = 1000000;
private BluetoothLeScanner mLEScanner;
private ScanSettings settings;
private List<ScanFilter> filters;
private BluetoothGatt mGatt;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler = new Handler();
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, "BLE Not Supported",
Toast.LENGTH_SHORT).show();
finish();
}
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
}
#Override
protected void onResume() {
super.onResume();
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
} else {
if (Build.VERSION.SDK_INT >= 21) {
mLEScanner = mBluetoothAdapter.getBluetoothLeScanner();
settings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.build();
filters = new ArrayList<ScanFilter>();
}
scanLeDevice(true);
}
}
#Override
protected void onPause() {
super.onPause();
if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
scanLeDevice(false);
}
}
#Override
protected void onDestroy() {
if (mGatt == null) {
return;
}
mGatt.close();
mGatt = null;
super.onDestroy();
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_ENABLE_BT) {
if (resultCode == Activity.RESULT_CANCELED) {
//Bluetooth not enabled.
finish();
return;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
private void scanLeDevice(final boolean enable) {
if (enable) {
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
} else {
mLEScanner.stopScan(mScanCallback);
}
}
}, SCAN_PERIOD);
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
//mLEScanner.startScan(filters, settings, mScanCallback);
mLEScanner.startScan( mScanCallback);
}
} else {
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
} else {
mLEScanner.stopScan(mScanCallback);
}
}
}
private ScanCallback mScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
Log.i("callbackType", String.valueOf(callbackType));
Log.i("result", result.toString());
BluetoothDevice btDevice = result.getDevice();
connectToDevice(btDevice);
}
#Override
public void onBatchScanResults(List<ScanResult> results) {
for (ScanResult sr : results) {
Log.i("ScanResult - Results", sr.toString());
}
}
#Override
public void onScanFailed(int errorCode) {
Log.e("Scan Failed", "Error Code: " + errorCode);
}
};
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("onLeScan", device.toString());
connectToDevice(device);
}
});
}
};
public void connectToDevice(BluetoothDevice device) {
if (mGatt == null) {
mGatt = device.connectGatt(this, false, 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("onConnectionStateChange", "Status: " + status);
switch (newState) {
case BluetoothProfile.STATE_CONNECTED:
Log.i("gattCallback", "STATE_CONNECTED");
gatt.discoverServices();
break;
case BluetoothProfile.STATE_DISCONNECTED:
Log.e("gattCallback", "STATE_DISCONNECTED");
break;
default:
Log.e("gattCallback", "STATE_OTHER");
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
List<BluetoothGattService> services = gatt.getServices();
Log.i("onServicesDiscovered", services.toString());
gatt.readCharacteristic(services.get(1).getCharacteristics().get
(0));
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic
characteristic, int status) {
Log.i("onCharacteristicRead", characteristic.toString());
gatt.disconnect();
}
};
}
I have these in the Manifest.
Added the coarse location as for BLE accuracy it is needed.
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-feature android:name="android.hardware.bluetooth_le"
android:required="true" />

Few things you have to check.
Is your android device is supporting BLE?
for this use (getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE))
If android version is 6.0 then allow location Access form -Settings-> app
make sure your Location is enable in android device.

Related

Bluetooth BLE does not connect

I tried all ways but i can't connect to the other bluetooth device. I perform these steps:
scan devices
stop scan when I find the device with "A-"
connect to device
I can find all the devices but then I can't connect to the one I want. Unfortunately I can't find a complete example
This is the code I use:
import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class MainActivity extends AppCompatActivity {
BluetoothManager btManager;
BluetoothAdapter btAdapter;
BluetoothLeScanner btScanner;
Button startScanningButton;
Button stopScanningButton;
TextView peripheralTextView;
BluetoothGatt bluetoothGatt;
BluetoothDevice device;
private final static int REQUEST_ENABLE_BT = 1;
private static final int PERMISSION_REQUEST_COARSE_LOCATION = 2;
private boolean mEchoInitialized;
private static final String TAG = "ClientActivity";
//
// private ActivityClientBinding mBinding;
private boolean mScanning;
private Handler mHandler;
private Handler mLogHandler;
private Map<String, BluetoothDevice> mScanResults;
private boolean mConnected;
private ScanCallback mScanCallback;
public static String SERVICE_STRING = "7D2EA28A-F7BD-485A-BD9D-92AD6ECFE93E";
public static UUID SERVICE_UUID = UUID.fromString(SERVICE_STRING);
public static String CHARACTERISTIC_ECHO_STRING = "7D2EBAAD-F7BD-485A-BD9D-92AD6ECFE93E";
public static UUID CHARACTERISTIC_ECHO_UUID = UUID.fromString(CHARACTERISTIC_ECHO_STRING);
public static final long SCAN_PERIOD = 5000;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
peripheralTextView = (TextView) findViewById(R.id.PeripheralTextView);
peripheralTextView.setMovementMethod(new ScrollingMovementMethod());
startScanningButton = (Button) findViewById(R.id.StartScanButton);
startScanningButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
startScanning();
}
});
stopScanningButton = (Button) findViewById(R.id.StopScanButton);
stopScanningButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
stopScanning();
}
});
stopScanningButton.setVisibility(View.INVISIBLE);
btManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
btAdapter = btManager.getAdapter();
btScanner = btAdapter.getBluetoothLeScanner();
if (btAdapter != null && !btAdapter.isEnabled()) {
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
}
// Make sure we have access coarse location enabled, if not, prompt the user to enable it
if (this.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("This app needs location access");
builder.setMessage("Please grant location access so this app can detect peripherals.");
builder.setPositiveButton(android.R.string.ok, null);
builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
#Override
public void onDismiss(DialogInterface dialog) {
requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSION_REQUEST_COARSE_LOCATION);
}
});
builder.show();
}
}
#Override
protected void onResume() {
super.onResume();
// Check low energy support
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
// Get a newer device
// logError("No LE Support.");
finish();
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
private ScanCallback leScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
// Log.d("ci sono",result.getDevice().getName());
if (result.getDevice().getName() != null && result.getDevice().getName().contains("A-"))
stopScanning();
// peripheralTextView.append("Device Name: " + result.getDevice().getName() + " rssi: " + result.getRssi() + "\n");
// auto scroll for text view
// final int scrollAmount = peripheralTextView.getLayout().getLineTop(peripheralTextView.getLineCount()) - peripheralTextView.getHeight();
// if there is no need to scroll, scrollAmount will be <=0
// if (scrollAmount > 0)
// peripheralTextView.scrollTo(0, scrollAmount);
connectDevice(result.getDevice());
// bluetoothGatt = result.getDevice().connectGatt(MainActivity.this, false, gattClientCallback);
}
};
#Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case PERMISSION_REQUEST_COARSE_LOCATION: {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
System.out.println("coarse location permission granted");
} else {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Functionality limited");
builder.setMessage("Since location access has not been granted, this app will not be able to discover beacons when in the background.");
builder.setPositiveButton(android.R.string.ok, null);
builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
#Override
public void onDismiss(DialogInterface dialog) {
}
});
builder.show();
}
return;
}
}
}
public void startScanning() {
System.out.println("start scanning");
peripheralTextView.setText("");
startScanningButton.setVisibility(View.INVISIBLE);
stopScanningButton.setVisibility(View.VISIBLE);
AsyncTask.execute(new Runnable() {
#Override
public void run() {
btScanner.startScan(leScanCallback);
}
});
}
public void stopScanning() {
System.out.println("stopping scanning");
peripheralTextView.append("Stopped Scanning");
startScanningButton.setVisibility(View.VISIBLE);
stopScanningButton.setVisibility(View.INVISIBLE);
AsyncTask.execute(new Runnable() {
#Override
public void run() {
btScanner.stopScan(leScanCallback);
}
});
}
private void connectDevice(BluetoothDevice device) {
// log("Connecting to " + device.getAddress());
GattClientCallback gattClientCallback = new GattClientCallback();
bluetoothGatt = device.connectGatt(this, false, gattClientCallback);
}
protected void onStop() {
// call the superclass method first
super.onStop();
Log.d("spegni", "bluetooth");
btScanner.stopScan(leScanCallback);
}
private class GattClientCallback extends BluetoothGattCallback {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
// log("onConnectionStateChange newState: " + newState);
if (status == BluetoothGatt.GATT_FAILURE) {
// logError("Connection Gatt failure status " + status);
disconnectGattServer();
return;
} else if (status != BluetoothGatt.GATT_SUCCESS) {
// handle anything not SUCCESS as failure
// logError("Connection not GATT sucess status " + status);
disconnectGattServer();
return;
}
if (newState == BluetoothProfile.STATE_CONNECTED) {
// log("Connected to device " + gatt.getDevice().getAddress());
setConnected(true);
gatt.discoverServices();
Log.d("discoverRiuscito", "onServicesDiscovered " + status + " " + gatt.discoverServices()
+ " " + gatt.getServices());
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
// log("Disconnected from device");
disconnectGattServer();
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
if (status != BluetoothGatt.GATT_SUCCESS) {
// log("Device service discovery unsuccessful, status " + status);
return;
}
List<BluetoothGattCharacteristic> matchingCharacteristics = BluetoothUtils.findCharacteristics(gatt);
if (matchingCharacteristics.isEmpty()) {
// logError("Unable to find characteristics.");
return;
}
// log("Initializing: setting write type and enabling notification");
for (BluetoothGattCharacteristic characteristic : matchingCharacteristics) {
characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
enableCharacteristicNotification(gatt, characteristic);
}
}
#Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
// log("Characteristic written successfully");
} else {
// logError("Characteristic write unsuccessful, status: " + status);
disconnectGattServer();
}
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
// log("Characteristic read successfully");
readCharacteristic(characteristic);
} else {
// logError("Characteristic read unsuccessful, status: " + status);
// Trying to read from the Time Characteristic? It doesnt have the property or permissions
// set to allow this. Normally this would be an error and you would want to:
// disconnectGattServer();
}
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
// log("Characteristic changed, " + characteristic.getUuid().toString());
readCharacteristic(characteristic);
}
private void enableCharacteristicNotification(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
boolean characteristicWriteSuccess = gatt.setCharacteristicNotification(characteristic, true);
if (characteristicWriteSuccess) {
// log("Characteristic notification set successfully for " + characteristic.getUuid().toString());
if (BluetoothUtils.isEchoCharacteristic(characteristic)) {
initializeEcho();
}
} else {
// logError("Characteristic notification set failure for " + characteristic.getUuid().toString());
}
}
private void readCharacteristic(BluetoothGattCharacteristic characteristic) {
byte[] messageBytes = characteristic.getValue();
// log("Read: " + StringUtils.byteArrayInHexFormat(messageBytes));
String message = StringUtils.stringFromBytes(messageBytes);
if (message == null) {
// logError("Unable to convert bytes to string");
return;
}
// log("Received message: " + message);
}
}
public void setConnected(boolean connected) {
mConnected = connected;
}
private void sendMessage() {
BluetoothGattCharacteristic characteristic = BluetoothUtils.findEchoCharacteristic(bluetoothGatt);
if (characteristic == null) {
return;
}
String message = "2";
byte[] messageBytes = StringUtils.bytesFromString(message);
if (messageBytes.length == 0) {
return;
}
characteristic.setValue(messageBytes);
boolean success = bluetoothGatt.writeCharacteristic(characteristic);
if (success) {
// log("Wrote: " + StringUtils.byteArrayInHexFormat(messageBytes));
} else {
// logError("Failed to write data");
}
}
public void initializeEcho() {
mEchoInitialized = true;
}
private class BtleScanCallback extends ScanCallback {
private Map<String, BluetoothDevice> mScanResults;
BtleScanCallback(Map<String, BluetoothDevice> scanResults) {
mScanResults = scanResults;
}
#Override
public void onScanResult(int callbackType, ScanResult result) {
addScanResult(result);
}
#Override
public void onBatchScanResults(List<ScanResult> results) {
for (ScanResult result : results) {
addScanResult(result);
}
}
#Override
public void onScanFailed(int errorCode) {
}
private void addScanResult(ScanResult result) {
BluetoothDevice device = result.getDevice();
String deviceAddress = device.getAddress();
mScanResults.put(deviceAddress, device);
}
}
public void disconnectGattServer() {
mConnected = false;
mEchoInitialized = false;
if (bluetoothGatt != null) {
bluetoothGatt.disconnect();
bluetoothGatt.close();
}
}
}```

Android BLE Gatt Connection Issue (Connection Timeout Status:8)

I'm developing an Android app that discovers and connects to a GATT service that is being advertised by my rPi 3B+. The iOS app that I've finished developing works without any issue. However, almost every time (95%) my Android app connects to the GATT server and tries to discover the services, the GATT connection times out with the status response code: 8.
Here's my android code:
package com.example.myapplication;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.ParcelUuid;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class MainActivity extends AppCompatActivity implements AdapterView.OnItemClickListener {
BluetoothAdapter bluetoothAdapter;
BluetoothLeScanner bluetoothLeScanner;
BluetoothManager bluetoothManager;
BluetoothScanCallback bluetoothScanCallback;
BluetoothGatt gattClient;
BluetoothGattCharacteristic characteristicID;
TextView scanningText;
final UUID SERVICE_UUID = UUID.fromString("6E400001-B5A3-F393-E0A9-E50E24DCCA9E");
final UUID CHARACTERISTIC_UUID_ID = UUID.fromString("6E400002-B5A3-F393-E0A9-E50E24DCCA9E");
final UUID DESCRIPTOR_UUID_ID = UUID.fromString("00002902-0000-1000-8000-00805F9B34FB");
ArrayList<String> wifiSSIDs = new ArrayList<>() ;
ListView listViewSSID;
ArrayAdapter<String> SSIDadapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
getSupportActionBar().setDefaultDisplayHomeAsUpEnabled(true);
listViewSSID = findViewById(R.id.lv_SSID);
scanningText = findViewById(R.id.scanningText);
SSIDadapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, wifiSSIDs);
listViewSSID.setAdapter(SSIDadapter);
listViewSSID.setOnItemClickListener(this);
bluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
bluetoothAdapter = bluetoothManager.getAdapter();
startScan();
}
#Override
protected void onDestroy() {
super.onDestroy();
Log.i("App","App closing...");
gattClient.disconnect();
gattClient.close();
}
private void startScan(){
ScanFilter scanFilter = new ScanFilter.Builder().setServiceUuid(new ParcelUuid(SERVICE_UUID)).build();
ArrayList<ScanFilter> filters = new ArrayList<>();
filters.add(scanFilter);
ScanSettings scanSettings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
Log.i("Bluetooth Scan","startScan()");
bluetoothScanCallback = new BluetoothScanCallback();
bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
bluetoothLeScanner.startScan(filters, scanSettings, bluetoothScanCallback);
}
private void connectDevice(BluetoothDevice device) {
if (device == null) Log.i("Bluetooth Connection","Device is null");
GattClientCallback gattClientCallback = new GattClientCallback();
gattClient = device.connectGatt(this,false, gattClientCallback, BluetoothDevice.TRANSPORT_LE);
}
private class BluetoothScanCallback extends ScanCallback {
#Override
public void onScanResult(int callbackType,final ScanResult result) {
Log.i("Bluetooth Scan Result", "onScanResult");
bluetoothLeScanner.stopScan(bluetoothScanCallback); // stop scan
connectDevice(result.getDevice());
}
#Override
public void onBatchScanResults(List<ScanResult> results) {
Log.i("Bluetooth Scan Result", "onBathScanResults");
}
#Override
public void onScanFailed(int errorCode) {
Log.i("Bluetooth Scan Result", "ErrorCode: " + errorCode);
}
}
private class GattClientCallback extends BluetoothGattCallback {
#Override
public void onConnectionStateChange(final BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
Log.i("Bluetooth Connection","onConnectionStateChange");
if (status == BluetoothGatt.GATT_FAILURE) {
Log.i("Bluetooth Connection", "onConnectionStateChange GATT FAILURE");
return;
} else if (status != BluetoothGatt.GATT_SUCCESS) {
Log.i("Bluetooth Connection", "onConnectionStateChange != GATT_SUCCESS");
Log.i("Bluetooth Connection", String.valueOf(status));
startScan();
return;
}
else {
final BluetoothDevice device = gatt.getDevice();
if (newState == BluetoothProfile.STATE_CONNECTED) {
Log.i("Bluetooth Connection", "onConnectionStateChange CONNECTED");
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
#Override
public void run() {
gatt.discoverServices(); }
}, 500);
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.i("Bluetooth Connection", "onConnectionStateChange DISCONNECTED");
}
}
}
#Override
public void onServicesDiscovered(final BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
Log.i("Bluetooth Services","onServicesDiscovered");
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
#Override
public void run() {
characteristicID = gatt.getService(SERVICE_UUID).getCharacteristic(CHARACTERISTIC_UUID_ID);
gatt.setCharacteristicNotification(characteristicID,true); }
}, 500);
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
#Override
public void run() {
BluetoothGattDescriptor descriptor = characteristicID.getDescriptor(DESCRIPTOR_UUID_ID);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor); }
}, 500);
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
Log.i("BT Characteristics","onCharacteristicRead");
}
#Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
Log.i("BT Characteristics","onCharacteristicWrite");
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
Log.i("BT Characteristics","onCharacteristicChanged");
Log.i("Characteristic Change",new String(characteristic.getValue()));
final String characteristicValue = new String(characteristic.getValue());
runOnUiThread(new Runnable() {
#Override
public void run() {
wifiSSIDs.add(characteristicValue);
SSIDadapter.notifyDataSetChanged();
listViewSSID.setVisibility(View.VISIBLE);
scanningText.setVisibility(View.INVISIBLE);
}
});
}
#Override
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorRead(gatt, descriptor, status);
Log.i("BT Characteristics","onDescriptorRead");
}
#Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorWrite(gatt, descriptor, status);
Log.i("BT Characteristics","onDescriptorWrite");
characteristicID.setValue("RW");
gatt.writeCharacteristic(characteristicID);
}
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
String ssid = ((TextView) view).getText().toString();
Log.i("Item Click","SSID: " + ssid);
characteristicID.setValue("SSID: " + ssid);
gattClient.writeCharacteristic(characteristicID);
Intent intent = new Intent(this, PasswordActivity.class);
startActivity(intent);
}
}
I can provide the python script code that's running on rPi but I believe that the problem stems from the android code. The log returns:
2020-10-05 14:05:53.710 19952-19952/com.example.myapplication I/Bluetooth Scan: startScan()
2020-10-05 14:05:53.711 19952-19952/com.example.myapplication I/chatty: uid=10299(com.example.myapplication) identical 1 line
2020-10-05 14:05:53.852 19952-19952/com.example.myapplication I/InputMethodManager: startInputInner - mService.startInputOrWindowGainedFocus
2020-10-05 14:05:54.937 19952-19952/com.example.myapplication I/Bluetooth Scan Result: onScanResult
2020-10-05 14:05:56.688 19952-19966/com.example.myapplication I/Bluetooth Connection: onConnectionStateChange
2020-10-05 14:05:56.688 19952-19966/com.example.myapplication I/Bluetooth Connection: onConnectionStateChange CONNECTED
2020-10-05 14:06:02.208 19952-19966/com.example.myapplication I/Bluetooth Connection: onConnectionStateChange
2020-10-05 14:06:02.208 19952-19966/com.example.myapplication I/Bluetooth Connection: onConnectionStateChange != GATT_SUCCESS
2020-10-05 14:06:02.208 19952-19966/com.example.myapplication I/Bluetooth Connection: 8

Not getting all the services from BLE device

The only services i got are
00001800-0000-1000-8000-00805f9b34fb -- Generic Access
00001801-0000-1000-8000-00805f9b34fb -generic attribute
8e400001-f315-4f60-9fb8-838830daea50 -- Buttonless DFU service
0000fee7-0000-1000-8000-00805f9b34fb --
i dont know how to find heart service, calories etc.
the band i am using is Rc-wh10 device name - M2S
MainActivity.java
package iband.bleexample;
import android.annotation.TargetApi;
import android.app.Activity;
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.BluetoothProfile;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
#TargetApi(21)
public class MainActivity extends AppCompatActivity {
private BluetoothAdapter mBluetoothAdapter;
private int REQUEST_ENABLE_BT = 1;
private Handler mHandler;
private static final long SCAN_PERIOD = 10000;
private BluetoothLeScanner mLEScanner;
private ScanSettings settings;
private List<ScanFilter> filters;
private BluetoothGatt mGatt;
TextView tw;
RecyclerView recyclerView;
ArrayList<BluetoothProvider> list;
iband.bleexample.BluetoothAdapter adapter;
TextView datatodisplay;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//datatodisplay = (TextView) findViewById(R.id.textView);
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
list = new ArrayList<>();
adapter = new iband.bleexample.BluetoothAdapter(this, list);
recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayout.VERTICAL, false));
recyclerView.setAdapter(adapter);
recyclerView.addOnItemTouchListener(
new RecyclerItemClickListener(MainActivity.this, new RecyclerItemClickListener.OnItemClickListener() {
#Override
public void onItemClick(View view, int position) {
// TODO Handle item click
//Toast.makeText(MainActivity.this, "Clicked", Toast.LENGTH_SHORT).show();
BluetoothDevice bd = new iband.bleexample.BluetoothAdapter(MainActivity.this, list).getDeviceAddress(position);
connectToDevice(bd);
}
})
);
mHandler = new Handler();
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, "BLE Not Supported",
Toast.LENGTH_SHORT).show();
finish();
}
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
tw = (TextView) findViewById(R.id.textView);
}
#Override
protected void onResume() {
super.onResume();
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
} else {
if (Build.VERSION.SDK_INT >= 21) {
mLEScanner = mBluetoothAdapter.getBluetoothLeScanner();
settings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.build();
filters = new ArrayList<ScanFilter>();
}
Toast.makeText(this, "Scanning...", Toast.LENGTH_SHORT).show();
scanLeDevice(true);
}
}
#Override
protected void onPause() {
super.onPause();
if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
scanLeDevice(false);
}
}
#Override
protected void onDestroy() {
if (mGatt == null) {
return;
}
mGatt.close();
mGatt = null;
super.onDestroy();
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_ENABLE_BT) {
if (resultCode == Activity.RESULT_CANCELED) {
//Bluetooth not enabled.
Toast.makeText(MainActivity.this, "Please enable Bluetooth", Toast.LENGTH_SHORT).show();
finish();
return;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
private void scanLeDevice(final boolean enable) {
if (enable) {
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
} else {
mLEScanner.stopScan(mScanCallback);
}
}
}, SCAN_PERIOD);
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mLEScanner.startScan(filters, settings, mScanCallback);
}
} else {
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
} else {
mLEScanner.stopScan(mScanCallback);
}
}
}
private ScanCallback mScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
Log.i("callbackType", String.valueOf(callbackType));
Log.i("result", result.toString());
tw.setText(result.toString());
// Log.i("result", result.isConnectable()+"");
//Log.i("result", result.getDevice().getName().toString()+"");
// Toast.makeText(MainActivity.this, result.toString()+"", Toast.LENGTH_SHORT).show();
BluetoothDevice btDevice = result.getDevice();
list.add(new BluetoothProvider(btDevice.getName(), btDevice.getAddress(), btDevice));
adapter.notifyDataSetChanged();
//connectToDevice(btDevice);
}
#Override
public void onBatchScanResults(List<ScanResult> results) {
for (ScanResult sr : results) {
Log.i("ScanResult - Results", sr.toString());
tw.setText("ScanResult - Results" + sr.toString());
}
}
#Override
public void onScanFailed(int errorCode) {
Log.e("Scan Failed", "Error Code: " + errorCode);
tw.setText("Scan Failed");
}
};
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("onLeScan", device.toString());
tw.setText(device.getName());
connectToDevice(device);
}
});
}
};
public void connectToDevice(BluetoothDevice device) {
if (mGatt == null) {
mGatt = device.connectGatt(this, false, gattCallback);
Log.i("ScanResult - Results", mGatt.toString());
//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("onConnectionStateChange", "Status: " + status);
switch (newState) {
case BluetoothProfile.STATE_CONNECTED:
Log.i("gattCallback", "STATE_CONNECTED");
gatt.discoverServices();
break;
case BluetoothProfile.STATE_DISCONNECTED:
Log.e("gattCallback", "STATE_DISCONNECTED");
break;
default:
Log.e("gattCallback", "STATE_OTHER");
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
List<BluetoothGattService> services = gatt.getServices();
Log.i("onServicesDiscovered", services.size() + services.toString());
for (int i = 0; i < services.size(); i++) {
Log.i("Services UUID", services.get(i).getUuid() +
"");
for (int j = 0; j < services.get(i).getCharacteristics().size(); j++) {
//gatt.readCharacteristic(services.get(i).getCharacteristics().get(j));
Log.i("Characterstics UUId", services.get(i).getCharacteristics().get(j).getUuid() + "");
}
}
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic
characteristic, int status) {
Log.i("onCharacteristicRead", characteristic.toString());
Toast.makeText(MainActivity.this, characteristic.toString() + "", Toast.LENGTH_SHORT).show();
//gatt.disconnect();
}
};
}

How to read data from a BLE device continuously to android app?

I am developing an android app that is going to communicate with a BLE device(RN4020) connected with a target board(micro controller). I developed the app that can able to send data to the target board through RN4020 and it received successfully through UART.But I am unable to receive data to the app from target device. I am sending data for every one second from microcontroller. But with the app MLDP perminal downloaded from play store can able to send and receive data simultaneously.
While debugging it doesn't reach onCharacteristicRead.
How to receive data from the device to app?
package com.example.designemb5.tempworking;
import android.Manifest;
import android.app.AlertDialog;
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.BluetoothProfile;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.ParcelUuid;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
public class MainActivity extends AppCompatActivity {
BluetoothManager btManager;
BluetoothAdapter btAdapter;
BluetoothLeScanner btScanner;
BluetoothDevice mBluetoothDevice;
public BluetoothAdapter mBluetoothAdapter;
public BluetoothGatt mBluetoothGatt;
public BluetoothGattService mBluetoothGattService;
private int mConnectionState = STATE_DISCONNECTED;
private BluetoothGattCharacteristic mWriteCharacteristic;
private static final int STATE_DISCONNECTED = 0;
private static final int STATE_CONNECTING = 1;
private static final int STATE_CONNECTED = 2;
Button startScanningButton;
Button stopScanningButton;
Button connectButton;
Button disconnectButton;
public boolean mConnected = false;
public boolean mCharacteristics = true;
private static final String TAG = "BLUETOOTH_LE";
public static List<ParcelUuid> MY_UUID;
public final static String ACTION_GATT_CONNECTED = "com.example.designemb5.tempworking.ACTION_GATT_CONNECTED";
public final static String ACTION_GATT_DISCONNECTED = "com.example.designemb5.tempworking.ACTION_GATT_DISCONNECTED";
public final static String ACTION_GATT_SERVICES_DISCOVERED = "com.example.designemb5.tempworking.ACTION_GATT_SERVICES_DISCOVERED";
public final static String ACTION_DATA_AVAILABLE = "com.example.designemb5.tempworking.ACTION_DATA_AVAILABLE";
public final static String EXTRA_DATA = "com.example.designemb5.tempworking.EXTRA_DATA";
public final static UUID MY_UUID_RN4020_SERVICE = UUID.fromString("00035b03-58e6-07dd-021a-08123a000300");
public final static UUID MY_UUID_RN4020_CHARACTERISTIC_WRITE = UUID.fromString("00035b03-58e6-07dd-021a-08123a000301");
public final static UUID MY_UUID_RN4020_CHARACTERISTIC_READ = UUID.fromString("00035b03-58e6-07dd-021a-08123a0003ff");
TextView peripheralTextView;
private final static int REQUEST_ENABLE_BT = 1;
private static final int PERMISSION_REQUEST_COARSE_LOCATION = 1;
public String mDeviceAddress;
public int mTestVal = 1;
public static Map<ParcelUuid, byte[]> mDeviceData;
#RequiresApi(api = Build.VERSION_CODES.M)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
connectButton = (Button) findViewById(R.id.ConnectButton);
disconnectButton = (Button) findViewById(R.id.disonnectButton);
peripheralTextView = (TextView) findViewById(R.id.PeripheralTextView);
peripheralTextView.setMovementMethod(new ScrollingMovementMethod());
startScanningButton = (Button) findViewById(R.id.StartScanButton);
startScanningButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
startScanning();
}
});
stopScanningButton = (Button) findViewById(R.id.StopScanButton);
stopScanningButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
stopScanning();
}
});
stopScanningButton.setVisibility(View.INVISIBLE);
btManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
btAdapter = btManager.getAdapter();
btScanner = btAdapter.getBluetoothLeScanner();
if (btAdapter != null && !btAdapter.isEnabled()) {
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableIntent,REQUEST_ENABLE_BT);
}
// Make sure we have access coarse location enabled, if not, prompt the user to enable it
if (this.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("This app needs location access");
builder.setMessage("Please grant location access so this app can detect peripherals.");
builder.setPositiveButton(android.R.string.ok, null);
builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
#Override
public void onDismiss(DialogInterface dialog) {
requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSION_REQUEST_COARSE_LOCATION);
}
});
builder.show();
}
}
public void startScanning() {
System.out.println("start scanning");
peripheralTextView.setText("");
startScanningButton.setVisibility(View.INVISIBLE);
stopScanningButton.setVisibility(View.VISIBLE);
AsyncTask.execute(new Runnable() {
#Override
public void run() {
btScanner.startScan(leScanCallback);
}
});
}
public void stopScanning() {
System.out.println("stopping scanning");
peripheralTextView.append("Stopped Scanning");
startScanningButton.setVisibility(View.VISIBLE);
stopScanningButton.setVisibility(View.INVISIBLE);
AsyncTask.execute(new Runnable() {
#Override
public void run() {
btScanner.stopScan(leScanCallback);
}
});
}
public void connect(View view) {
connectButton.setVisibility(View.INVISIBLE);
disconnectButton.setVisibility(View.VISIBLE);
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
final BluetoothDevice device = btAdapter
.getRemoteDevice(mDeviceAddress);
mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
}
public void disconnect(View view) {
connectButton.setVisibility(View.VISIBLE);
disconnectButton.setVisibility(View.INVISIBLE);
mBluetoothGatt.disconnect();
}
public void senddata(View view) {
{
int value = 0x01;
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
/*check if the service is available on the device*/
BluetoothGattService mCustomService = mBluetoothGatt.getService(MY_UUID_RN4020_SERVICE);
if (mCustomService == null) {
Log.w(TAG, "Custom BLE Service not found");
return;
}
/*get the read characteristic from the service*/
BluetoothGattCharacteristic mWriteCharacteristic = mCustomService.getCharacteristic(MY_UUID_RN4020_CHARACTERISTIC_WRITE);
mWriteCharacteristic.setValue(value, BluetoothGattCharacteristic.FORMAT_UINT8, 0);
if (mBluetoothGatt.writeCharacteristic(mWriteCharacteristic) == false) {
Log.w(TAG, "Failed to write characteristic");
}
}
}
public void receivedata(View view) {
{
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
/*check if the service is available on the device*/
BluetoothGattService mCustomService = mBluetoothGatt.getService(MY_UUID_RN4020_SERVICE);
if (mCustomService == null) {
Log.w(TAG, "Custom BLE Service not found");
return;
}
BluetoothGattCharacteristic mReadCharacteristic = mCustomService.getCharacteristic(MY_UUID_RN4020_CHARACTERISTIC_READ);
if (mBluetoothGatt.readCharacteristic(mReadCharacteristic) == false) {
Log.w(TAG, "Failed to read characteristic");
}
mBluetoothGatt.readCharacteristic(mReadCharacteristic);
}
}
// Device scan callback.
private ScanCallback leScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
processResult(result);
}
private void processResult(ScanResult result){
mBluetoothDevice = result.getDevice();
mDeviceAddress = result.getDevice().getAddress();
mDeviceData = result.getScanRecord().getServiceData();
MY_UUID = result.getScanRecord().getServiceUuids();
peripheralTextView.append("Device Name: " + mDeviceAddress + "\n");
stopScanning();
}
};
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
#Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
// updateStatus(characteristic);
Log.e("gatt", "writeChar");
}
#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);
Log.i(TAG, "Connected to GATT server.");
// Attempts to discover services after successful connection.
Log.i(TAG, "Attempting to start service discovery:"
+ mBluetoothGatt.discoverServices());
TimerTask task = new TimerTask() {
#Override
public void run() {
if (mBluetoothGatt != null)
mBluetoothGatt.readRemoteRssi();
}
};
Timer mRssiTimer = new Timer();
mRssiTimer.schedule(task, 1000, 1000);
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
intentAction = ACTION_GATT_DISCONNECTED;
mConnectionState = STATE_DISCONNECTED;
Log.i(TAG, "Disconnected from GATT server.");
broadcastUpdate(intentAction);
}
}
#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);
}
BluetoothGattService mCustomService = mBluetoothGatt.getService(MY_UUID_RN4020_SERVICE);
BluetoothGattCharacteristic mReadCharacteristic = mCustomService.getCharacteristic(MY_UUID_RN4020_CHARACTERISTIC_READ);
gatt.readCharacteristic(mReadCharacteristic);
gatt.setCharacteristicNotification(mReadCharacteristic, true);
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
gatt.readCharacteristic(characteristic);
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
};
#Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case PERMISSION_REQUEST_COARSE_LOCATION: {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
System.out.println("coarse location permission granted");
} else {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Functionality limited");
builder.setMessage("Since location access has not been granted, this app will not be able to discover beacons when in the background.");
builder.setPositiveButton(android.R.string.ok, null);
builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
#Override
public void onDismiss(DialogInterface dialog) {
}
});
builder.show();
}
return;
}
}
}
public void writeCharacteristic(BluetoothGattCharacteristic characteristic) {
mBluetoothGatt.writeCharacteristic(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:
// http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml
/* if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
int flag = characteristic.getProperties();
int format = -1;
if ((flag & 0x01) != 0) {
format = BluetoothGattCharacteristic.FORMAT_UINT16;
Log.d(TAG, "Heart rate format UINT16.");
} else {
format = BluetoothGattCharacteristic.FORMAT_UINT8;
Log.d(TAG, "Heart rate format UINT8.");
}
final int heartRate = characteristic.getIntValue(format, 1);
Log.d(TAG, 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());
}
}*/
final byte[] data = characteristic.getValue();
Log.v(TAG, "data.length: " + data.length);
if (data != null && data.length > 0) {
final StringBuilder stringBuilder = new StringBuilder(data.length);
for(byte byteChar : data) {
stringBuilder.append(String.format("%02X ", byteChar));
Log.v(TAG, String.format("%02X ", byteChar));
}
intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString());
}
sendBroadcast(intent);
}
private void showMessage(String str){
Toast.makeText(MainActivity.this, str, Toast.LENGTH_SHORT).show();
}
}
To enable notifications:
gatt.setCharacteristicNotification(yourCharacteristic, true);
BluetoothGattDescriptor desc = yourCharacteristic.getDescriptor(UUID);
then:
desc.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(desc);
After doing this, onCharacteristicChanged() should be called every time data is sent. Confirm that enabling notifications was successful by overriding the onDescriptorWrite() method.

Bluetooth Location Permissions on Android 6.0

First of all, I'd just like to point out that I've been learning Android for only a few weeks now so please excuse any shabby code. I'm trying to make an app which will simply list nearby bluetooth low energy devices (in the logs for now) and eventually connect to them.
Several weeks after starting this project, I have hit a brick wall with Android 6.0 location permissions that are required for a successful bluetooth scan. The following code is what I have at the moment:
package com.benstechtips.bluetoothletest4;
import android.annotation.TargetApi;
import android.app.Activity;
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.BluetoothProfile;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
#TargetApi(21)
public class MainActivity extends AppCompatActivity {
private BluetoothAdapter mBluetoothAdapter;
private int REQUEST_ENABLE_BT = 1;
private Handler mHandler;
private static final long SCAN_PERIOD = 10000;
private BluetoothLeScanner mLEScanner;
private ScanSettings settings;
private List<ScanFilter> filters;
private BluetoothGatt mGatt;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler = new Handler();
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, "BLE Not Supported", Toast.LENGTH_SHORT).show();
finish();
}
final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
}
#Override
protected void onResume() {
super.onResume();
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
} else {
if (Build.VERSION.SDK_INT >= 21) {
mLEScanner = mBluetoothAdapter.getBluetoothLeScanner();
settings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
filters = new ArrayList<ScanFilter>();
}
scanLeDevice(true);
}
}
#Override
protected void onPause() {
super.onPause();
if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
scanLeDevice(false);
}
}
#Override
protected void onDestroy() {
if (mGatt == null) {
return;
}
mGatt.close();
mGatt = null;
super.onDestroy();
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_ENABLE_BT) {
if (resultCode == Activity.RESULT_CANCELED) {
//Bluetooth not enabled.
finish();
return;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
private void scanLeDevice(final boolean enable) {
if (enable) {
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
Log.i("Scan Status", "Stopping Scan...");
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
} else {
mLEScanner.stopScan(mScanCallback);
}
}
}, SCAN_PERIOD);
if (Build.VERSION.SDK_INT < 21) {
Log.i("Scan Status", "Starting Scan...");
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mLEScanner.startScan(filters, settings, mScanCallback);
}
} else {
Log.i("Scan Status", "Stopping Scan...");
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
} else {
mLEScanner.stopScan(mScanCallback);
}
}
}
private ScanCallback mScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
Log.i("callbackType", String.valueOf(callbackType));
Log.i("result", result.toString());
BluetoothDevice btDevice = result.getDevice();
//connectToDevice(btDevice);
}
#Override
public void onBatchScanResults(List<ScanResult> results) {
for (ScanResult sr : results) {
Log.i("ScanResult - Results", sr.toString());
}
}
#Override
public void onScanFailed(int errorCode) {
Log.e("Scan Failed", "Error Code: " + errorCode);
}
};
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("onLeScan", device.toString());
//connectToDevice(device);
}
});
}
};
public void connectToDevice(BluetoothDevice device) {
if (mGatt == null) {
mGatt = device.connectGatt(this, false, 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("onConnectionStateChange", "Status: " + status);
switch (newState) {
case BluetoothProfile.STATE_CONNECTED:
Log.i("gattCallback", "STATE_CONNECTED");
gatt.discoverServices();
break;
case BluetoothProfile.STATE_DISCONNECTED:
Log.e("gattCallback", "STATE_DISCONNECTED");
break;
default:
Log.e("gattCallback", "STATE_OTHER");
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
List<BluetoothGattService> services = gatt.getServices();
Log.i("onServicesDiscovered", services.toString());
gatt.readCharacteristic(services.get(1).getCharacteristics().get
(0));
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic
characteristic, int status) {
Log.i("onCharacteristicRead", characteristic.toString());
gatt.disconnect();
}
};
Now, as far as I can tell, on a version of Android prior to version 6.0, this code would work and display the names of discovered devices (I have disabled the ability to connect to the device for the moment). However, on Android 6.0, I get the following error:
java.lang.SecurityException: Need ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission to get scan results
I understand that this is due to my app not having required location permissions. Although I have the following in my AndroidManifest.xml file, I know I need to request permission from the user.
<?xml version="1.0" encoding="utf-8"?>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
But my question is, how do I request permission from the user?
I encountered this same problem! This was a recently added security feature in Marshmallow. Here is what requesting the permission looks like:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (this.checkSelfPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{android.Manifest.permission.ACCESS_COARSE_LOCATION},
PERMISSION_REQUEST_COARSE_LOCATION);
}
}
such that
private static final int PERMISSION_REQUEST_COARSE_LOCATION = 1;
I just chose coarse location. As far as I know, it doesn't matter which one you pick. You can put this in the onCreate for your main activity. Hope this helps!

Categories

Resources