Do Geofences remain active in android after a device reboot
Asked Answered
C

2

24

I'm writing an application that needs to use geofencing for when someone enters/exits multiple sites over the lifetime of the application being installed.

My implementation of geofencing (very similar to the second link below) is all working fine when I first install the application, both when moving in/out of the geofences and when using mock locations to simulate it, until a device is rebooted.

On reboot neither mock locations nor actually physically moving in and out of a geofence appears to trigger the event and fire the pending intent to my broadcast receiver.

I have looked at the following three links and have also read quite a bit of the doc's but I can't find a definitive answer to this anywhere that straight up says registered geofences persist or do not persist after a reboot.

These are the links I reviewed on stack overflow: Are Android geofences surviving a reboot?

Android Geofence eventually stop getting transition intents

Do Android Geofences remain active until removed/expired or only until my PendingIntent is launched

If anyone happens to know the answer to whether they stick around post reboot, or has a work around if they do not, it would be much appreciated! My last hope currently is to create a listener for BOOT_COMPLETED and re-register them on start up but id prefer to only do this if absolutely necessary.

Thanks a lot in advance!

Edit: Whilst I have not found a definitive (in writing) answer, I am pretty sure what Mr. TonyC posted is correct, and have opted for that solution. Thanks a lot TonyC!

In case anyone wants to see the solution I have, I listen for the boot complete action when a device is booted, and then re-register all the geofences I need.

This is in the manifest:

<!-- Listen for the device starting up -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

<receiver android:name="com.YOUR.PACKAGE.geofence.BootCompleteReceiver">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED"/>
    </intent-filter>
</receiver>

and then create a broadcast receiver for it which will re-register the geofences on boot:

package com.YOUR.PACKAGE.geofence;

import android.app.PendingIntent.CanceledException;
import android.content.Context;
import android.content.Intent;
import android.support.v4.content.WakefulBroadcastReceiver;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.location.Geofence;

public class BootCompleteReceiver extends WakefulBroadcastReceiver
{
    private static final String TAG = "BootCompleteReceiver";

    @Override
    public void onReceive(Context context, Intent intent)
    {
        //Do what you want/Register Geofences
    }
}

It's also worth noting, that if you are inside a geofence on boot, this usually will then trigger the geofence's pending intent once the geofence has been registered.

So if for instance, the geofence starts an app, then when you boot a device that happens to be in the geofence, it will also open the app once the boot complete broadcast receiver has registered the geofence, and location services has worked out where you are.

Hope that is some help to someone.

Culpa answered 19/11, 2013 at 19:5 Comment(2)
Do you really need WakefulBroadcastReceiver at boot? Wouldn't simple lBroadcastReceiver be enough? It would be quite unikely for device to go asleep just after booting...Kinslow
@Culpa do you have an example on how are you registering the Geofences, I'm having issues, since the Google Play service hasn't initialized yetSungod
F
15

In my experience the geofences do not survive reboot. I use a BOOT_COMPLETED receiver just as you suggest. It works fine.

Frog answered 21/11, 2013 at 12:41 Comment(1)
That's confirmed by the docs in "Re-register geofences only when required" section.Jeep
O
13

Geofences don't survive a reboot. There are other cases where you'll have to re-register geofences as well.

The Re-register geofences only when required documentation calls out several situations in addition to BOOT_COMPLETED where you need to re-register your geofences. It is unfortunately vague and incomplete.

Let's go point-by-point taking into account the new background execution limits imposed starting with Android O:

  • The device is rebooted

This one can be handled by adding the following to your intent-filter since these are exempted from the implicit broadcast ban:

<action android:name="android.intent.action.BOOT_COMPLETED" />

Then, make sure to add the following permission to your manifest:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

You may want to catch the newer LOCKED_BOOT_COMPLETED intent introduced in Android N:

<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />

But, if you do, you'll also need to mark your receiver with android:directBootAware=”true”. This introduces ramifications for your app. Namely that any file-based data you access must be done using device protected storage. The long and short of it is, if you don't need to be notified when the device is booted to the lock screen, don't use LOCKED_BOOT_COMPLETED.

  • The app is uninstalled and re-installed

Again, we're in luck here since you can use this explicit intent:

<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
  • The app's data is cleared

This is where I've no idea. There is an ACTION_PACKAGE_DATA_CLEARED that is exempt from the implicit broadcast ban, but it would only be fired if another package's data is cleared. I've tried this and can confirm you will not get called when your own app's data is cleared.

  • Google Play services data is cleared

This can be handled by adding the following to your receiver:

<intent-filter>
    <!-- Used to watch for Google Play Services data cleared -->
    <action android:name="android.intent.action.PACKAGE_DATA_CLEARED" />
    <data android:scheme="package" android:sspPrefix="com.google.android.gms"/>
</intent-filter>

and then adding the following code to your BroadcastReceiver's onReceive method:

String action = intent.getAction();
if (TextUtils.equals(Intent.ACTION_PACKAGE_DATA_CLEARED, action)) {
    Uri uri = intent.getData();
    if (uri.toString().equals("package:com.google.android.gms")) {
        // Code here to handle Google Play services data cleared
    }
}

This is Android's way of notifying you through the geofencing API that location services are no longer available and is signified by sending a GeofencingEvent with an error and status code of GEOFENCE_NOT_AVAILABLE.

However, simply following the vague advice of the geofencing documentation would lead you to believe you can re-register geofences at this point. This would be bad as location services is probably still disabled and doing so would result in more GEOFENCE_NOT_AVAILABLEs. What is needed is a hook to tell when location services has been toggled.

Up until Android O, registering a BroadcastReceiver for android.location.MODE_CHANGED_ACTION would give you this hook. On Android O and later, this implicit intent is banned and your BroadcastReceiver won't be called any longer, so another hook is needed.

For Android O and later, I've found that using a JobScheduler in conjunction with JobInfo.Builder.addTriggerContentUri to monitor the Settings.Secure.LOCATION_PROVIDERS_ALLOWED URI works for this purpose and will even launch your app if it isn't currently running to invoke your JobService. This approach requires API >= 24. I've verified that this works, including with Android P (API 28).

A few caveats with the JobScheduler approach:

  1. Your app may not get notified right away of the change, but in my testing, it does get notified within a few minutes.
  2. LOCATION_PROVIDERS_ALLOWED is deprecated and could be removed in a future version of Android.

So, if you're OK with a minApi version of 24, you can just use JobScheduler/JobService to get the Settings.Secure.LOCATION_PROVIDERS_ALLOWED hook.

But, if you don't like giving up 10% of your user base (as of this writing, KitKat (API 19) garners 9.1% of Android's active user base) and need a lower minApi, you'll need to have both a BroadcastReceiver and a JobService.

Oversight answered 15/6, 2018 at 4:53 Comment(8)
Michael, may I ask how you register your geofences? We do It with an explicit Broadcastreceiver that enqueues a jobintentservice. We do this because we can't do network calls directly in the broadcast receiver. However, we have problems with geofences triggering up to 20 minutes late...Facilitate
Hi @Mathias. As you've alluded to, BroadcastReceiver#onReceive is called from the main thread. I make use of the BroadcastReceiver#goAsync method to offload work to a background thread instead of a JobIntentService in this case.Oversight
Hey Michael, I ended up doing goAsync(), thanks! I tried it before, could have sworn I got the "background thread not allowed"-exception, but it seems to work. It feels a bit scary though that Android might kill the BroadcastReceiver if it takes too long...Facilitate
Michael, are you sure you get network operation to work in goasync()? It works very intermittently for me, and I'm note sure why but I suspect doze mode in Oreo and up.Facilitate
Made a separate thread about this issue #52257795Facilitate
@Facilitate One thing I can think of that might exacerbate this issue is that when the geofence transition happens, this could be accompanied by a network transition from Wifi->LTE, or LTE->Wifi. So, it's possible that you won't be able to report the transition right away in which case you'll have to schedule a retry when the network is available again. Lots of corner cases to deal with when it comes to geofencing on Android, I'm afraid.Oversight
@MichaelKrause Hi, could you please help me out in #53406341Hunk
@MichaelKrause First of all, thanks for your solid answer on this issue. On the last subtopic (The app has received a GEOFENCE_NOT_AVAILABLE alert) you briefly explained the way you managed to solve this and verified that it works that way. Could you provide a sample on how to achieve this? There's a lot of mixed documentation out there and I am stuck on this.Directoire

© 2022 - 2024 — McMap. All rights reserved.