DSS
DSS

Reputation: 7259

Beacons didEnterRegion and didExitRegion repeatedly called

I am trying to notify the user when the user enters a region defined and also when the user exits the region. This is what i have done so far. I used to use the rangeNotifier method (didBeaconsEnterRange) in order to notify a user when he/she enters a region, but then this method would get called every 1 second, so i moved the notification logic to the monitorNotifier class (didEnterRegion) method.

My application class extends BootStrapNotifier, and i am setting the scanPeriod to 2000s instead of the default 1100l seconds, because i seem to receive exit and entry notifications way too quick, even when the beacon is in range. Earlier i even increased the timeout period from 10000ms to 20000ms that would fire the exit when a beacon doesnt fire signals within the timeout period.

Code snippet for myapplication class

BeaconManager beaconManager = org.altbeacon.beacon.BeaconManager.getInstanceForApplication(this);
    beaconManager.getBeaconParsers().add(new BeaconParser().setBeaconLayout(LAYOUT_HERE));
    beaconManager.setForegroundScanPeriod(2000l);
    beaconManager.setBackgroundBetweenScanPeriod(1100l);
    //beaconManager.setBackgroundScanPeriod(5000l);
    beaconManager.setDebug(true);

    Log.d(TAG, "setting up background monitoring for beacons and power saving");
    // wake up the app when a beacon is seen
    mRegion = new Region("myRangingUniqueId",
            null, null, null);
    setRegionAgain();

SetRegionAgain method

if(SharedPrefs.getString(SharedPrefs.iBEACON_ID, "").trim().length() > 0) {
        if(SharedPrefs.getString(SharedPrefs.iBEACON_ID, "").trim().length() > 0 ) {
            try {
                mRegion = new Region("myRangingUniqueId",
                        Identifier.parse(SharedPrefs.getString(SharedPrefs.iBEACON_ID, "")), 
                        null, null);
            }catch(IllegalArgumentException e) {
                e.printStackTrace();
                mRegion = new Region("myRangingUniqueId",
                        null, null, null);
            }
        }
    }

    regionBootstrap = new RegionBootstrap(this, mRegion);

    // simply constructing this class and holding a reference to it in your custom Application
    // class will automatically cause the BeaconLibrary to save battery whenever the application
    // is not visible.  This reduces bluetooth power usage by about 60%
    backgroundPowerSaver = new BackgroundPowerSaver(this);

I have a background service that does the notification work, so it implements the BeaconConsumer interface. Code snippet below:

OnStart method:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    flagToCheckExit = false;
    mHandler = new Handler();
    beaconManager.bind(this);
    if (beaconManager.isBound(this)) beaconManager.setBackgroundMode(false);
    return Service.START_STICKY;
}

onDestroy method:

@Override
public void onDestroy() {
    super.onDestroy();
    try {
        beaconManager.stopMonitoringBeaconsInRegion(CommonUtilities.getRegion());
// this region is the one that i use to monitor (singleton types)
    } catch (RemoteException e) {
        e.printStackTrace();
    }

    beaconManager.unbind(this);
}

onServiceConnected method:

@Override
public void onBeaconServiceConnect() {
    beaconManager.setMonitorNotifier(new MonitorNotifier() {

        @Override
        public void didExitRegion(final Region region) {
            LogManager.d(TAG, "didExitRegion %s", region);

            if(SharedPrefs.getString(SharedPrefs.USER_ID, "").trim().length() > 0) {
                flagToCheckExit = true;
// i use this flag to prevent random entry and exit notifications
                mHandler.postDelayed(mRunnable, 20000);
// waiting for 20seconds before firing an exit notification, since an entry notification might get fired immediately after the exit
            }
        }

        @Override
        public void didEnterRegion(Region region) {
            Log.e(TAG,"region id1 >>> " + ((region.getId1() == null) ? "null" : region.getId1().toUuidString()));

            LogManager.d(TAG, "didEnterRegion %s ",region);

            if(!flagToCheckExit) {
                if(SharedPrefs.getString(SharedPrefs.USER_ID, "").trim().length() > 0) {
                    if(region.getId1() != null && 
                            region.getId1().toUuidString().equalsIgnoreCase(SharedPrefs.getString(SharedPrefs.iBEACON_ID, ""))) {
                        if(!SharedPrefs.getBoolean(SharedPrefs.IS_ENTRY_LOG_CALLED, false)) {
                            String entryRange = getAppContext().getString(R.string.entered_beacon_region);
                    CommonUtilities.sendNotification(MonitoringAltBeaconService.this,entryRange,1);
                        }
                    }
                }
            }else {
                // invalidate the handler
                // stop all operations of the handler
                // we do this to prevent an exit getting called since entry has been called immediately.
                mHandler.removeCallbacks(mRunnable);
            }
        }

        @Override
        public void didDetermineStateForRegion(int state, Region region) {
            LogManager.d(TAG, "didDetermineStateForRegion %s ",region);
        }
    });

    startMonitoring();
}

startMonitoring method:

private void startMonitoring() {
    try {
        if(SharedPrefs.getString(SharedPrefs.iBEACON_ID, "").trim().length() > 0 ) {
            try {
                beaconManager.startMonitoringBeaconsInRegion(CommonUtilities.getRegion());
            }catch(IllegalArgumentException e) {
                e.printStackTrace();

                beaconManager.startMonitoringBeaconsInRegion(CommonUtilities.getRegion());
            }
        }
    } catch (RemoteException e) {   }
}

the runnable thread:

Runnable mRunnable = new Runnable() {
    @Override
    public void run() {
        SharedPrefs.putBoolean(SharedPrefs.IS_ENTRY_LOG_CALLED, false);
        SharedPrefs.putBoolean(SharedPrefs.IS_EXIT_LOG_CALLED, true);

        String exitedRange = getAppContext().getString(R.string.exited_beacon_region);
        CommonUtilities.sendNotification(MonitoringAltBeaconService.this,exitedRange,2);
        LogManager.d(TAG, "exit called");
        flagToCheckExit = false;
    }
};

There is a strange behavior with this, there are multiple entry and exit logs that i get even when the beacon device is in range, i get the exit. I tried bypassing the exit notification but it seems to fail with the logic (patch) above.

Log:

03-19 18:00:25.866: D/MonitoringAltBeaconService(22795): didDetermineStateForRegion id1: cbb7c628-a321-4cf6-934d-37dbfa335735 id2: null id3: null 
03-19 18:00:25.867: D/MonitoringAltBeaconService(22795): didExitRegion id1: cbb7c628-a321-4cf6-934d-37dbfa335735 id2: null id3: null
03-19 18:00:26.470: D/MonitoringAltBeaconService(22795): didDetermineStateForRegion id1: cbb7c628-a321-4cf6-934d-37dbfa335735 id2: null id3: null 

03-19 18:00:26.477: D/MonitoringAltBeaconService(22795): didEnterRegion id1: cbb7c628-a321-4cf6-934d-37dbfa335735 id2: null id3: null 

03-19 18:00:48.076: D/MonitoringAltBeaconService(22795): didDetermineStateForRegion id1: cbb7c628-a321-4cf6-934d-37dbfa335735 id2: null id3: null 
03-19 18:00:48.076: D/MonitoringAltBeaconService(22795): didExitRegion id1: cbb7c628-a321-4cf6-934d-37dbfa335735 id2: null id3: null

03-19 18:00:51.275: D/MonitoringAltBeaconService(22795): didDetermineStateForRegion id1: cbb7c628-a321-4cf6-934d-37dbfa335735 id2: null id3: null 
03-19 18:00:51.282: D/MonitoringAltBeaconService(22795): didEnterRegion id1: cbb7c628-a321-4cf6-934d-37dbfa335735 id2: null id3: null 
03-19 18:01:10.269: D/MonitoringAltBeaconService(22795): didDetermineStateForRegion id1: cbb7c628-a321-4cf6-934d-37dbfa335735 id2: null id3: null
03-19 18:01:10.269: D/MonitoringAltBeaconService(22795): didExitRegion id1: cbb7c628-a321-4cf6-934d-37dbfa335735 id2: null id3: null
03-19 18:01:15.876: D/MonitoringAltBeaconService(22795): didDetermineStateForRegion id1: cbb7c628-a321-4cf6-934d-37dbfa335735 id2: null id3: null 
03-19 18:01:15.883: D/MonitoringAltBeaconService(22795): didEnterRegion id1: cbb7c628-a321-4cf6-934d-37dbfa335735 id2: null id3: null 

I am using AprilBeacon. The app i am working on is held up because the beacons dont generate notifications correctly. Please assist.

EDIT:

the device i use if Motorolla g2, this scenario happens in both the kitkat as well as lollipop versions. And Wifi is on, i prefer it being on, since there are webservice calls involved, when an entry exit is determined. Realtime , it would not be encouraged to ask users to switch their wifi off :(..

The library i use is , android-beacon-library the one here, and i am afraid i do not know the frequency at which the april beacon sends out signals.

EDIT 2:

Log 1 can be found here

Log 2 can be found here

EDIT 3

log when wifi is switched off I noticed that i did get an exit even when the beacon was in range And i opened the locate app, and it showed no beacons are there (see screenshot). i removed the battery and put them back it got the beacon, and so did my app too. (but in real life, i am sure batteries will not be tampered with)

When not detected After removing the batteries

Upvotes: 0

Views: 3468

Answers (3)

davidgyoung
davidgyoung

Reputation: 64941

EDIT: After reviewing the logs I have submitted a different answer that better answers the question. Please see that answer.

I noticed from the two posted logs that the library is reporting that the app is in the background and using the Android L APIs. This causes them to use "Low Latency" detections:

03-26 15:47:21.647: D/CycledLeScannerForLollipop(28596): This is Android L. Doing a filtered scan for the background.

Low latency detections save power, but they get delayed by 5 seconds or more. This works fine for background operations, but it isn't intended for foreground operations, and should be used with the default scan intervals. You should never set a non-zero between scan interval when using these Andorid L APIs, otherwise you may get exits like you describe.

If the logs are misreporting that your app is in the background, then there may be something wrong with the setup of the BackgroundPowerSaver.

You might also try disabling the Android L APIs, and use the older 4.x scanning APIs to see if that works better for your use case. If you do this, I would also remove any special scan interval settings so not to make your configuration too complex:

beaconManager.setAndroidLScanningDisabled(true)

You will need to put that line in the onCreate of your application class before you do any other beacon processing.

Upvotes: 0

davidgyoung
davidgyoung

Reputation: 64941

The problem is a combination of an infrequently advertising beacon, and a custom background scan interval that is too short.

It appears that the beacon is only advertising once every 2 seconds because that is the closest I ever see two subsequent detection timestamps in the log. This is low transmission frequency is problematic, and exacerbated by the custom settings.

For proper operation of the library, the background scan period must be at least 5x the beacon transmission period. This is because not all bluetooth packets get received due to radio noise. Having an interval that is 5x the transmission period means that there are 5 chances to receive a packet on each background scan cycle, making it very unlikely the library will miss one.

The default library settings set a background scan period to 10 seconds, which should be enough to give a very high probability of detecting even a beacon transmitting only once every 2 seconds, and making incorrect region exits very, very rare.

Recommendations:

  1. Change the background scan period to be at least the default of 10 seconds. To save battery, the between scan period should probably be longer, too -- the default of 5 minutes is a reasonable choice unless you have a good reason to make it shorter. If you still get exit events, make the background scan period even longer.

  2. Use different beacons that advertise more frequently. Although you should be able to make the library work with these beacons, you won't get updates as frequently, and apps like Locate will periodically show dropouts in detections. If you are doing distance estimates in your app, you need beacons that transmit at 10Hz or more. If not doing distance estimates in your app, you should still use beacons that transmit at 1Hz or more, otherwise you will be more likely to have problems like this.

How do I know this is the problem?

As you can see from the log excerpts below, an incorrect region exit happens at 15:50:52, because it has been 10 seconds since the previous detection. Any 10 second period without detections will cause a region exit event the next time a scan cycle stops. So you must be very sure that a scan cycle is long enough to capture a beacon transmission. With the custom settings, missing a detection on three subsequent scan cycles will cause a region exit.

03-26 15:49:49.533: D/BeaconService(28596): beacon detected : id1: cbb7c628-a321-4cf6-934d-37dbfa335735 id2: 1 id3: 7
03-26 15:49:55.567: D/BeaconService(28596): beacon detected : id1: cbb7c628-a321-4cf6-934d-37dbfa335735 id2: 1 id3: 7
03-26 15:50:01.624: D/BeaconService(28596): beacon detected : id1: cbb7c628-a321-4cf6-934d-37dbfa335735 id2: 1 id3: 7
03-26 15:50:08.898: D/BeaconService(28596): beacon detected : id1: cbb7c628-a321-4cf6-934d-37dbfa335735 id2: 1 id3: 7
03-26 15:50:11.315: D/BeaconService(28596): beacon detected : id1: cbb7c628-a321-4cf6-934d-37dbfa335735 id2: 1 id3: 7
03-26 15:50:17.380: D/BeaconService(28596): beacon detected : id1: cbb7c628-a321-4cf6-934d-37dbfa335735 id2: 1 id3: 7
03-26 15:50:21.010: D/BeaconService(28596): beacon detected : id1: cbb7c628-a321-4cf6-934d-37dbfa335735 id2: 1 id3: 7
03-26 15:50:26.440: D/BeaconService(28596): beacon detected : id1: cbb7c628-a321-4cf6-934d-37dbfa335735 id2: 1 id3: 7
03-26 15:50:30.066: D/BeaconService(28596): beacon detected : id1: cbb7c628-a321-4cf6-934d-37dbfa335735 id2: 1 id3: 7
03-26 15:50:33.108: D/BeaconService(28596): beacon detected : id1: cbb7c628-a321-4cf6-934d-37dbfa335735 id2: 1 id3: 7
03-26 15:50:36.120: D/BeaconService(28596): beacon detected : id1: cbb7c628-a321-4cf6-934d-37dbfa335735 id2: 1 id3: 7
03-26 15:50:39.745: D/BeaconService(28596): beacon detected : id1: cbb7c628-a321-4cf6-934d-37dbfa335735 id2: 1 id3: 7
03-26 15:50:41.573: D/BeaconService(28596): beacon detected : id1: cbb7c628-a321-4cf6-934d-37dbfa335735 id2: 1 id3: 7

03-26 15:50:52.415: D/MonitorState(28596): We are newly outside the region because the lastSeenTime of 1427365241573 was 10841 seconds ago, and that is over the expiration duration of 10000
03-26 15:50:52.415: D/BeaconService(28596): found a monitor that expired: id1: cbb7c628-a321-4cf6-934d-37dbfa335735 id2: null id3: null
03-26 15:50:52.415: D/Callback(28596): attempting callback via intent: ComponentInfo{com.credencys.mycarline/org.altbeacon.beacon.BeaconIntentProcessor}

03-26 15:50:54.879: D/BeaconService(28596): beacon detected : id1: cbb7c628-a321-4cf6-934d-37dbfa335735 id2: 1 id3: 7
03-26 15:50:56.705: D/BeaconService(28596): beacon detected : id1: cbb7c628-a321-4cf6-934d-37dbfa335735 id2: 1 id3: 7
03-26 15:51:00.940: D/BeaconService(28596): beacon detected : id1: cbb7c628-a321-4cf6-934d-37dbfa335735 id2: 1 id3: 7
03-26 15:51:03.348: D/BeaconService(28596): beacon detected : id1: cbb7c628-a321-4cf6-934d-37dbfa335735 id2: 1 id3: 7
03-26 15:51:03.349: D/BeaconService(28596): looking for ranging region matches for this beacon
03-26 15:51:03.842: D/CycledLeScanner(28596): Waiting to stop scan cycle for another 1000 milliseconds
03-26 15:51:04.730: D/MonitoringAltBeaconService(28596): didExitRegion id1: cbb7c628-a321-4cf6-934d-37dbfa335735 id2: null id3: null

Upvotes: 2

Furkan Varol
Furkan Varol

Reputation: 252

Your configuration seems to valid but there is a little flaw. You should have set both foreground scan configs:

beaconManager.setForegroundScanPeriod(2000l);
beaconManager.setForegroundBetweenScanPeriod(1100l);
//Update default time with the new one
beaconManager.updateScanPeriods();

or/and background scan configs:

beaconManager.setBackgroundScanPeriod(2000l);
beaconManager.setBackgroundBetweenScanPeriod(1100l);
//Update default time with the new one
beaconManager.updateScanPeriods();

Also, do you know advertisement frequency of the beacon ? You have said that you have increased scan period to 20secs and nothing changed still frequency would be helpful for us to help you.

Btw, can you also share AltBeacon's logcat outputs? Also, what is your device and model ? And which Android version is it running ? And which AltBeacon library version are you using?

If your device is running Lollipop try with following line and please share results with us:

BeaconManager.setAndroidLScanningDisabled(true);

Also, try with turning of your WiFi if it is on because in some devices it interferes Bluetooth scan.

Btw, sorry this is more like a comment than an answer but it was to long for a comment as well :).

Upvotes: 2

Related Questions