Skip to content

Applying LocationComponentPlugin settings *before* setting a custom LocationProvider leaves LocationRequest that consumes battery #2679

@jeffbarge

Description

@jeffbarge

Environment

  • Android OS version: 16
  • Devices affected: Pixel 9, Samsung S25
  • Maps SDK Version: Unsure -- I reproduced using the latest mapbox-maps-android commit on the main branch

Observed behavior and steps to reproduce

When using a custom LocationProvider, any LocationComponentPlugin settings are changed before setting the custom provider, there is a LocationRequest left open from the DefaultLocationProvider that prevents batching LocationRequests. This impacts all other LocationRequests in the app that might otherwise be able to batch. This request remains open when the app is backgrounded or when the phone is locked as well, resulting in significant battery consumption. If the custom LocationProvider is set BEFORE updating any other LocationComponentPlugin settings, the DefaultLocationProvider is never instantiated and therefore this LocationRequest is never registered with the OS, allowing the consumer app's batching LocationRequests to behave as expected.

The necessity to register a custom LocationProvider before changing any other settings is neither expected nor documented.

Expected behavior

It should not matter at what point I set my custom LocationProvider -- once it is set, the default LocationRequest should be removed so that my LocationRequests can be batched as I intend.

Notes / preliminary analysis

From the sample app's LocationTrackingActivity:

private fun initLocationComponent() {
    val locationComponentPlugin = mapView.location

    /**********
     * Setting location provider here is safe, because it when updateSettings is called, the location provider
     * is not null, so we never instantiate a DefaultLocationProvider
     */
    locationComponentPlugin.setLocationProvider(FakeLocationProvider())

    locationComponentPlugin.updateSettings {
      puckBearing = PuckBearing.COURSE
      puckBearingEnabled = true
      enabled = true
      locationPuck = LocationPuck2D(
        bearingImage = ImageHolder.from(R.drawable.mapbox_user_puck_icon),
        shadowImage = ImageHolder.from(R.drawable.mapbox_user_icon_shadow),
        scaleExpression = interpolate {
          linear()
          zoom()
          stop {
            literal(0.0)
            literal(0.6)
          }
          stop {
            literal(20.0)
            literal(1.0)
          }
        }.toJson()
      )
    }

    /**********
     * Setting the location provider here is NOT SAFE. At this point, since it was null when updateSettings
     * was called, we created a DefaultLocationProvider. DefaultLocationProvider somehow registers a LocationRequest
     * that has a `minUpdateDistance` of 0.1 meters. This is a non-batching LocationRequest, and doesn't get
     * removed when setting a custom provider OR WHEN BACKGROUNDING THE APP.
     */
//    locationComponentPlugin.setLocationProvider(FakeLocationProvider())


    locationComponentPlugin.addOnIndicatorPositionChangedListener(onIndicatorPositionChangedListener)
    locationComponentPlugin.addOnIndicatorBearingChangedListener(onIndicatorBearingChangedListener)
  }

Additional links and references

The following logs were gathered via adb shell dumpsys activity service com.google.android.location.internal.GoogleLocationManagerService. They show the open request with minUpdateDistance=0.1 when the custom LocationProvider is set after updating the other settings, and it is missing when set before updating the other settings.

DefaultLocationProvider (no custom provider):

Fused Location Provider:
      source: Request[@1s HIGH_ACCURACY, WorkSource{10310 com.mapbox.maps.testapp}]
      listeners:
        10185/com.google.android.gms[fused_location_provider]/179714e4 (FINE) Request[PASSIVE, minUpdateInterval=0s, maxUpdateAge=0s, THROTTLE_ALWAYS, WorkSource{10185 com.google.android.gms}]
        10185/com.google.android.gms[current_semantic_location]/db8f52de (FINE) Request[@2m(10m)/10m BALANCED_POWER_ACCURACY, minUpdateInterval=15s, THROTTLE_ALWAYS, WorkSource{10185 com.google.android.gms}]
        10310/com.mapbox.maps.testapp/6854d070 (FINE) Request[@1s HIGH_ACCURACY, minUpdateDistance=0.1, waitForAccurateLocation, WorkSource{10310 com.mapbox.maps.testapp}]
        1000/android[LocationService]/74484423 (FINE) Request[PASSIVE/106751991167d7h12m55s807ms, minUpdateInterval=0s, THROTTLE_NEVER, bypass, WorkSource{1000 android}]
        10310/com.mapbox.maps.testapp/70197718 (FINE) Request[PASSIVE/106751991167d7h12m55s807ms, minUpdateInterval=1s, minUpdateDistance=1.0, maxUpdateAge=2s, waitForAccurateLocation, WorkSource{10310 com.mapbox.maps.testapp}]
        10185/com.google.android.gms[.findmydevice]/a462af5d (FINE) Request[@20m BALANCED_POWER_ACCURACY, THROTTLE_ALWAYS, WorkSource{10185 com.google.android.gms}]
        10304/com.alltrails.alltrailsalpha/e2a18811 {bg, na} (FINE) Request[PASSIVE/4s, minUpdateInterval=1s, minUpdateDistance=1.0, maxUpdateAge=2s, waitForAccurateLocation, WorkSource{10304 com.alltrails.alltrailsalpha}] (inactive)
        10185/com.google.android.gms[earthquake_alerting]/d8b1b22f (FINE) Request[@30m BALANCED_POWER_ACCURACY, minUpdateInterval=5m, minUpdateDistance=1000.0, THROTTLE_NEVER, WorkSource{10185 com.google.android.gms}]
        10185/com.google.android.gms[.personalsafety]/4a589c94 (FINE) Request[@10m BALANCED_POWER_ACCURACY, minUpdateInterval=2m, minUpdateDistance=40.0, THROTTLE_NEVER, WorkSource{10185 com.google.android.gms}]
        10185/com.google.android.gms[fused_location_provider]/b03aa68d (FINE) Request[PASSIVE/106751991167d7h12m55s807ms, minUpdateInterval=750ms, maxUpdateAge=1h, THROTTLE_NEVER, WorkSource{10185 com.google.android.gms}]

Custom provider, added after updateSettings:

Fused Location Provider:
      source: Request[@1s HIGH_ACCURACY, WorkSource{10310 com.mapbox.maps.testapp}]
      listeners:
        10185/com.google.android.gms[fused_location_provider]/179714e4 (FINE) Request[PASSIVE, minUpdateInterval=0s, maxUpdateAge=0s, THROTTLE_ALWAYS, WorkSource{10185 com.google.android.gms}]
        10185/com.google.android.gms[current_semantic_location]/db8f52de (FINE) Request[@2m(10m)/10m BALANCED_POWER_ACCURACY, minUpdateInterval=15s, THROTTLE_ALWAYS, WorkSource{10185 com.google.android.gms}]
        10310/com.mapbox.maps.testapp/fafaa085 (FINE) Request[@1s HIGH_ACCURACY, minUpdateDistance=0.1, waitForAccurateLocation, WorkSource{10310 com.mapbox.maps.testapp}]
        1000/android[LocationService]/74484423 (FINE) Request[PASSIVE/106751991167d7h12m55s807ms, minUpdateInterval=0s, THROTTLE_NEVER, bypass, WorkSource{1000 android}]
        10185/com.google.android.gms[.findmydevice]/a462af5d (FINE) Request[@20m BALANCED_POWER_ACCURACY, THROTTLE_ALWAYS, WorkSource{10185 com.google.android.gms}]
        10310/com.mapbox.maps.testapp/e74080d9 (FINE) Request[PASSIVE/106751991167d7h12m55s807ms, minUpdateInterval=1s, minUpdateDistance=1.0, maxUpdateAge=2s, waitForAccurateLocation, WorkSource{10310 com.mapbox.maps.testapp}]
        10304/com.alltrails.alltrailsalpha/e2a18811 {bg, na} (FINE) Request[PASSIVE/4s, minUpdateInterval=1s, minUpdateDistance=1.0, maxUpdateAge=2s, waitForAccurateLocation, WorkSource{10304 com.alltrails.alltrailsalpha}] (inactive)
        10185/com.google.android.gms[earthquake_alerting]/d8b1b22f (FINE) Request[@30m BALANCED_POWER_ACCURACY, minUpdateInterval=5m, minUpdateDistance=1000.0, THROTTLE_NEVER, WorkSource{10185 com.google.android.gms}]
        10185/com.google.android.gms[.personalsafety]/4a589c94 (FINE) Request[@10m BALANCED_POWER_ACCURACY, minUpdateInterval=2m, minUpdateDistance=40.0, THROTTLE_NEVER, WorkSource{10185 com.google.android.gms}]
        10185/com.google.android.gms[fused_location_provider]/b03aa68d (FINE) Request[PASSIVE/106751991167d7h12m55s807ms, minUpdateInterval=750ms, maxUpdateAge=1h, THROTTLE_NEVER, WorkSource{10185 com.google.android.gms}]

Custom provider, added before updateSettings:

Fused Location Provider:
      source: Request[@2m(10m)/10m BALANCED_POWER_ACCURACY, WorkSource{10185 com.google.android.gms}]
      listeners:
        10310/com.mapbox.maps.testapp/f88cc222 (FINE) Request[PASSIVE/106751991167d7h12m55s807ms, minUpdateInterval=1s, minUpdateDistance=1.0, maxUpdateAge=2s, waitForAccurateLocation, WorkSource{10310 com.mapbox.maps.testapp}]
        10185/com.google.android.gms[current_semantic_location]/db8f52de (FINE) Request[@2m(10m)/10m BALANCED_POWER_ACCURACY, minUpdateInterval=15s, THROTTLE_ALWAYS, WorkSource{10185 com.google.android.gms}]
        1000/android[LocationService]/74484423 (FINE) Request[PASSIVE/106751991167d7h12m55s807ms, minUpdateInterval=0s, THROTTLE_NEVER, bypass, WorkSource{1000 android}]
        10185/com.google.android.gms[.findmydevice]/a462af5d (FINE) Request[@20m BALANCED_POWER_ACCURACY, THROTTLE_ALWAYS, WorkSource{10185 com.google.android.gms}]
        10304/com.alltrails.alltrailsalpha/e2a18811 {bg, na} (FINE) Request[PASSIVE/4s, minUpdateInterval=1s, minUpdateDistance=1.0, maxUpdateAge=2s, waitForAccurateLocation, WorkSource{10304 com.alltrails.alltrailsalpha}] (inactive)
        10185/com.google.android.gms[earthquake_alerting]/d8b1b22f (FINE) Request[@30m BALANCED_POWER_ACCURACY, minUpdateInterval=5m, minUpdateDistance=1000.0, THROTTLE_NEVER, WorkSource{10185 com.google.android.gms}]
        10185/com.google.android.gms[.personalsafety]/4a589c94 (FINE) Request[@10m BALANCED_POWER_ACCURACY, minUpdateInterval=2m, minUpdateDistance=40.0, THROTTLE_NEVER, WorkSource{10185 com.google.android.gms}]
        10185/com.google.android.gms[fused_location_provider]/b03aa68d (FINE) Request[PASSIVE/106751991167d7h12m55s807ms, minUpdateInterval=750ms, maxUpdateAge=1h, THROTTLE_NEVER, WorkSource{10185 com.google.android.gms}]

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions