I'm trying to implement a BLE scan app where i want to list all the devices nearby in a single scan rather than getting one BLE device at a time.
I read from the Android Documentation that i can use setReportDelay() in the Scan Setting Builder to delay the scan results and to use the BatchScanResults() to get a batch/list of devices found.
However when i try to use the setReportDelay() in the Scan Settings builder the scan does not start at all and i get the following error.
04-13 16:03:19.887 8454-8454/com.example.sasnee_lab2.sasbeacon D/BluetoothLeScanner﹕ could not find callback wrapper
Here is my StartScan function with Scan Settings
public void startScan(BluetoothLeScanner scanner)
{
ScanFilter filter = new ScanFilter.Builder().setDeviceName(null).build();
ArrayList<ScanFilter> filters = new ArrayList<ScanFilter>();
filters.add(filter);
ScanSettings settings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_POWER).setReportDelay(1l)
.build();
Log.i(TAG,"The setting are "+settings.getReportDelayMillis());
scanner.startScan(filters,settings,BLEScan);
}
And here is the Scan Callback.
private ScanCallback BLEScan = new ScanCallback() {
#Override
public void onBatchScanResults(List<ScanResult> results) {
Log.i(TAG,"The batch result is "+results.size());
}
#Override
public void onScanResult(int callbackType, ScanResult result) {
Log.i(TAG,"******************************************");
Log.i(TAG,"The scan result "+result);
Log.i(TAG,"------------------------------------------");
}
#Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
}
}
};
Please let me know if i'm doing anything wrong in the implementation here.
And thank you.
You must check whether your hardware supports scan batching through BluetoothAdapter.isOffloadedScanBatchingSupported(). If this returns false then you should not attempt to set a report delay.
Use this:
Java
boolean f = adapter.isOffloadedScanBatchingSupported();
if (!f) builder.setReportDelay(0) else builder.setReportDelay(1)
Kotlin
val f: Boolean = adapter.isOffloadedScanBatchingSupported
if (!f) builder.setReportDelay(0) else builder.setReportDelay(1)
Related
I have implemented the following BLE scan callback,
private final ScanCallback mScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
Log.d(TAG,"onScanResult: " +result.toString());
runOnUiThread(() -> {
if (result.getDevice().getName() != null && getString(R.string.unknown_device_text).compareTo(result.getDevice().getName().toLowerCase()) != 0) {
mLeDeviceListAdapter.addDevice(result.getDevice());
mLeDeviceListAdapter.notifyDataSetChanged();
}
});
super.onScanResult(callbackType, result);
}
#Override
public void onBatchScanResults(List<ScanResult> results) {
Log.d(TAG,"onBatchScanResults: " +results.toString());
super.onBatchScanResults(results);
}
#Override
public void onScanFailed(int errorCode) {
Log.d(TAG,"onScanFailed: errorCode: " +errorCode);
super.onScanFailed(errorCode);
}
};
However, in this callback I am not getting BLE devices that are advertising extended message. In contrast, in same place nRF app shows extended devices in their list.
Here is my scan method,
private void scanLeDevice() {
List<ScanFilter> filters = new ArrayList<>();
ScanFilter.Builder scanFilterBuilder = new ScanFilter.Builder();
filters.add(scanFilterBuilder.build());
ScanSettings.Builder settingsBuilder = new ScanSettings.Builder();
settingsBuilder.setPhy(ScanSettings.PHY_LE_ALL_SUPPORTED);
final BluetoothLeScanner bluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
bluetoothLeScanner.stopScan(mScanCallback);
Log.d(TAG, "scanLeDevice stopScan called");
}
}, SCAN_PERIOD);
bluetoothLeScanner.startScan(filters, settingsBuilder.build(), mScanCallback);
}
So, how can I filter and find the devices with extended advertising capabilities.
In order to show extended advertisements, you need to use the setLegacy(false) method. By default this is set to true, which is why you need to change it when setting up your scan settings.
Have a look at the links below for more information:-
ScanSettings.Builder setLegacy
How to scan Bluetooth 5 extended advertising with Pixel 3a
UPDATE
You can filter only BLE devices that are doing extended adverts by checking the advert type. You can access the advert type by reading the scanRecord (e.g. using the getBytes method). You can read further on how to read the advert type here and here. Legacy adverts will be one of the following 4 types:-
ADV_IND
ADV_DIRECT_IND
ADV_NONCONN_IND
ADV_SCAN_IND
While extended adverts will be one of the following 4 types:
ADV_EXT_IND
AUX_ADV_IND
AUX_SYNC_IND
AUX_CHAIN_IND
This can be see in more details in the table below:-
Below are some other useful links on understanding the meaning of advert packets:-
Bluetooth 5 adverts: Everything you need to know
How do iBeacons work
BLE advertising primer
I'm using the AltBeacon library for detecting iBeacons in my Android app. The code I have works on the following devices:
Xiaomi MI9 (Android 10.0)
Motorola Moto G4 (Android 6.0.1)
Huawei P Smart (Android 8.0)
Samsung Galaxy S8 (Android 9.0)
However, the same code doesn't work for a OnePlus 6 (Android 10, OxygenOS 10.3.2). It doesn't detect any beacons in my app. I tried to detect the beacons using an other app (Locate), that works. The creator of the AltBeacon library told me that Locate uses the AltBeacon library, so the beacons are detectable. This means my code setup is wrong. Can you help me by finding out what is wrong with my setup?
I checked (e.g.) this answer, although it didn't fix my problem. I turned debugging on for the BeaconManager but nothing interesting came out of that (an example at the bottom of this question).
In the ViewModel I call the MyStateManager. It contains a List regionsInRange, which contains beacons that are in range. I left out some code because I think it is irrelevant. If you feel like I left out too much, I will add it.
public class MyStateManager implements BootstrapNotifier {
private static final MyStateManager instance = new MyStateManager();
private final MyBeaconHelper myBeaconHelper;
// ViewModel accessess this List to retrieve the beacons that are found.
public final List<Region> regionsInRange = new ArrayList<>();
private PresenceRegistrationStateManager() {
presenceRegistrationBeaconHelper = new PresenceRegistrationBeaconHelper(this);
updateScanningRegions();
}
#Override
public Context getApplicationContext() {
return MyApplication.getAppContext();
}
#Override
public void didEnterRegion(Region region) {
//Empty method
}
#Override
public void didExitRegion(Region region) {
//Empty method
}
#Override
public void didDetermineStateForRegion(int status, Region region) {
if (status == OUTSIDE) {
regionsInRange.remove(region);
} else {
if (!regionsInRange.contains(region)) {
regionsInRange.add(region);
}
}
updateState();
}
public static MyStateManager getInstance() {
return instance;
}
public void updateState() {
// Own implementation here
}
private void updateScanningRegions() {
// add all the regions here
}
}
In addition, this is the MyBeaconHelper:
public class MyBeaconHelper implements BeaconConsumer, Serializable {
private transient final RegionBootstrap regionBootstrap;
private List<Region> scanRegions = new ArrayList<>();
public MyBeaconHelper(BootstrapNotifier bootstrapNotifier) {
BeaconManager beaconManager = BeaconManager.getInstanceForApplication(getApplicationContext());
beaconManager.getBeaconParsers().clear();
beaconManager.getBeaconParsers().add(new BeaconParser().setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"));
LogManager.setVerboseLoggingEnabled(true);
beaconManager.bind(this);
regionBootstrap = new RegionBootstrap(bootstrapNotifier, new ArrayList<>());
}
#Override
public void onBeaconServiceConnect() {
//Empty method
}
#Override
public Context getApplicationContext() {
return MyApplication.getAppContext();
}
#Override
public void unbindService(ServiceConnection serviceConnection) {
getApplicationContext().unbindService(serviceConnection);
}
#Override
public boolean bindService(Intent intent, ServiceConnection serviceConnection, int i) {
return getApplicationContext().bindService(intent, serviceConnection, i);
}
public void updateScanRegions(List<Region> newRegions) {
for (Region oldRegion : this.scanRegions) {
if (!newRegions.contains(oldRegion)) {
regionBootstrap.removeRegion(oldRegion);
}
}
for (Region newRegion : newRegions) {
if (!this.scanRegions.contains(newRegion)) {
regionBootstrap.addRegion(newRegion);
}
}
this.scanRegions = newRegions;
}
}
When I turned debugging on for the BeaconManager, it showed me this a lot of times:
2020-03-31 11:57:30.181 25259-25259/com.my.app D/CycledLeScanner: starting a new scan cycle
2020-03-31 11:57:30.181 25259-25259/com.my.app D/CycledLeScanner: We are already scanning and have been for 1134 millis
2020-03-31 11:57:30.181 25259-25259/com.my.app D/CycledLeScanner: Waiting to stop scan cycle for another 1100 milliseconds
2020-03-31 11:57:30.181 25259-25259/com.my.app D/CycledLeScanner: Scan started
2020-03-31 11:57:31.213 25259-25259/com.my.app D/CycledLeScanner: Waiting to stop scan cycle for another 69 milliseconds
2020-03-31 11:57:31.323 25259-25259/com.my.app D/CycledLeScanner: Done with scan cycle
It keeps printing these lines over and over again...
The log messages shown (these are for OnePlus, yes?) indicate that BLE scanning is started. Do you see any log lines showing hex bytes of the packets detected? If BLE scanning is actually functioning you should. You may want to compare the logs output by the other devices.
Are you certain proper location permission has been granted to your app on the OnePlus? You can check in Settings -> Apps - > You App -> Permissions. Also confirm Bluetooth is on and location is on for the global phone settings (but if Locate works on the same device, this should not be a problem .)
It is not clear if this is related, but the use of beaconManager.bind() at the same time as RegionBootstrap is unnecessary and may cause conflicts. The code appears to not use the BeaconConsumer interface that is called back by the bind method. I suggest you remove the bind call, the use of BeaconConsumer and remove all that interface's callback methods just to be sure.
I'm developing an android app that needs to connect to a Bluetooth-low-energy device. In order of achieving that goal, and following the Android Dev page, I have included the correct permissions in the manifest file. In the mainActivity I'm trying to scan for BLE devices and printing the result on the screen. The code looks like this:
final BluetoothLeScanner bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
bluetoothLeScanner.startScan(callback);
// Before 5 seconds, stop the scan and show results.
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
bluetoothLeScanner.stopScan(callback);
callback.onBatchScanResults(results);
callback.onScanFailed(2);
callback.onScanResult(3,result);
listOfResults.setText(results.toString());
}
},5000);
Where:
bluetoothApater is the BlueoothAdapter needed to perform the operation as it's told in the android page,
bluetoothLeScanner is the object needed to perform LE scan operations,
callback is a Scan call back object,
results is a List < ScanResult >
result is a ScanResult,
and listOfResults is text view.
The problem maybe is in the method used, because according to the Android Official Page, we find three voids to perform with a callback (onBatchScanResult, onScanResult, and onScanFailed), but I only get working onBatchScanResult.
Why no device is shown? The only thing printed is the name of the activity, the name of the package and the app name.
The way I did it was to implement scanCallback() object and overriding the onScanResult() or onBatchScanResults() as needed.
private ScanCallback callback= new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
// handles scan result
}
#Override
public void onBatchScanResults(List<ScanResult> results) {
super.onBatchScanResults(results);
// handles batch scan results
for (ScanResult result : results) {
// you can iterate through each result like so
}
}
#Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
// handles error
}
};
You then pass callback inside of stopScan() and startScan()
bluetoothLeScanner.startScan(callback);
bluetoothLeScanner.stopScan(callback);
Also consider using result.getDevice() so you can retrieve the specific data of the device you need instead of a large chunk of information. For example, result.getDevice().getAddress() if you want the address only.
Have you added location permissions in manifest and also check if location permission is granted at runtime? If you havent done this, your results will always be null.
I am trying to create proof of concept to find nearby bluetooth low energy devices.
Here is my try:
public async void StartScan()
{
BluetoothAdapter adapter = BluetoothAdapter.DefaultAdapter;
adapter.Enable();
await Task.Delay(TimeSpan.FromMinutes(1)); // rode that this my help but didn't
if (adapter == null) return;
BluetoothLeScanner scanner = adapter.BluetoothLeScanner;
scanner?.StartScan(_callback);
}
The callback class:
public class MyScanCallback : ScanCallback
{
public override void OnBatchScanResults(IList<ScanResult> results)
{
base.OnBatchScanResults(results);
}
public override void OnScanResult(ScanCallbackType callbackType, ScanResult result)
{
base.OnScanResult(callbackType, result);
}
public override void OnScanFailed(ScanFailure errorCode)
{
base.OnScanFailed(errorCode);
}
}
I`ve spent some time looking in Internet but each solutions gives me always the same error. Everytime the code is executed it goes into OnScanFailed method with enum paramter ApplicationRegistrationFailed.
Does anyone faces the same issue?
PS. I have added required premissions.
I have two Android devices communicating using BLE. One is adverting while the other is scanning and receiving. When I advertise using a Service UUID, the other device will pick it up just fine. However, when I filter by Service UUID, the scanning device won't pick up the advertisement at all. Both devices are using the exact same UUID to advertise/scan. The scanning device is running 5.0.1. This is the code I have set up for the scanfilter:
P_UUID = new ParcelUuid(UUID.fromString("6be5ab1b-328b-4709-a737-84db596e10d2"));
B_scanFilter = new ScanFilter.Builder().setServiceUuid(P_UUID).build();
list = new ArrayList<ScanFilter>();
list.add(B_scanFilter);
On the advertising device, this is the code I have for the AdvertiseData:
//The random UUID is just a placeholder. Using P_UUID for both the serviceUUID and serviceDataUUID doesn't change anything
B_advertiseData = new AdvertiseData.Builder().addServiceUuid(P_UUID).addServiceData(new ParcelUuid(UUID.randomUUID()), getByteArray(data)).build();
And finally, here is the scanCallback and startScan code:
B_scanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
Toast.makeText(getApplicationContext(),"Received Data", Toast.LENGTH_SHORT).show();
}
#Override
public void onBatchScanResults(List<ScanResult> results) {
super.onBatchScanResults(results);
Toast.makeText(getApplicationContext(),"Received Data(Batch)", Toast.LENGTH_SHORT).show();
}
#Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
Toast.makeText(getApplicationContext(),"Scan failed", Toast.LENGTH_SHORT).show();
Log.e(TAG, "Scan Error: " + Integer.toString(errorCode));
}
};
B_scanner.startScan(list, B_scanSettings, B_scanCallback);
Toast.makeText(getApplicationContext(),"Scan Started", Toast.LENGTH_SHORT).show();
Note that with B_scanCallback, none of the methods are called during scanning(even onScanFailed()).
Any help with this issue would be greatly appreciated! If you want me to post more of the code, I can do that. I only posted the code that I think could be causing this problem.