BLE Scan Failed - android

I am using MI note 4(Android 7.0) and Moto x play (Android 7.1.1)
I am doing BLE scan in sperate service.
While scanning I am getting scan response as "scan failed"
Turning ON/OFF Bluetooth is not affecting in scan response.
Turning ON/OFF Wifi is also not affecting in scan response.
(But in this case android inbuilt(from Settings->Bluetooth) Bluetooth scanning was working fine).
I used BLE scanner app also but that app is also not detecting BLE advertisement!
I tried with Turn ON/OFF airplane mode with this and my device is able to scan without fail.
Scan Function:
mLeScanner.startScan(filters, scanSettings, mScanCallback);
ScanCallback:
ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
Log.e("TAG","onScanResult");
}
#Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
Log.e("TAG","onScanFailed");
}
}
ScanSettings:
scanSettings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.build();
filters:
List<ScanFilter> filters = new ArrayList<>();
ScanFilter filter = new ScanFilter.Builder().setDeviceAddress("device address").build();
filters.add(filter);
Beacon Scan filter
ScanFilter.Builder builder = new ScanFilter.Builder();
builder.setManufacturerData(0x004c, new byte[]{});
Anyone have an idea why it only worked with switching airplane mode?
will network affect for BLE scanning?

The error code 0x02 means SCAN_FAILED_APPLICATION_REGISTRATION_FAILED(Fails to start scan as app cannot be registered). This means, before moving to scan we need to initialize Bluetooth adapter
/**
* Initialize BluetoothAdapter
* Check the device has the hardware feature BLE
* Then enable the hardware,
*/
public boolean init(Context context) {
BluetoothManager bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
return mBluetoothAdapter != null && context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
}
Then register receiver
**
* Register GATT update receiver
*/
private void registerServiceReceiver() {
this.registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());
registerReceiver(mReceiver, new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
}
The service initialization method also including in the answer. The Service creation is optional.
/**
* Initialize Bluetooth service.
*/
public void initBLEService(Context context) {
try {
this.mContext = context;
if (mBLEService == null) {
Intent gattServiceIntent = new Intent(mContext, BLEService.class);
if (this.mContext != null) {
isBind = mContext.bindService(gattServiceIntent, mServiceConnection, mContext.BIND_AUTO_CREATE);
}
}
} catch (Exception e) {
AppLog.logError(TAG, e.getMessage());
}
}
I hope you have already added permission in the manifest given below
<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" />
I hope this will help you.

My issue was resolved after user permission ACCESS_COARSE_LOCATION is granted. You could ask user permission in onStart()
Java Syntax
if (ContextCompat.checkSelfPermission(this, ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
this,
new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},
PERMISSIONS_REQUEST_LOCATION);
}
Kotlin syntax
if (ContextCompat.checkSelfPermission(this, ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION),
PERMISSIONS_REQUEST_LOCATION)
}

Related

BluetoothLeScanner ScanFilter on Service UUID does not work

I am working on a simple app to scan for BLE devices, and filter the scan results down to find only my ESP32. I want to find my ESP32 by filtering on service UUIDs. I have confirmed that my custom service exists in the advertising data (via nRF Connect listing the service in "Complete list of 128-bit Service UUIDs"). However, when I add the service UUID filter as a ScanFilter before scanning, my ScanCallback is never called. I can however remove the scan filter, and manually check if the service exists in the ScanCallback just fine. See the below code examples.
This first example is using manual filtering, and works just fine.
private ParcelUuid mServiceUuidFilter = ParcelUuid.fromString("6E400001-B5A3-F393-E0A9-E50E24DCCA9E");
private final ScanCallback mScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
boolean matched = (mServiceUuidFilter == null);
if (mServiceUuidFilter != null && result.getScanRecord().getServiceUuids() != null) {
for (ParcelUuid uuid : result.getScanRecord().getServiceUuids()) {
if (uuid.equals(mServiceUuidFilter)) {
matched = true;
break;
}
}
}
if (matched) {
// do something
}
}
};
private void scan() {
List<ScanFilter> scanFilters = new ArrayList<>();
// works
ScanFilter filter = new ScanFilter.Builder().build();
scanFilters.add(filter);
ScanSettings scanSettings = new ScanSettings.Builder().build();
// skipping mScanner initialization
mScanner.startScan(scanFilters, scanSettings, mScanCallback);
}
This second example uses a ScanFilter for filtering, and does not work. mScanCallback is never called.
private ParcelUuid mServiceUuidFilter = ParcelUuid.fromString("6E400001-B5A3-F393-E0A9-E50E24DCCA9E");
private final ScanCallback mScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
// do something
}
};
private void scan() {
List<ScanFilter> scanFilters = new ArrayList<>();
// does not work
ScanFilter filter = new ScanFilter.Builder().setServiceUuid(mServiceUuidFilter).build();
scanFilters.add(filter);
ScanSettings scanSettings = new ScanSettings.Builder().build();
// skipping mScanner initialization
mScanner.startScan(scanFilters, scanSettings, mScanCallback);
}
Is there something I'm missing here?
Hm, I'm not exactly sure what I changed, but in setting my minimum API level to 21, this started working.
The ScanFilter relies on the service UUIDs advertised by the BLE server. The ESP32 BLE library doesn't advertise services automatically, you have to do it similarly to this (where you might normally be only calling bleServer->startAdvertising()):
BLEAdvertising *ad = bleServer->getAdvertising();
ad->addServiceUUID(UUID_SERVICE);
bleServer->startAdvertising();
This needs to be done in addition to bleServer->createService(UUID_SERVICE), which I wasn't aware of before.

How to get the uuid for ble device in android

I am new to android development. I am working with ble scanning,connection etc.I want to display specific ble device according to UUID. How can I scan ble device using UUID.
You can use startScan (List<ScanFilter> filters, ScanSettings settings, ScanCallback callback) method inside class BluetoothLeScanner. Set the service UUID in the ScanFilter to display specific BLE devices according to UUIDs.
Example on scan:
#Override
public void startScan(final BleDeviceScanCallback callback) {
mCallback = callback;
List<ScanFilter> filters = new ArrayList<ScanFilter>();
if (mServiceUuids != null && mServiceUuids.length > 0) {
for (UUID uuid : mServiceUuids) {
ScanFilter filter = new ScanFilter.Builder().setServiceUuid(
new ParcelUuid(uuid)).build();
filters.add(filter);
}
}
ScanSettings settings = new ScanSettings.Builder().build();
mBleScanner = mBluetoothAdapter.getBluetoothLeScanner();
if (mBleScanner != null) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
mBleScanner.startScan(filters, settings, mScanCallback);
} else {
// Unless required permissions were acquired, scan does not start.
if (BleUtils.isBLEPermission(mContext)) {
mBleScanner.startScan(filters, settings, mScanCallback);
}
}
}
}
Refer more here
More API details can be found here.
Learn about Scan Filters here.
Google Bluetooth LE Central Application Example here.
Please Note: BluetoothAdapter.startLeScan method was deprecated in API level 21. use startScan(List, ScanSettings, ScanCallback) instead.

Android Bluetooth app can't discover other devices

I am a beginner in android, and tried a demo code Bluetoothchat in https://developer.android.com/samples/BluetoothChat/index.html. It doesn't work well on my phone(Nexus 5, android 6.0.1). the full package is on github. https://github.com/googlesamples/android-BluetoothChat. I didn't change anything in the demo. Android studio 2.1.2
It failed to discover other Bluetooth devices(an ipad and a blackberry). The visibility of the devices were on. If I use the default Bluetooth's discovery function on the nexus 5, the two devices were showed on the list.
I have also tried some other demos about Bluetooth discovery app. They did not work on my phone either. I know the basic idea about how to find a device, but couldn't find why it didn't work.
The relative code in Bluetoothchat is here:
basic setup
mNewDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
// Find and set up the ListView for newly discovered devices
ListView newDevicesListView = (ListView) findViewById(R.id.new_devices);
newDevicesListView.setAdapter(mNewDevicesArrayAdapter);
newDevicesListView.setOnItemClickListener(mDeviceClickListener);
// Register for broadcasts when a device is discovered
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
this.registerReceiver(mReceiver, filter);
// Register for broadcasts when discovery has finished
filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
this.registerReceiver(mReceiver, filter);
// Get the local Bluetooth adapter
mBtAdapter = BluetoothAdapter.getDefaultAdapter();
click the scanButton to scan devices
// Initialize the button to perform device discovery
Button scanButton = (Button) findViewById(R.id.button_scan);
scanButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
doDiscovery();
v.setVisibility(View.GONE);
}
});
// Initialize array adapters.
// one for newly discovered devices
discovery part
/**
* Start device discover with the BluetoothAdapter
*/
private void doDiscovery() {
Log.d(TAG, "doDiscovery()");
// Indicate scanning in the title
setProgressBarIndeterminateVisibility(true);
setTitle(R.string.scanning);
// Turn on sub-title for new devices
findViewById(R.id.title_new_devices).setVisibility(View.VISIBLE);
// If we're already discovering, stop it
if (mBtAdapter.isDiscovering()) {
mBtAdapter.cancelDiscovery();
}
// Request discover from BluetoothAdapter
mBtAdapter.startDiscovery();
}
Broadcast receiver
/**
* The BroadcastReceiver that listens for discovered devices and changes the title when
* discovery is finished
*/
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
// When discovery finds a device
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// Get the BluetoothDevice object from the Intent
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// If it's already paired, skip it, because it's been listed already
if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
}
// When discovery is finished, change the Activity title
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
setProgressBarIndeterminateVisibility(false);
setTitle(R.string.select_device);
if (mNewDevicesArrayAdapter.getCount() == 0) {
String noDevices = getResources().getText(R.string.none_found).toString();
mNewDevicesArrayAdapter.add(noDevices);
}
}
}
};
destroy part
#Override
protected void onDestroy() {
super.onDestroy();
// Make sure we're not doing discovery anymore
if (mBtAdapter != null) {
mBtAdapter.cancelDiscovery();
}
// Unregister broadcast listeners
this.unregisterReceiver(mReceiver);
}
Could someone help me? Thanks in advance!
Make sure you've added these permissions to your android manifest.
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
and make sure you're requesting ACCESS_COARSE_LOCATION if it doesn't do it automatically:
// Handling permissions.
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
// Permission is not granted
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.ACCESS_COARSE_LOCATION)) {
// Not to annoy user.
Toast.makeText(this, "Permission must be granted to use the app.", Toast.LENGTH_SHORT).show();
} else {
// Request permission.
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},
REQUEST_PERMISSION_BLUETOOTH);
}
} else {
// Permission has already been granted.
Toast.makeText(this, "Permission already granted.", Toast.LENGTH_SHORT).show();
}
and specify what should happen if the user accept/decline the request:
#Override
public void onRequestPermissionsResult(int requestCode,
#NonNull String permissions[], #NonNull int[] grantResults) {
switch (requestCode) {
case REQUEST_PERMISSION_BLUETOOTH: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "Permission granted.", Toast.LENGTH_SHORT).show();
// Permission granted.
} else {
Toast.makeText(this, "Permission must be granted to use the application.", Toast.LENGTH_SHORT).show();
}
}
}
}

Bluetooth isEnabled() fails

I use this code to start bluetooth:
BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
if(btAdapter == null)
returns; /no bluetooth
if(btAdapter.isEnabled() == false)
{ Toast("Bluetooth off. Starting it...");
if(btAdapter.enable() == false)
Toast("Error enabling bluetooth.");
}
It should be very simple. Just gets and adapter and if it is not enabled then I start it.
The problem is that isEnabled() returns false when bluetooth is actualy ON (it should return true) And calling to enable() returns false so it shows "Error enabling bluetooth." I guess because it was already ON. After that my bluetooth symbols (in the status bar) is gone.
Any hint?
Of course I have the permissions.
ensure you have the permissions correct in the manifest file
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
You can't enable Bluetooth without the user's confirmation. You need to do it like this,
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter != null) {
// Device supports Bluetooth
if (!mBluetoothAdapter.isEnabled()) {
// Bluetooth isn't enabled, so enable it.
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}
A dialog will appear requesting user permission to enable Bluetooth, as shown below.
If the user responds "Yes," the system will begin to enable Bluetooth and focus will return to your application once the process completes (or fails).
To check Bluetooth state, ON programmatically:
Add Following Permission : -
android.permission.BLUETOOTH
Use Following Function For Enable BLUETOOTH:-
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
private void turnOn() {
if (!mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new
Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivity(enableBtIntent);
}
}

How can I programmatically tell if a Bluetooth device is connected?

I understand how to get a list of paired devices, but how can I tell if they are connected?
It must be possible since I see them listed in my phone's Bluetooth device list and it states their connection status.
Add the Bluetooth permission to your AndroidManifest,
<uses-permission android:name="android.permission.BLUETOOTH" />
Then use intent filters to listen to the ACTION_ACL_CONNECTED, ACTION_ACL_DISCONNECT_REQUESTED, and ACTION_ACL_DISCONNECTED broadcasts:
public void onCreate() {
...
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED);
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
this.registerReceiver(mReceiver, filter);
}
//The BroadcastReceiver that listens for bluetooth broadcasts
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
... //Device found
}
else if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) {
... //Device is now connected
}
else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
... //Done searching
}
else if (BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED.equals(action)) {
... //Device is about to disconnect
}
else if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
... //Device has disconnected
}
}
};
A few notes:
There is no way to retrieve a list of connected devices at application startup. The Bluetooth API does not allow you to query, instead it allows you to listen to changes.
A hoaky workaround to the above problem would be to retrieve the list of all known/paired devices... then trying to connect to each one (to determine if you're connected).
Alternatively, you could have a background service watch the Bluetooth API and write the device states to disk for your application to use at a later date.
In my use case I only wanted to see if a Bluetooth headset is connected for a VoIP app. The following solution worked for me.
Kotlin:
fun isBluetoothHeadsetConnected(): Boolean {
val mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
return (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled
&& mBluetoothAdapter.getProfileConnectionState(BluetoothHeadset.HEADSET) == BluetoothHeadset.STATE_CONNECTED)
}
Java:
public static boolean isBluetoothHeadsetConnected() {
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
return mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()
&& mBluetoothAdapter.getProfileConnectionState(BluetoothHeadset.HEADSET) == BluetoothHeadset.STATE_CONNECTED;
}
Of course you'll need the Bluetooth permission:
<uses-permission android:name="android.permission.BLUETOOTH" />
There is an isConnected function in the BluetoothDevice system API in https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/bluetooth/BluetoothDevice.java.
If you want to know if a bounded (paired) device is currently connected or not, the following function works fine for me:
public static boolean isConnected(BluetoothDevice device) {
try {
Method m = device.getClass().getMethod("isConnected", (Class[]) null);
boolean connected = (boolean) m.invoke(device, (Object[]) null);
return connected;
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
For some reason, BluetoothAdapter.ACTION_ACL_CONNECTED could not be resolved by Android Studio. Perhaps it was deprecated in Android 4.2.2?
Here is a modification of Skylarsutton's code (Big thanks to Skylarsutton for his answer.) . The registration code is the same; the receiver code differs slightly. I use this in a service which updates a Bluetooth-connected flag that other parts of the app reference.
public void onCreate() {
//...
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED);
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
this.registerReceiver(BTReceiver, filter);
}
//The BroadcastReceiver that listens for bluetooth broadcasts
private final BroadcastReceiver BTReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) {
//Do something if connected
Toast.makeText(getApplicationContext(), "BT Connected", Toast.LENGTH_SHORT).show();
}
else if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
//Do something if disconnected
Toast.makeText(getApplicationContext(), "BT Disconnected", Toast.LENGTH_SHORT).show();
}
//else if...
}
};
This code is for the headset profiles, and probably it will work for other profiles too.
First you need to provide a profile listener (Kotlin code):
private val mProfileListener = object : BluetoothProfile.ServiceListener {
override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) {
if (profile == BluetoothProfile.HEADSET)
mBluetoothHeadset = proxy as BluetoothHeadset
}
override fun onServiceDisconnected(profile: Int) {
if (profile == BluetoothProfile.HEADSET) {
mBluetoothHeadset = null
}
}
}
Then while checking Bluetooth:
mBluetoothAdapter.getProfileProxy(context, mProfileListener, BluetoothProfile.HEADSET)
if (!mBluetoothAdapter.isEnabled) {
return Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
}
It takes a bit of time until onSeviceConnected is called. After that you may get the list of the connected headset devices from:
mBluetoothHeadset!!.connectedDevices
BluetoothAdapter.getDefaultAdapter().isEnabled ->
returns true when Bluetooth is open.
val audioManager = this.getSystemService(Context.AUDIO_SERVICE) as AudioManager
audioManager.isBluetoothScoOn ->
returns true when a device connected
I was really looking for a way to fetch the connection status of a device, not listen to connection events. Here's what worked for me:
BluetoothManager bm = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
List<BluetoothDevice> devices = bm.getConnectedDevices(BluetoothProfile.GATT);
int status = -1;
for (BluetoothDevice device : devices) {
status = bm.getConnectionState(device, BLuetoothGatt.GATT);
// compare status to:
// BluetoothProfile.STATE_CONNECTED
// BluetoothProfile.STATE_CONNECTING
// BluetoothProfile.STATE_DISCONNECTED
// BluetoothProfile.STATE_DISCONNECTING
}

Categories

Resources