BLE Xamarin Android - android

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.

Related

How to filter BLE devices that are advertising extended advertising message?

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

How to use Wifimanager.LocalOnlyHotspotCallback in Xamarin.Forms

the environment is Xamarin.forms in android,
but there are no Information about this.
how can i get WifiConfiguration from callback.onstarted ?
OR can i WifiManager.LocalOnlyHotspotReservation get value from callback.onstarted ?
please check below code, the code is about to using wifi AP over oreo version
when java code, i refer this article
a link
private WifiManager wifiManager;
private WifiManager.LocalOnlyHotspotReservation reservation;
void SetHotSpot()
{
wifiManager = (WifiManager)Android.App.Application.Context.GetSystemService(Context.WifiService);
WifiManager.LocalOnlyHotspotCallback callback = new WifiManager.LocalOnlyHotspotCallback();
callback.OnStarted( reservation);
wifiManager.StartLocalOnlyHotspot(callback, new Handler());
}
void getConfiguration(object sender, System.EventArgs e)
{
if (reservation != null)
{
Log.Debug("config", reservation.WifiConfiguration.Ssid);
Log.Debug("config", reservation.WifiConfiguration.NetworkId.ToString());
Log.Debug("config", reservation.WifiConfiguration.PreSharedKey);
Log.Debug("config", reservation.WifiConfiguration.Bssid);
}
}
but when i click button, reservation is null. so Log Dose not output anything.
I converted the Java code here and came up with the following solution which seems to be working kindly take a look and let me know whether or not it works for you.
Add a Callback class that inherits from WifiManager.LocalOnlyHotspotCallback and pass the Activity in my case it is the MainActivity.
public class OreoWifiManagerCallback : WifiManager.LocalOnlyHotspotCallback
{
private const string TAG = nameof(OreoWifiManagerCallback);
private MainActivity mainActivity;
public OreoWifiManager(Activity _activity)
{
if (_activity.GetType() == typeof(MainActivity))
mainActivity = (MainActivity)_activity;
}
public override void OnStarted(WifiManager.LocalOnlyHotspotReservation reservation)
{
base.OnStarted(reservation);
Log.Debug(TAG, "Wifi Hotspot is on now");
mainActivity.mReservation = reservation;
}
public override void OnFailed([GeneratedEnum] LocalOnlyHotspotCallbackErrorCode reason)
{
base.OnFailed(reason);
Log.Debug(TAG, "onStopped: ");
}
public override void OnStopped()
{
base.OnStopped();
Log.Debug(TAG, "onFailed: ");
}
}
Then add a property in the MainActivity to keep track of the reservations
public WifiManager.LocalOnlyHotspotReservation mReservation { get; set; }
And then use these methods to turn on or off wifi in that Activity, also note that you can have a global field for wifi manager if needed.
private void TurnOnHotspot()
{
var WifiManager = (WifiManager)this.Application.GetSystemService(Android.Content.Context.WifiService);
WifiManager.StartLocalOnlyHotspot(new OreoWifiManagerCallback(this), new Handler());
}
private void TurnOffHotspot()
{
if (mReservation != null)
{
mReservation.Close();
}
}
Good luck
Feel free to revert at any time

RxAndroid Subscribe code never called

I'm fairly new to RxJava and RxAndroid, and while some things work, I'm now completely stumped by what I see as basic functionality not working.
I have a subscribe call on a Subject that never seems to run, and I can't figure out why:
public class PairManager implements DiscoveryManagerListener {
private Subscription wifiAvailableSubscription;
private Subscription debugSubscription;
private DiscoveryManager discoveryManager;
private AsyncSubject<Map<String, ConnectableDevice>> availableDevices;
public PairManager(Context appContext) {
DiscoveryManager.init(appContext);
discoveryManager = DiscoveryManager.getInstance();
discoveryManager.addListener(this);
availableDevices = AsyncSubject.<Map<String, ConnectableDevice>> create();
//
// This subscription doesn't work
//
debugSubscription = availableDevices
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Map<String, ConnectableDevice>>() {
#Override
public void call(Map<String, ConnectableDevice> stringConnectableDeviceMap) {
//
// This code is never run !
//
Timber.d(">> Available devices changed %s", stringConnectableDeviceMap);
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
Timber.d("Subscription failed %s", throwable);
}
});
availableDevices.onNext(Collections.<String, ConnectableDevice>emptyMap());
wifiAvailableSubscription = ReactiveNetwork.observeNetworkConnectivity(appContext)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Connectivity>() {
#Override
public void call(Connectivity connectivity) {
if (connectivity.getState().equals(NetworkInfo.State.CONNECTED) && connectivity.getType() == ConnectivityManager.TYPE_WIFI) {
discoveryManager.start();
} else {
discoveryManager.stop();
availableDevices.onNext(Collections.<String, ConnectableDevice>emptyMap());
}
}
});
}
public AsyncSubject<Map<String, ConnectableDevice>> getAvailableDevices() {
return availableDevices;
}
#Override
public void onDeviceAdded(DiscoveryManager manager, ConnectableDevice device) {
Timber.d("onDeviceAdded %s", device);
availableDevices.onNext(manager.getAllDevices());
Timber.d("Sanity check %s", availableDevices.getValue());
}
// ...
}
Is there a way to debug what is going wrong? I have tried creating basic Observable.from-type calls and logging those, and that works as expected. The sanity check log in onDeviceAdded also prints and indicates that availableDevices has in fact updated as expected. What am I doing wrong?
I've found the issue, I've used AsyncSubjects which only ever emit values when they are Completed, where I expect the functionality of BehaviorSubjects.
From the doccumentation:
When Connectivity changes, subscriber will be notified. Connectivity can change its state or type.
You say:
I have a subscribe call on a Subject
A subject won't return te last value. I will only return a value when onNext is called. I assume the Connectivity never changes so it never fires.

Bluetooth Low Energy - Android scanFilter not working

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.

Android BLE setReportDelay()

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)

Categories

Resources