google play services – Do Geofences remain active in android after a device reboot-ThrowExceptions

Exception or error:

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.

How to solve:

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

###

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.

Leave a Reply

Your email address will not be published. Required fields are marked *