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.
Related
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.
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)
}
I am working with android BLE (Bluetooth Low Energy). I am having trouble in scanning BLE device using startLeScan(UUID[] serviceUuids, BluetoothAdapter.LeScanCallback callback) method while startLeScan(BluetoothAdapter.LeScanCallback callback) is working fine. When i use filter to scan specific serviceUUIDs , the callback is not executing. I am testing with samsung galaxy s6.I want to know whether this issue is device specific or there is some bug in scaning function.
I'm pretty sure it's not device specific.
First of all as IshArt mentioned you should use startScan from Android 5.0 on.
startScan(List<ScanFilter> filters, ScanSettings settings, ScanCallback callback)
From my experience the implementation of Scanfilters works fine if you go for MAC addresses but other filter setup parameters are problematic:
ScanFilter filter = new ScanFilter.Builder().setDeviceAddress(deviceMacAddress).build();
filters.add(filter);
If that is not an option for you, you can also implement your own filter. If you leave the filters list for the startScan() empty, it ignores all filtering and receives everything. Then in the callback you can write your own method to check whether the result meets your requirements or not.
This is a specific problem I've found with the Samsung Galaxy S6. I use the newer scanner API and only support devices on Android 5.0+. My testing has shown the S4, (haven't tested S5), S7, and S8 all work; I don't know why the S6 has issues.
The workaround it is, lamentably, to just filter manually on mac address after the devices are found.
Update
This fixed my issues above that I was having with the Galaxy S6.
Previously, I was adding two conditions to the same ScanFilter like this (Kotlin):
ScanFilter.Builder()
.setServiceUuid(BluetoothBlind.Service.ParcelUUID)
.setDeviceAddress(macAddress)
.build()
Changing it to split the conditions into multiple filters fixes it:
ScanFilter.Builder()
.setDeviceAddress(macAddress)
.build()
ScanFilter.Builder()
.setServiceUuid(BluetoothBlind.Service.ParcelUUID)
.build()
The issue with BLE filtered scanning is a known issue. See https://github.com/iDevicesInc/SweetBlue/wiki/Android-BLE-Issues for this and other BLE issues. The conclusion is simple: "You have to scan for all devices and do filtering yourself."
there's 2 scan method:
//Device scan callback Lollipop and above
private ScanCallback generateScanCallback(){
if(apiVersion> Build.VERSION_CODES.KITKAT) {
ScanCallback mScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
final BluetoothDevice device = result.getDevice();
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
Log.e(TAG, "running scan " + device.getAddress());
if (device.getAddress().equals(mDeviceAddress)) {
Log.e(TAG, "device founded, trying to connect");
scanLeDevice(false);
Intent gattServiceIntent = new Intent(mContext, BluetoothLeService.class);
mContext.bindService(gattServiceIntent, mServiceConnection, mContext.BIND_AUTO_CREATE);
mIndicationText.setText(mContext.getString(R.string.waiting));
}
}
});
}
};
return mScanCallback;
}
return null;
}
// Device scan callback KITKAT and below.
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
Log.e(TAG,"running scan "+device.getType());
if(device.getAddress().equals(mDeviceAddress)){
Log.e(TAG, "device founded, trying to connect");
scanLeDevice(false);
Intent gattServiceIntent = new Intent(mContext, BluetoothLeService.class);
mContext.bindService(gattServiceIntent, mServiceConnection, mContext.BIND_AUTO_CREATE);
mIndicationText.setText(mContext.getString(R.string.waiting));
}
}
});
}
};
and than:
if(apiVersion> Build.VERSION_CODES.KITKAT) {
scanner = mBluetoothAdapter.getBluetoothLeScanner();
// Device scan callback LOLLIPOP
scanner.startScan(generateScanCallback());
} else {
mBluetoothAdapter.startLeScan(mLeScanCallback);
}
you can customize your scan method as you want but you must know that there's 2 scan method one for android 5 and above and one for the other android OS
I am new to Android Platform. I am working with an application requires integration of Bluetooth. The requirement is instead of manually connecting and disconnecting a Bluetooth headset(HSP profile),Connection and disconnection should be possible within the application.Is it possible to connect and disconnect the device in Android devices running OS 4.2 ,4.3 and 4.4.If any one has a solution for this issue,Please advise me for the same.
It is possible, but sometimes not that simple.
To connect, start by checking whether or not the device you are running on has BT support at all:
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter==null) {
// device not support BT
}
If not - gracefully disable the BT portion of your app and move on.
If supported, check whether or not it is currently enabled (remember, the user can
turn BT on & off as with other communication channels):
boolean isEnabled = bluetoothAdapter.isEnabled(); // Equivalent to: getBluetoothState() == STATE_ON
And, if not enabled, allow the user to turn it on by firing an ACTION_REQUEST_ENABLE intent:
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, ENABLE_BT_CODE);
Once you are clear in terms of availability, perform lookup for the specific device you aim for.
It is always a good idea to start with the bonded device list maintained by Android:
Set<BluetoothDevice> bondedDevices = bluetoothAdapter.getBondedDevices();
for (BluetoothDevice device: pairedDevices) {
if (device is the one we look for) {
return device;
}
}
If not, you will need to issue a BT discovery command.
Discovery must never be performed on the UI thread, so please spawn a thread (use AsyncTask, Executer, etc. to do the work).
Discovery should not be performed when a BT connection operation is still taking place. The
impact on the device resources will be too high.
Start by setting your discovery receiver:
discoveryReceiver = new BroadcastReceiver() {
private boolean wasFound = false;
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
System.out.println(action);
if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
discoveryStatus = STARTED;
}
else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
discoveryStatus = FINISHED;
}
else if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (device is what we look for) {
stopDiscovery(context);
}
}
}
};
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
filter.addAction(BluetoothDevice.ACTION_FOUND);
context.registerReceiver(discoveryReceiver, filter);
And follow with a start off command:
boolean started = bluetoothAdapter.startDiscovery(); //async call!
if (!started) {
// log error
}
Once you find your device, you will then need to create a dedicated BT socket:
BluetoothSocket clientSocket = null;
try {
if (secureMode == SECURE) {
clientSocket = device.createRfcommSocketToServiceRecord(serviceUuid);
}
else { // INSECURE
clientSocket = device.createInsecureRfcommSocketToServiceRecord(serviceUuid);
}
if (clientSocket == null) {
throw new IOException();
}
} catch (IOException e) {
// log error
}
Followed by connect command:
clientSocket.connect(context);
Once connect returns, you can transmit data back & forth the way you do with sockets and when done:
clientSocket.close(context);
The above depicts the general flow. In many cases your work will be harder:
You will use different socket generation methods for secure vs. insecure BT modes. You will use different
methods to interrogate the device for supported UUIDs. You may also sometimes have to resort to reflection to activate hidden services e.g. getUuids() for Android < ver 15. And the list goes on.
It makes sense, especially for a beginner, to use a tool for this job.
My favorite (I am biased, I wrote it..) is BTWiz which will encapsulate the above
flow from you and will also provide you with a simple interface for async IO. Feel free to try it out.
In android how can my Activity will get to know if a Bluetooth A2DP device is connected to my device.
Is there any broadcast receiver for that?
How to write this broadcast receiver?
Starting from API 11 (Android 3.0) you can use BluetoothAdapter to discover devices connected to a specific bluetooth profile. I used the code below to discover a device by its name:
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (profile == BluetoothProfile.A2DP) {
boolean deviceConnected = false;
BluetoothA2dp btA2dp = (BluetoothA2dp) proxy;
List<BluetoothDevice> a2dpConnectedDevices = btA2dp.getConnectedDevices();
if (a2dpConnectedDevices.size() != 0) {
for (BluetoothDevice device : a2dpConnectedDevices) {
if (device.getName().contains("DEVICE_NAME")) {
deviceConnected = true;
}
}
}
if (!deviceConnected) {
Toast.makeText(getActivity(), "DEVICE NOT CONNECTED", Toast.LENGTH_SHORT).show();
}
mBluetoothAdapter.closeProfileProxy(BluetoothProfile.A2DP, btA2dp);
}
}
public void onServiceDisconnected(int profile) {
// TODO
}
};
mBluetoothAdapter.getProfileProxy(context, mProfileListener, BluetoothProfile.A2DP);
You can do that for every bluetooth profile. Take a look at Working with profiles in Android's guide.
However, as written in other answers, you can register a BroadcastReceiver to listen to connection events (like when you're working on android < 3.0).
You cannot get the list of connected devices by calling any API.
You need instead to listen to the intents ACTION_ACL_CONNECTED, ACTION_ACL_DISCONNECTED that notifies about devices being connected or disconnected.
No way to get the initial list of connected devices.
I had this problem in my app and the way I handle it (didn't find better...) is to bounce off/on the Bluetooth at application start to be sure to start with an empty list of connected devices, and then listen to the above intents.
muslidrikk's answer is broadly correct; however you can alternatively use fetchUUIDsWithSDP() and see what you get back... it's a bit of a hack though -- you'd have to know what UUIDs (capabilities) you could expect from the device, if it were turned on. And that might be difficult to guarantee.
For BluetoothHeadset specifically, you can call getConnectedDevices() to get connected devices for this specific profile.
Reference: http://developer.android.com/reference/android/bluetooth/BluetoothHeadset.html
Other cases you need to register a receiver for that.
In your activity, define broadcast receiver...
// Create a BroadcastReceiver for ACTION_FOUND
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
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);
// Add the name and address to an array adapter to show in a ListView
mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
}
}
};
// Register the BroadcastReceiver
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy