androidandroid-pendingintentandroid-geofenceandroid-12

In Android 12/API 31, Geofence doesn't work with IMMUTABLE pendingintent. Why?


A new PendingIntent field in PendingIntent is FLAG_IMMUTABLE.

In 31, you must specify MUTABLE or IMMUTABLE, or you can't create the PendingIntent, (Of course we can't have defaults, that's for losers) as referenced here

According to the (hilarious) Google Javadoc for Pendingintent, you should basically always use IMMUTABLE (empasis mine):

It is strongly recommended to use FLAG_IMMUTABLE when creating a PendingIntent. FLAG_MUTABLE should only be used when some functionality relies on modifying the underlying intent, e.g. any PendingIntent that needs to be used with inline reply or bubbles (editor's comment: WHAT?).

Right, so i've always created PendingIntents for a Geofence like this:

PendingIntent proximityIntent = PendingIntent.getBroadcast(context, requestCode, intent, PendingIntent.FLAG_NO_CREATE)

Always worked fine. However, following the docs above, i added the IMMUTABLE flag like this:

PendingIntent proximityIntent = PendingIntent.getBroadcast(context, requestCode, intent, PendingIntent.FLAG_NO_CREATE|PendingIntent.FLAG_IMMUTABLE)

Now, what that results in is that while i still the geofence transitions in my Receiver, if i call

List<Geofence> triggeringGeofences = geofencingEvent.getTriggeringGeofences();

It returns null!

So, i have two questions.

  1. Why does the IMMUTABLE flag result in that i don't get the triggering geofence as i have in the past?

  2. Am i doing something wrong? Is there a way to do set IMMUTABLE with Geofence triggers?

Actually, i have three questions:

  1. Why is Google's documentation so confusing, bad, contradictory and lagging behind? (it's a rhetorical question)

Pointers much appreciated.


Solution

  • In this case, the pending intent for the geofence needs to use FLAG_MUTABLE while the notification pending intent needs to use FLAG_IMMUTABLE. Unfortunately, they have not updated the documentation or the codelabs example for targeting Android 12 yet. Here's how I modified the codelabs geofence example to work.

    First, update gradle to target SDK31.

    In HuntMainActivity, change the geofencePendingIntent to:

      private val geofencePendingIntent: PendingIntent by lazy {
        val intent = Intent(this, GeofenceBroadcastReceiver::class.java)
        intent.action = ACTION_GEOFENCE_EVENT
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
          PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE)
        } else {
          PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
        }
      }
    

    In NotificationUtils.kt, update the notifications pending intent as follows:

      val contentIntent = Intent(context, HuntMainActivity::class.java)
      contentIntent.putExtra(GeofencingConstants.EXTRA_GEOFENCE_INDEX, foundIndex)
      val contentPendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        PendingIntent.getActivity(
          context,
          NOTIFICATION_ID,
          contentIntent,
          PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )
      } else {
        PendingIntent.getActivity(
          context,
          NOTIFICATION_ID,
          contentIntent,
          PendingIntent.FLAG_UPDATE_CURRENT
        )
      }