Skip to content

Android Auto map freezes when app is backgrounded on Samsung Android 16 (Mapbox v11) #2716

Open

Description

@aliQMIC

Environment

  • Android OS version: Android 16
  • Devices affected: Galaxy S25 Ultra (and multiple new Samsung models)
  • Maps SDK Version: 11.20.1
  • Mapbox Android Auto Extension: 11.20.1
  • Android Car App Library: 1.7.0
  • Working devices: Pixel (Android 16) and Samsung (Android <16)

Observed behavior and steps to reproduce

I am observing an issue where the Mapbox map rendered in Android Auto stops updating when the mobile app is backgrounded on specific Samsung devices running Android 16.

The issue does not occur on:

Google Pixel devices running Android 16
Samsung devices running lower Android versions

This suggests a device/vendor-specific behavior affecting Mapbox rendering or camera updates when the app is backgrounded.

Steps to reproduce:

  1. Launch the app on a Samsung device (Android 16)
  2. Connect to Android Auto (Desktop Head Unit or real car)
  3. Render a Mapbox map using MapboxCarMap
  4. Enable location puck + follow camera (viewport follow puck)
  5. Start moving (real or simulated location)
  6. Background the mobile app (home button or switch apps)
  7. Observe Android Auto screen

Expected behavior

  • Location puck continues updating
  • Camera continues following location
  • Map continues rendering smoothly

Notes / preliminary analysis

Additional Notes / Hypothesis:
The issue appears to be related to Samsung’s background performance policies on Android 16
It seems that animation or rendering loops are being suspended when the app is not in foreground
Mapbox camera updates using animation or viewport transitions may be affected

Notably:

Using setCamera() (non-animated) appears to still work in some cases
Animated transitions (viewport / easeTo) and puck movement appear to stop completely

Additional links and references

This log appears exactly when the app is backgrounded:
Choreographer: CoreRune.SYSPERF_ACTIVE_APP_BBA_ENABLE : stop animation in background states

Minimal Repro

A minimal app was created with:

Mapbox Compose on phone
Mapbox Android Auto extension
Viewport follow-puck camera

The issue is reproducible consistently on Samsung Android 16.
Main Activity:

class MainActivity : ComponentActivity() {

    private var hasLocationPermission by mutableStateOf(false)

    private val requestLocationPermission =
        registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
            hasLocationPermission = granted
        }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        hasLocationPermission = ContextCompat.checkSelfPermission(
            this,
            Manifest.permission.ACCESS_FINE_LOCATION
        ) == PackageManager.PERMISSION_GRANTED

        if (!hasLocationPermission) {
            requestLocationPermission.launch(Manifest.permission.ACCESS_FINE_LOCATION)
        }

        setContent {
            val mapViewportState = rememberMapViewportState()

            MainMapScreen(
                hasLocationPermission = hasLocationPermission,
                mapViewportState = mapViewportState
            )
        }
    }
}

@Composable
private fun MainMapScreen(
    hasLocationPermission: Boolean,
    mapViewportState: MapViewportState
) {
    MapboxMap(
        modifier = Modifier.fillMaxSize(),
        mapViewportState = mapViewportState,
        style = {
            MapboxStandardStyle()
        },
        scaleBar = {
            ScaleBar(Modifier.padding(top = 60.dp))
        },
        logo = {
            Logo(Modifier.padding(bottom = 40.dp))
        },
        attribution = {
            Attribution(Modifier.padding(bottom = 40.dp))
        }
    ) {
        MapEffect(hasLocationPermission) { mapView ->
            val locationPlugin = mapView.location

            if (hasLocationPermission) {
                locationPlugin.updateSettings {
                    enabled = true
                    puckBearingEnabled = true
                    puckBearing = PuckBearing.HEADING
                    locationPuck = createDefault2DPuck(withBearing = true)
                }
            }
        }
    }

    LaunchedEffect(hasLocationPermission) {
        if (hasLocationPermission) {
            mapViewportState.transitionToFollowPuckState(
                FollowPuckViewportStateOptions.Builder()
                    .zoom(16.0)
                    .pitch(0.0)
                    .build()
            )
        } else {
            mapViewportState.setCameraOptions {
                zoom(2.0)
                center(Point.fromLngLat(-98.0, 39.5))
                pitch(0.0)
                bearing(0.0)
            }
        }
    }
}

Android auto related code:

class MyCarAppService : CarAppService() {
    override fun createHostValidator(): HostValidator {
        return HostValidator.ALLOW_ALL_HOSTS_VALIDATOR
    }

    override fun onCreateSession(): Session = MyCarSession()
}

class MyCarSession : Session() {
    private val mapboxCarMap = mapboxMapInstaller()
        .onCreated(TestLandingScreenCameraController())
        .install { carContext ->
            // Callback is triggered when the Session calls onCreate. This allows you to specify
            // custom MapInitOptions.
            MapInitOptions(carContext)
        }
    override fun onCreateScreen(intent: android.content.Intent): Screen {
        return MyCarScreen(carContext)
    }
}
class MyCarScreen(carContext: CarContext) : Screen(carContext) {
    override fun onGetTemplate(): Template {

        val temp = NavigationTemplate.Builder()
        val actionStrip = ActionStrip.Builder()
        actionStrip.addAction(Action.PAN)
        temp.setActionStrip(actionStrip.build())
        return temp.build()
    }
}

class TestLandingScreenCameraController : MapboxCarMapObserver {
    private var mapBoxMap : MapboxMap? = null
    private var mapSurface: MapSurface? = null
    
    override fun onAttached(mapboxCarMapSurface: MapboxCarMapSurface) {
        val mapboxMap = mapboxCarMapSurface.mapSurface.getMapboxMap()
        val mapSurface = mapboxCarMapSurface.mapSurface
        this.mapBoxMap = mapboxMap
        this.mapSurface = mapSurface

        mapboxMap.loadStyleUri(Style.MAPBOX_STREETS) {

            mapSurface.location.updateSettings {

                enabled = true
                pulsingEnabled = true
            }

        }
        val followPuckState = mapSurface.viewport.makeFollowPuckViewportState(
            FollowPuckViewportStateOptions.Builder()
                .zoom(16.0)
                .build()
        )

        mapSurface.viewport.transitionTo(followPuckState)
    }

    override fun onDetached(mapboxCarMapSurface: MapboxCarMapSurface) {

    }

}

Activity

  1. magnussp commented on May 13, 2026

    @magnussp

    I have observed exactly the same on my AA implementation. And old Samsung Galaxy S10 with Android version12 works fine with the app in background.

    On a Samsung Galaxy S22 with Android16 then the user puck and camera movement stops a few seconds after the app is put in background.

  2. raga70 commented on May 16, 2026

    @raga70

    https://github.com/mapbox/mapbox-maps-android/tree/main/extension-androidauto#xiaomi-animations

    https://docs.mapbox.com/android/maps/api/10.9.1/mapbox-maps-android/com.mapbox.maps.threading/-animation-thread-controller/

    i am also having issues with the background thread freezing on other devices , it`s a bit hit or miss and for Samsung device with android 16 it appears to be better with the background thread on

  3. magnussp commented on May 18, 2026

    @magnussp

    https://github.com/mapbox/mapbox-maps-android/tree/main/extension-androidauto#xiaomi-animations

    https://docs.mapbox.com/android/maps/api/10.9.1/mapbox-maps-android/com.mapbox.maps.threading/-animation-thread-controller/

    i am also having issues with the background thread freezing on other devices , it`s a bit hit or miss and for Samsung device with android 16 it appears to be better with the background thread on

    Thanks @raga70 this seem to have solved it for the Samsung phones we use for testing.

  4. aliQMIC commented on May 31, 2026

    @aliQMIC
    Author

    Thank you! it fixed it for me as well. Since mapbox says to only use background thread when necessary, is there a way in the code to detect that this issue has occurred ? @magnussp @raga70

  5. raga70 commented on May 31, 2026

    @raga70

    @aliQMIC Here is what is actually going on:
    The background thread flag is not a real fix, it is a trade-off. Enabling it unconditionally for everyone will stop the freeze on Samsung One UI 8+ / Android 16, but it will reintroduce the exact same freeze on the majority of other devices.
    The root cause is how each manufacturer's AOSP fork implements its Background Process Optimization. When the app is backgrounded, the BPO suspends a thread, but which thread it suspends is vendor dependent (some vendors don't put anything to sleep):
    Keep in mind that Battery optimization settings for the app do absolutely noting for BPO .

    Most devices suspend the background thread, so keeping rendering on the main thread is what works.
    Samsung on Android 16 suspends the main thread instead and lets the background thread keep running.

    That is why flipping the flag fixes one group and breaks the other. You are not detecting a bug condition so much as choosing which thread the OS is willing to keep alive on a given device.
    Because of that, I would not try to detect the issue after it occurs. By the time the puck has stopped updating, you are already in the bad state and there is no clean signal to react to.

    the easiest path for you would be:

    if ( Build.MANUFACTURER.equals("samsung", ignoreCase = true) && Build.VERSION.SDK_INT >= 36) {
        com.mapbox.maps.threading.AnimationThreadController.useBackgroundThread()
    }
    

    I personally detect if user is using a Samsung on A16 and show them a warning and telling them to flip the setting in my app`s settings if they have issues

    I did the stupid thing of putting all my 15k users on background thread and discovered that a lot of people not on Samsung started crying that their map animation freezes

    one thing I am not certain for is if Xiaomi is still putting the main thread to sleep .

    Keep in mind the background thread path has a magnitude of additional downsides, which is why Mapbox tells you to only use it when necessary. It is not free, and as noted above it recreates the same problem it solves on the wrong device set, so gating it behind the vendor and SDK check matters.

    I have also implemented a bunch of other safe guards , which pretty much don't really help like:

    • Foreground service with wake lock (FOREGROUND_SERVICE_TYPE_LOCATION)
    • KeepAlive watchdog for mapSurface and triggerRepaint
    • carSessionLifecycleOwner

    AND I REPEAT WHATEVER YOU DO, DO NOT ENABLE BACKGROUND THREAD FOR EVERYONE YOU WILL BE IN A VARY STICKY SITUATION

  6. Ranafugaz commented on Jun 2, 2026

    @Ranafugaz

    I tested the workaround on my Mapbox Android Auto implementation and it seems to work for me as well.

    My previous workaround was keeping the app alive with an overlay window from a background service, because on Samsung devices the Android Auto map froze when the phone app was backgrounded.

    After applying AnimationThreadController.useBackgroundThread() only for Samsung devices running Android 16 / API 36+, the map keeps updating correctly in Android Auto without needing the overlay workaround.

    I gated it like this:

    if (Build.MANUFACTURER.equals("samsung", ignoreCase = true) && Build.VERSION.SDK_INT >= 36) {
        AnimationThreadController.useBackgroundThread()
    }
  7. kyle-young commented on Jul 15, 2026

    @kyle-young

    For others finding this issue, I'm not using Mapbox but ran into a similar problem.

    My findings and workaround:

    The suspension is edge-triggered: it engages when the app transitions to the background (for example, when the screen locks) and disengages when the device wakes. After that, animations continue running even if the screen turns off again, because no new background transition occurs. As a result, recovery only requires a single brief screen wake, or "bump."

    I developed a watchdog that runs only while a car session has an active renderer. It periodically posts a canary animation-frame callback on the main thread's Choreographer. If the canary doesn't fire, animation callbacks are considered suspended, and the watchdog briefly wakes the phone screen to restore them. Screen bumps are rate-limited and are skipped while the screen is already interactive, since waking the device cannot help in that state.

  8. g4rb4g3 commented on Aug 24, 2026

    @g4rb4g3

    I had the exact same issue on some Samsung Android 16 devices (S23 Ultra, S25), from what I found you must not release the virtual display in onSurfaceDestroyed but rather keep a reference and release it when onSurfaceAvailable is invoked again and the new virtual display is shown (or when AA shuts down).
    Check this for details: Iternio-Planning-AB/react-native-auto-play#107

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bug 🪲Something isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions