mapbox / mapbox/mapbox-maps-android

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

Open
#2,679 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

auto-triaged bug :beetle: ticketed
Dominant language
Kotlin
Stars
578
Forks
161
PR merge metrics
No merged PRs in 30d

Description

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}]

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the sample app's LocationTrackingActivity and trace LocationComponentPlugin.updateSettings, setLocationProvider, and DefaultLocationProvider. Reproduce both provider-ordering cases and inspect the adb dumpsys location requests. Done means setting a custom provider removes the default request, including after backgrounding or locking the app.

Written by the indexing model from the issue text.

Assessment

Tech stack
android, kotlin
Domain
mobile-dev, performance
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.