Android OnePlus6 can't detect iBeacon using AltBeacon library - android

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.

Related

AltBeaconLibrary in service and seperate process. Part 2

Help is needed. Thanks to David for the last answer to the question -AltBeacon service in separate android process.
I'm trying to implement on the Xamarin Android - AltBeacon Library in the service and in a separate process. (A separate process is needed to ensure that scanning works constantly, round the clock.
What would the android not cut the scanning after a certain time, after the phone goes into sleep mode).
1. What did I do after David's instructions -
-added attributes in the service definition:
 
[Service (Name = SERVICE_NAME, Process = ": myProcess", Enabled = true, Exported = false, IsolatedProcess = false)]
installed a new version of the library - AltBeacon 2.11. To date, Xamarin does not have version 2.11. The latest version is 2.7
 I downloaded the AltBeacon v2.11 native library and wrapped it with Managed Callable Wrappers (MCW). [(https://developer.xamarin.com/guides/android/advanced_topics/binding-a-java-library/)][1].
 As a result, I got a full library on Xamarin.
 - Connected this library to the project. For testing, I removed the Process: myProcess attribute and ran the application.
  In one process, everything works well. The service works and finds the bicons. Perfectly!
  But - 
 As soon as I started the service in another process I stopped getting the bicons.
 After the other method that triggers this is OnBeaconServiceConnect ().
public void OnBeaconServiceConnect()
{
for (int i = 0; i < guids.Length; i++)
{
var uuid = Identifier.Parse(guids[i]);
var region = new Region("R" + i, uuid, null, null);
beaconManager.StartRangingBeaconsInRegion(region);
}
}
It turns out that the method does not work in the Range Notifier object =(
A rough example of the implementation of the service:
public class myService:Service
{
private BeaconWorker beaconWorker;
public myService()
{
beaconWorker=new beaconWorker(DroidApplication.CurrentInstanceApp);
}
public void MainMethod()
{
var notification = ...Build();
StartForeground(s_id, notification);
while(true)
{
StartMainWork(guids)
}
}
public void StartMainWork(string guid)
{
beaconWorker.GetResult();
}
}
/////////////////////////////////////
public class BeaconWorker:IBeaconConsumer
{
List<Beacon> Beacons;
private Context context;
private RangeNotifier rangeNotifier;
private BeaconManager beaconManager;
//This is main configuring scanning
public BeaconWorker(Context context)
{
Context = context;
this.guids = ...;
rangeNotifier = new RangeNotifier();
BeaconManager.SetDebug(true);
beaconManager =
BeaconManager.GetInstanceForApplication(context);
beaconManager.SetForegroundBetweenScanPeriod(1000);
beaconManager.SetForegroundScanPeriod(1000);
beaconManager.SetBackgroundMode(false);
var beaconParser = new BeaconParser();
beaconParser.SetBeaconLayout("...");
beaconManager.BeaconParsers.Add(beaconParser);
beaconManager.SetRangeNotifier(rangeNotifier);
beaconManager.ApplySettings();
}
//Method that do bind
public void GetResult()
{
beaconManager.Bind(this);
Task.Wait(3000);
beaconManager.UnBind(this);
}
//Implement IBeaconConsumer BindService
public bool BindService(Intent p0, IServiceConnection p1, int p2)
{
return context.BindService(p0, p1, Bind.AutoCreate);
}
public void OnBeaconServiceConnect()
{
for (int i = 0; i < guids.Length; i++)
{
var uuid = Identifier.Parse(guids[i]);
var region = new Region("R" + i, uuid, null, null);
beaconManager.StartRangingBeaconsInRegion(region);
}
}
private class RangeNotifier
{
//THIS METHOD DOES NOT INVOKE =(
public void DidRangeBeaconsInRegion(ICollection<Beacon> beacons, Region region)
{
this.Beacons = beacons;
}
}
}
In the logs of the phone there is such information:
Time Device Name Type PID Tag Message
01-18 18:52:25.370 AGM A8 Warning 17056 BeaconManager Ranging/Monitoring may not be controlled from a separate BeaconScanner process. To remove this warning, please wrap this call in: if (beaconManager.isMainProcess())
Time Device Name Type PID Tag Message
01-18 18:52:25.370 AGM A8 Debug 17056 BeaconManager we have a connection to the service now
Time Device Name Type PID Tag Message
01-18 18:52:25.361 AGM A8 Debug 17056 BeaconManager consumer count is now: 1
Time Device Name Type PID Tag Message
01-18 18:52:25.363 AGM A8 Info 17024 BeaconService beaconService version 2.11 is starting up on the main process
I know about the verification David pointed out in the answer to IsMainProcess() method. But how can I call this check if all this happens in the service itself and not in the main application process?
  Tell me where I'm wrong. What else can I add?
  In the article https://github.com/AltBeacon/android-beacon-library/pull/479
  on how much I realized that it makes no sense to specify a RangeNotifier in a separate process. Is it so?
  I will be grateful for any help! Thank you!

AltBeacon service in separate android process

I need help. There is an application for Xamarin Android. Inside it, a service is started that works with the AltBeacon library.
In this service, a thread is created, in which beacons are constantly being scanned. Service started as StartForeground(...).
The service should work constantly, so it was decided to run it in a separate process, because after a while the android system stops allocating
memory and service terminates.
If you run the application and the service in one process, everything works fine. Service works, beacons are scanned.
But as soon as I run it in a separate process (using the Process =: myProcess attribute), the scanning not works.
The DidRangeBeaconsInRegion method does not work for the IRangeNotifier implementation object.
It simply does not work, there are no exceptions.
Brief code base:
public class BeaconsWorker : Java.Lang.Object, IBeaconConsumer
{
private string[] guids;
private readonly Context context;
private readonly BeaconManager beaconManager;
private readonly RangeNotifier rangeNotifier;
private readonly List<BeaconEntry> beacons;
public Context ApplicationContext
{
get { return context.ApplicationContext; }
}
public BeaconsWorker(Context context, string[] guids, int scanTime)
{
...
this.context = context;
rangeNotifier = new RangeNotifier();
beaconManager = BeaconManager.GetInstanceForApplication(context);
beaconManager.SetForegroundBetweenScanPeriod(1000);
beaconManager.SetForegroundScanPeriod(1000);
beaconManager.SetBackgroundMode(false);
var beaconParser = new BeaconParser();
beaconParser.SetBeaconLayout("...");
beaconManager.BeaconParsers.Add(beaconParser);
rangeNotifier.DidRangeBeaconsInRegionComplete += OnBeaconsRanging;
beaconManager.SetRangeNotifier(rangeNotifier);
}
public bool BindService(Intent intent, IServiceConnection serviceConnection, [GeneratedEnum] Bind flags)
{
return context.BindService(intent, serviceConnection, flags);
}
public void OnBeaconServiceConnect()
{
foreach (var region in beaconManager.RangedRegions.ToList())
beaconManager.StopRangingBeaconsInRegion(region);
for (int i = 0; i < guids.Length; i++)
{
var uuid = Identifier.Parse(guids[i]);
var region = new Region("R" + i, uuid, null, null);
beaconManager.StartRangingBeaconsInRegion(region);
}
}
public void UnbindService(IServiceConnection serviceConnection)
{
context.UnbindService(serviceConnection);
}
public async Task<BeaconEntry> GetLocationResult()
{
beaconManager.Bind(this);
await Task.Delay(scanTime * 1000);
beaconManager.Unbind(this);
...
return result;
}
private void OnBeaconsRanging(object sender, RangeEventArgs e)
{
lock (beacons)
foreach (var item in e.Beacons)
{
var beacon = new BeaconEntry()
{
BeaconGUID = item.Id1.ToString(),
BeaconMajor = Int32.Parse(item.Id2.ToString()),
BeaconMinor = Int32.Parse(item.Id3.ToString())
};
beacons.Add(beacon);
}
}
private class RangeEventArgs : EventArgs
{
public Region Region { get; set; }
public ICollection<Beacon> Beacons { get; set; }
}
private class RangeNotifier : Java.Lang.Object, IRangeNotifier
{
public event EventHandler<RangeEventArgs> DidRangeBeaconsInRegionComplete;
public void DidRangeBeaconsInRegion(ICollection<Beacon> beacons, Region region)
{
OnDidRangeBeaconsInRegion(beacons, region);
}
private void OnDidRangeBeaconsInRegion(ICollection<Beacon> beacons, Region region)
{
DidRangeBeaconsInRegionComplete?.Invoke(this, new RangeEventArgs { Beacons = beacons, Region = region });
}
}
It is possible to set up the Android Beacon Library to run in a separate process. You can read the basic configuration instructions here:
https://github.com/AltBeacon/android-beacon-library/pull/479
This multi-process setup was successfully tested with library version 2.11. Version 2.12, however, included significant rework to support Android 8, and I have not tested multi-process support with versions 2.12+, so use those versions with caution. Your best bet is to use version 2.11.
The instructions linked above are written for Android apps built using standard Java or Kotlin development toolset with Android Studio or Gradle. Clearly modifications are needed to make this work with Xamarin. Since I am not a Xamarin expert, it's hard for me to help more.

Application cycles between didEnterRegion() and didExitRegion() even when the device stays stationary near the beacon

I am using AltBeacon Android Library (I reproduced issue with v2.9.2; and also with v2.11) for integrating with iBeacon devices provided by Onyx and kontact.io.
The library seems to work very well, but I seem to have an issue with it for which I could not find an acceptable solution.
Here are some more details about how I use AltBeacon Library and about the issue:
Device is stationary near the beacon
Bluetooth on
Application runs in foreground
The BeaconManager is configured to scan in foreground mode with the following settings:
BeaconManager.setRegionExitPeriod(30000L);
beaconManager.setBackgroundBetweenScanPeriod(120000L);
beaconManager.setForegroundScanPeriod(5000L);
beaconManager.setForegroundBetweenScanPeriod(10000L);
beaconManager.getBeaconParsers().add(
new BeaconParser().setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"));
Application sets the BeaconManager in foreground mode
beaconManager.setBackgroundMode(false);
Application bounds to the BeaconManager
beaconManager.bind(…)
When onBeaconServiceConnect() is triggered, the application starts monitoring beacons in specific regions (the list of beacons I want to monitor is known, static; I use a list of regions, one different region for each beacon I want to monitor)
beaconManager.startMonitoringBeaconsInRegion(region);
When device enters beacon region (didEnterRegion() is called) application starts ranging for entered region
beaconManager.startRangingBeaconsInRegion(region);
Beacon is detected (didRangeBeaconsInRegion() is called for corresponding beacon)
Application switched beacon scanning to background mode:
beaconManager.setBackgroundMode(true);
After a few minutes, the didExitRegion() is called even if the device and the beacon were not moved and the application remained in the same state.
I have found two Stackoverflow issues which describe the same issue:
AltBeacon unstable for OnyxBeacons, cycling through didEnterRegion and didExitRegion repeatedly
http://stackoverflow.com/questions/40835671/altbeacon-reference-app-and-multiple-exit-entry-calls
The workaround that I currently use is the one suggested in the Stackoverflow issues:
I have updated beacon Advertising Frequency value from 1000 ms to 100 ms.
Once the frequency is increased, everything seems to work fine, but
the solution is not acceptable because the battery life of the beacon is
drastically impaired.
All the beacon scanning is performed in background (i.e. no Activity is used):
import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.RemoteException;
import android.support.annotation.NonNull;
import org.altbeacon.beacon.Beacon;
import org.altbeacon.beacon.BeaconConsumer;
import org.altbeacon.beacon.BeaconManager;
import org.altbeacon.beacon.BeaconParser;
import org.altbeacon.beacon.Identifier;
import org.altbeacon.beacon.MonitorNotifier;
import org.altbeacon.beacon.RangeNotifier;
import org.altbeacon.beacon.Region;
import org.altbeacon.beacon.powersave.BackgroundPowerSaver;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class BeaconDataProvider implements BeaconConsumer, RangeNotifier, MonitorNotifier {
private final Logger LOGGER = LogFactory.get(this);
private final Context applicationContext;
private final BeaconIdentifierFactory beaconIdentifierFactory;
private final BeaconScanningListener beaconScanningListener;
private BeaconManager beaconManager;
private Collection<Region> targetedRegions;
/**
* This field is used for improving battery consumption. Do not remove it.
*/
#SuppressWarnings({"unused", "FieldCanBeLocal"})
private BackgroundPowerSaver backgroundPowerSaver;
public BeaconDataProvider(Context applicationContext, BeaconIdentifierFactory beaconIdentifierFactory,
BeaconScanningListener beaconScanningListener) {
LOGGER.v("BeaconDataProvider - new instance created.");
this.applicationContext = applicationContext;
this.beaconIdentifierFactory = beaconIdentifierFactory;
this.beaconScanningListener = beaconScanningListener;
beaconManager = BeaconManager.getInstanceForApplication(applicationContext);
LOGGER.v("BeaconManager hashCode=%s", beaconManager.hashCode());
BeaconManager.setRegionExitPeriod(30000L);
beaconManager.setBackgroundBetweenScanPeriod(120000L);
beaconManager.setForegroundScanPeriod(5000L);
beaconManager.setForegroundBetweenScanPeriod(10000L);
beaconManager.getBeaconParsers().add(
new BeaconParser().setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"));
backgroundPowerSaver = new BackgroundPowerSaver(applicationContext);
}
public void setBackgroundMode() {
LOGGER.i("setBackgroundMode()");
beaconManager.setBackgroundMode(true);
}
public void setForegroundMode() {
LOGGER.i("setForegroundMode()");
beaconManager.setBackgroundMode(false);
}
public boolean checkAvailability() {
return android.os.Build.VERSION.SDK_INT >= 18 && applicationContext.getPackageManager()
.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
}
public boolean isBluetoothEnabled() {
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
boolean result = mBluetoothAdapter != null && mBluetoothAdapter.isEnabled();
LOGGER.i("isBluetoothEnabled() -> %s", result);
return result;
}
public boolean isLocationPermissionGranted(Context context) {
return (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& context.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION)
== PackageManager.PERMISSION_GRANTED);
}
public void startScanning(Collection<BeaconIdentifier> targetedBeacons) {
LOGGER.i("startScanning()");
if (!beaconManager.isBound(this)) {
this.targetedRegions = getRegionsForTargetedBeacons(targetedBeacons);
beaconManager.bind(this);
}
else {
LOGGER.i("Scanning already started.");
}
}
#NonNull
private List<Region> getRegionsForTargetedBeacons(Collection<BeaconIdentifier> beaconIdentifiers) {
List<Region> regions = new ArrayList<>();
for (BeaconIdentifier beaconIdentifier : beaconIdentifiers) {
try {
Region region = new Region(beaconIdentifier.getRegionId(), Identifier.parse(beaconIdentifier.getUuid()),
Identifier.parse(String.valueOf(beaconIdentifier.getMajor())),
Identifier.parse(String.valueOf(beaconIdentifier.getMinor())));
regions.add(region);
}
catch (Exception e) {
LOGGER.e("Caught exception.", e);
LOGGER.w("Failed to create region for beaconIdentifier=%s", beaconIdentifier.getCallParamRepresentation());
}
}
return regions;
}
public void stopScanning() {
LOGGER.i("stopScanning()");
if (beaconManager.isBound(this)) {
for (Region region : targetedRegions) {
try {
beaconManager.stopMonitoringBeaconsInRegion(region);
}
catch (RemoteException e) {
LOGGER.e("Caught exception", e);
}
}
beaconManager.unbind(this);
}
}
#Override
public void didEnterRegion(Region region) {
LOGGER.v("didEnterRegion(region=%s)", region);
beaconScanningListener.onEnterRegion(region.getUniqueId());
try {
beaconManager.startRangingBeaconsInRegion(region);
}
catch (RemoteException e) {
LOGGER.e("Caught Exception", e);
}
}
#Override
public void didExitRegion(Region region) {
LOGGER.v("didExitRegion(region=%s)", region);
beaconScanningListener.onExitRegion(region.getUniqueId());
try {
beaconManager.stopRangingBeaconsInRegion(region);
}
catch (RemoteException e) {
LOGGER.e("Error", e);
}
}
#Override
public void didDetermineStateForRegion(int state, Region region) {
LOGGER.v("didDetermineStateForRegion(state=%s, region=%s)", state, region);
}
#Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
LOGGER.v("didRangeBeaconsInRegion(size=%s, region=%s, regionUniqueId=%s)", beacons.size(), region,
region.getUniqueId());
if (beacons.size() > 0) {
beaconScanningListener.onBeaconsInRange(beaconIdentifierFactory.from(beacons, region.getUniqueId()));
}
}
#Override
public void onBeaconServiceConnect() {
LOGGER.v("onBeaconServiceConnect()");
beaconManager.addRangeNotifier(this);
beaconManager.addMonitorNotifier(this);
for (Region region : targetedRegions) {
try {
beaconManager.startMonitoringBeaconsInRegion(region);
}
catch (RemoteException e) {
LOGGER.e("Caught exception", e);
}
}
}
#Override
public Context getApplicationContext() {
return applicationContext;
}
#Override
public void unbindService(ServiceConnection serviceConnection) {
LOGGER.v("unbindService()");
applicationContext.unbindService(serviceConnection);
}
#Override
public boolean bindService(Intent intent, ServiceConnection serviceConnection, int i) {
LOGGER.v("bindService()");
return applicationContext.bindService(intent, serviceConnection, i);
}
}
public class BeaconIdentifier {
private final String uuid;
private final int major;
private final int minor;
private String regionId;
public BeaconIdentifier(String uuid, int major, int minor) {
this.uuid = uuid;
this.major = major;
this.minor = minor;
}
public int getMinor() {
return minor;
}
public int getMajor() {
return major;
}
public String getUuid() {
return uuid;
}
public String getCallParamRepresentation() {
return (uuid + "_" + major + "_" + minor).toUpperCase();
}
public String getRegionId() {
return regionId;
}
public void setRegionId(String regionId) {
this.regionId = regionId;
}
#Override
public boolean equals(Object o) {
if (o != null) {
if (o instanceof BeaconIdentifier) {
BeaconIdentifier other = (BeaconIdentifier) o;
return this == other || (this.uuid.equalsIgnoreCase(other.uuid)
&& this.major == other.major && this.minor == other.minor);
}
else {
return false;
}
}
else {
return false;
}
}
#Override
public int hashCode() {
int result = 17;
result = 31 * result + (uuid != null ? uuid.toUpperCase().hashCode() : 0);
result = 31 * result + major;
result = 31 * result + minor;
return result;
}
#Override
public String toString() {
return "BeaconIdentifier{" +
"uuid='" + uuid + '\'' +
", major=" + major +
", minor=" + minor +
", regionId='" + regionId + '\'' +
'}';
}
}
The BeaconDataProvider is used as a single instance per application; It is instantiated by Dagger 2 when the Android Application is created. It has #ApplicationScope lifecycle.
The beacon scanning is first started`in foreground mode from an Android IntentService:
beaconDataProvider.setForegroundMode();
beaconDataProvider.startScanning(targetedBeacons);
Once the device enters the region and the beacon is detected, beacon scanning is switched to background mode:
beaconDataProvider.setBackgroundMode();
At first I thought there was something wrong with the Onyx Beacons I was using, but I could reproduce the same issue with the Kontact IO Beacons.
Do you have any suggestions?
Am I miss-using the AltBeacon Android Library?
Thanks,
Alin
The fundamental cause of a call to didExitRegion() is the fact that no BLE beacon advertisement packets matching the region were received by the Android bluetooth stack in the previous 10 seconds. (Note: This value is configurable with BeaconManager.setRegionExitPeriod(...).)
There are several things that could be causing these spurious didExitRegion() calls:
A beacon is not advertising frequently enough.
A beacon is advertising with a very low radio signal.
There is too much radio noise in the vicinity for reliable detections.
The receiving device has a poor bluetooth antenna design causing weaker signals to not get detected.
The receiving device is too far away to reliably detect the beacon.
The foregroundScanPeriod or backgroundScanPeriod is set too short to get a guaranteed detection
Given the setup you've described, I suspect that when you have the beacon transmitting at 1Hz, some combination of 1-4 is causing the problem. You will have to experiment with each of these variables to see if you can isolate the problem to one predominant issue. But again, more than one may be at play at the same time.
Understand that even under good conditions only 80-90 percent of beacons packets transmitted over the air are received by a typical Android device. Because of this, if you have a setup where only 1-5 beacon packets are typically received in a 10 second period, you'll still sometimes get exit events if you get unlucky and a few packets in a row get corrupted by radio noise. There is no way to guarantee this won't happen. You can just make it statistically more unlikely by setting up your system so under nominal conditions it receives as many packets as possible in a 10 second period, so this becomes more unlikely.
Increasing the advertising rate is the easiest way to fix this, because it gives you more statistical chances of getting packets detected in any 10 second period. But as you have seen, there is a tradeoff in terms of battery life.
If you want do preserve battery life but don't care about the time it takes to get a didExitRegion callback, then you may want to modify BeaconManager.setRegionExitPeriod(...) to 30,000 milliseconds or more until the problem goes away.
The above discussion is specific to the configuration of the Android Beacon Library, the same theoretical ideas apply to any beacon detection framework including iOS Core Location. You sometimes see spurious exit events with that framework as well.
I think the problem is here:
beaconManager.setForegroundScanPeriod(5000L);
beaconManager.setForegroundBetweenScanPeriod(10000L);
You should generally set the scanPeriod to 5100 ms or more, because beacons that advertise have a slight chance of being missed if their transmission is always on the boundary of when you start and stop scanning.
So try:
beaconManager.setForegroundScanPeriod(5100L);
beaconManager.setForegroundBetweenScanPeriod(10000L);
Hope it helps. Let me know if works.
As a workaround to this issue, I have implemented some extra logic to consider a didExitRegion() event only if the corresponding didEnterRegion() is not called in a certain time interval (5 minutes in my case, but this can be adjusted).

AltBeacon ranging never returns more than 1 beacon

I'm working with the AltBeacon library (2.5.1) to detect beacons.
I setup ranging with an "universal" Region to be able to detect any beacon in range, then do my stuff with it.
The issue is that when I have several beacons in range, the didRangeBeaconsInRegion callback always provides me a Collection of only 1 beacon at a time and this beacon is a random one among all the present beacons... Why can't I get all the beacons in range in my Collection ?
All of this is made from within a Service, I did clean all the other stuff to keep only the relevant parts of the code below -> Hopefully I am doing something wrong here ?
public class MonitorService extends Service implements BeaconConsumer
{
private BeaconManager beaconManager;
#Override
public void onCreate()
{
super.onCreate();
beaconManager = BeaconManager.getInstanceForApplication(this);
beaconManager.getBeaconParsers().add(new BeaconParser().
setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"));
beaconManager.setForegroundScanPeriod(5000l);
beaconManager.setBackgroundScanPeriod(5000l);
beaconManager.setForegroundBetweenScanPeriod(1100l);
beaconManager.setBackgroundBetweenScanPeriod(1100l);
setupBeaconManager();
}
private void setupBeaconManager()
{
if (!beaconManager.isBound(this))
beaconManager.bind(this);
}
private void unsetBeaconManager()
{
if (beaconManager.isBound(this))
{
beaconManager.unbind(this);
try
{
beaconManager.stopRangingBeaconsInRegion(new Region("apr", null, null, null));
}
catch (RemoteException e)
{
Log.i(TAG, "RemoteException = "+e.toString());
}
}
}
#Override
public void onBeaconServiceConnect()
{
beaconManager.setRangeNotifier(new RangeNotifier() {
#Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region)
{
Log.i(TAG,"didRangeBeaconsInRegion, number of beacons detected = "+beacons.size());
// HERE IT IS : the size is Always 1, but the beacon (UUID etc. can be different)
}
});
try
{
beaconManager.startRangingBeaconsInRegion(new Region("apr", null, null, null));
}
catch (RemoteException e)
{
Log.i(TAG, "RemoteException = "+e.toString());
}
}
#Override
public void onDestroy()
{
unsetBeaconManager();
super.onDestroy();
}
}
I'm working on Android 5.1.1 with a Nexus 6 (but a Wiko cheap phone gives the same results). The beacons are setup to advertise every 600ms... But even with 100ms it also gives the exact same results...
The code looks OK. A couple of thoughts:
Try using an off the shelf beacon scanner app based on the same library like Locate. Does it detect all of your beacons simultaneously? If not, something may be wrong with the beacons or their configuration.
Do each of your beacons have unique identifiers? The library by default only detects multiple beacons if they have unique identifiers.

Wifi P2P service discovery works intermittently

Wifi P2P service discovery is not behaving as expected. I am seeing intermittent issues where the DNSSD listeners are not called always and hence I have no clue of nearby devices running the same app. I am using the following two APIs - one to register a service to be discovered by other devices and the other to discover the nearby services running on other devices. Any idea if I am doing anything wrong here or is there some specific sequence of other android API calls that need to be made before I call these APIs to ensure that the listeners are always called whenever there is a new service registered or even if a service is registered before we call the API to discover the local services.
API to register a local service:
private void registerService() {
Map<String, String> values = new HashMap<String, String>();
values.put("name", "Steve");
values.put("port", "8080");
WifiP2pServiceInfo srvcInfo = WifiP2pDnsSdServiceInfo.newInstance(mMyDevice.deviceName, "_http._tcp", values);
manager.addLocalService(channel, srvcInfo, new WifiP2pManager.ActionListener() {
#Override
public void onSuccess() {
Toast.makeText(WiFiDirectActivity.this, "Local service added successfully",
Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(int reasonCode) {
Toast.makeText(WiFiDirectActivity.this, "Local service addition failed : " + reasonCode,
Toast.LENGTH_SHORT).show();
}
});
}
API to discover local services:
public void discoverService() {
manager.clearServiceRequests(channel, null);
DnsSdTxtRecordListener txtListener = new DnsSdTxtRecordListener() {
#Override
/* Callback includes:
* fullDomain: full domain name: e.g "printer._ipp._tcp.local."
* record: TXT record data as a map of key/value pairs.
* device: The device running the advertised service.
*/
public void onDnsSdTxtRecordAvailable(String fullDomain, Map record, WifiP2pDevice device) {
Log.d(TAG, "DnsSdTxtRecord available -" + record.toString());
}
};
DnsSdServiceResponseListener servListener = new DnsSdServiceResponseListener() {
#Override
public void onDnsSdServiceAvailable(String instanceName, String registrationType, WifiP2pDevice resourceType) {
Log.d(TAG, "onBonjourServiceAvailable " + instanceName);
}
};
manager.setDnsSdResponseListeners(channel, servListener, txtListener);
WifiP2pDnsSdServiceRequest serviceRequest = WifiP2pDnsSdServiceRequest.newInstance();
manager.addServiceRequest(channel, serviceRequest, new ActionListener() {
#Override
public void onSuccess() {
// Success!
Log.d(TAG, "addServiceRequest success");
}
#Override
public void onFailure(int code) {
// Command failed. Check for P2P_UNSUPPORTED, ERROR, or BUSY
Log.d(TAG, "addServiceRequest failure with code " + code);
}
});
manager.discoverServices(channel, new ActionListener() {
#Override
public void onSuccess() {
// Success!
Log.d(TAG, "discoverServices success");
}
#Override
public void onFailure(int code) {
// Command failed. Check for P2P_UNSUPPORTED, ERROR, or BUSY
if (code == WifiP2pManager.P2P_UNSUPPORTED) {
Log.d(TAG, "P2P isn't supported on this device.");
} else {
Log.d(TAG, "discoverServices failure");
}
}
});
}
Note: manager & channel are initialized as
WifiP2pManager manager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
Channel channel = manager.initialize(this, getMainLooper(), null);
WifiP2p (in general):
Some time ago I was developing an application with a pretty complex network connectivity system based on WifiP2p with Service Broadcasting/Discovery. And based on that experience I already wrote few posts here on SO about how difficult, wearing and problematic that is. Here are two of them (they are quite full of the inside knowledge I acquired about WifiP2p with Service Discovery, and WifiP2p itself):
Why is discovering peers for Android WifiDirect so unreliable
Wi-fi P2P. Inform all peers available of some event
I would advise you to read both of my answers (even though they are focused a bit more on the WifiP2p itself). They should give you some perspective on the things you should be looking for when working with the WifiP2p Service Discovery.
I can easily say that if you want to build an efficient, relatively reliable and robust WifiP2p connection system (especially with Service Discovery), you will have to work your ass off.
WifiP2p Service Discovery:
To better answer your exact question, I will tell you what I did (different from you) to make my Service Discovery work pretty reliably.
1. Broadcasting Service:
First of all: before registering your Service (with addLocalService method) you should use the WifiP2pManager's clearLocalServices method. And it is important, that you should only call addLocalService if the listener passed in the clearLocalServices returned with the onSuccess callback.
Although this sets up the broadcasting pretty nicely, I found that other nodes were not always able to detect the broadcasted service (especially when those nodes weren't already actively detecting services at the moment of registering your local Service - but they "joined" later). I couldn't find a way to fix this issue 100% reliably. And believe me I was trying probably everything WifiP2p-related. And no, the clearLocalServices-addLocalService sequence wasn't really giving satisfying results. Or more so: doing something different was working much better. What I decided to do, was after I successfully added local service (onSuccess callback from addLocalService), I started a Thread that would periodically call WifiP2pManager's method discoverPeers. That seemed to be forcing to rebroadcast all the service information.
So... basically the base of your broadcasting code should look more-less like this (bare in mind that every single piece of code I will post here is stripped-off of all "checks" if the network connectivity system is in the right state, you should design them yourself to fit your solution the best):
public void startBroadcastingService(){
mWifiP2pManager.clearLocalServices(mWifiP2pChannel, new WifiP2pManager.ActionListener() {
#Override
public void onSuccess() {
mWifiP2pManager.addLocalService(mWifiP2pChannel, mWifiP2pServiceInfo,
new WifiP2pManager.ActionListener() {
#Override
public void onSuccess() {
// service broadcasting started
mServiceBroadcastingHandler
.postDelayed(mServiceBroadcastingRunnable,
SERVICE_BROADCASTING_INTERVAL);
}
#Override
public void onFailure(int error) {
// react to failure of adding the local service
}
});
}
#Override
public void onFailure(int error) {
// react to failure of clearing the local services
}
});
}
where the mServiceBroadcastingRunnable should be:
private Runnable mServiceBroadcastingRunnable = new Runnable() {
#Override
public void run() {
mWifiP2pManager.discoverPeers(mWifiP2pChannel, new WifiP2pManager.ActionListener() {
#Override
public void onSuccess() {
}
#Override
public void onFailure(int error) {
}
});
mServiceBroadcastingHandler
.postDelayed(mServiceBroadcastingRunnable, SERVICE_BROADCASTING_INTERVAL);
}
};
2. Discovering Service:
For the discovering of your service I used similar approach. Both with the setting up the discovering, and with trying to force "rediscovery" of services.
Setting up was performed with the sequence of the following three WifiP2pManager's methods:
removeServiceRequest, addServiceRequest, discoverServices
They were called in this exact order and a particular method (second or the third one to be exact) has been called only after the previous one had "returned" with the onSuccess callback.
The rediscovery of services was being performed with the intuitive method (just by repeating the mentioned sequence: removeServiceRequest -> addServiceRequest -> discoverServices).
The base of my code looked more-less like this (to start Service Discovery I would first call prepareServiceDiscovery() and then startServiceDiscovery()):
public void prepareServiceDiscovery() {
mWifiP2pManager.setDnsSdResponseListeners(mWifiP2pChannel,
new WifiP2pManager.DnsSdServiceResponseListener() {
#Override
public void onDnsSdServiceAvailable(String instanceName,
String registrationType, WifiP2pDevice srcDevice) {
// do all the things you need to do with detected service
}
}, new WifiP2pManager.DnsSdTxtRecordListener() {
#Override
public void onDnsSdTxtRecordAvailable(
String fullDomainName, Map<String, String> record,
WifiP2pDevice device) {
// do all the things you need to do with detailed information about detected service
}
});
mWifiP2pServiceRequest = WifiP2pDnsSdServiceRequest.newInstance();
}
private void startServiceDiscovery() {
mWifiP2pManager.removeServiceRequest(mWifiP2pChannel, mWifiP2pServiceRequest,
new WifiP2pManager.ActionListener() {
#Override
public void onSuccess() {
mWifiP2pManager.addServiceRequest(mWifiP2pChannel, mWifiP2pServiceRequest,
new WifiP2pManager.ActionListener() {
#Override
public void onSuccess() {
mWifiP2pManager.discoverServices(mWifiP2pChannel,
new WifiP2pManager.ActionListener() {
#Override
public void onSuccess() {
//service discovery started
mServiceDiscoveringHandler.postDelayed(
mServiceDiscoveringRunnable,
SERVICE_DISCOVERING_INTERVAL);
}
#Override
public void onFailure(int error) {
// react to failure of starting service discovery
}
});
}
#Override
public void onFailure(int error) {
// react to failure of adding service request
}
});
}
#Override
public void onFailure(int reason) {
// react to failure of removing service request
}
});
}
the mServiceDiscoveringRunnable was just:
private Runnable mServiceDiscoveringRunnable = new Runnable() {
#Override
public void run() {
startServiceDiscovery();
}
};
All this made my system work quite well. It wasn't perfect yet, but with the lack of documentation on this subject I think I couldn't do much more to improve it.
If you test this approach, be sure to tell me how it works for you (or if it works for you ;) ).
if the problem is the detection of the service i believe that crearing group is the best way to make the device and service detectable but the if created group in the all devices then you cannot connect in direct.
but as wifi network.
i do it every day and it works.

Categories

Resources