Skip to main content

Suspend Mode Audio Focus Guide

What Is Suspend Mode

Suspend mode is an operating state entered after vehicle ignition (IGN) is turned OFF, where the system does not fully shut down but transitions into a low-power state. In this state, most user-facing functions are paused, while memory and selected core capabilities remain active for fast resume.

The key characteristics of Suspend mode are:

  • An inactive state with no user interaction
  • Display and most UI features are disabled
  • General services such as audio and media are paused
  • Some system jobs (for example, Garage mode) may run in a limited scope
  • State is preserved for fast recovery on resume

Suspend mode is not a full shutdown; it is a low-power retention state. From an app perspective, it should be treated as a state where execution can be interrupted and restarted at any time.

Audio Focus Handling in Suspend Mode

Core Principles

When entering Suspend mode, all regular audio sessions must stop output to the user, and each app must release any audio focus it currently holds.

The core principles are:

  • Audio output must be stopped before entering Suspend mode.
  • Audio focus must not be retained and should be explicitly abandoned.
  • Focus should not be automatically restored after Resume.
  • Audio state should be persisted and requested again only when needed.

Handling During Suspend Entry

When Suspend entry is detected, each app should process in the following order:

  1. Immediately stop ongoing playback
  2. Abandon audio focus
  3. Save playback state (play/pause state, playback position, etc.)
  4. Release audio-related resources (player, track, etc.)
During this process, new audio playback or new audio focus requests are not allowed.

Exception Policy

The following functions may be allowed as limited exceptions depending on system policy:

  • Phone calls and emergency communication
  • Safety-related warning sounds
  • System-critical voice guidance

However, exception features must not be allowed for the purpose of continuously providing user audio during Suspend, and the scope of exceptions must be kept to a minimum.

Handling on Resume

After Resume from Suspend, the previous audio state must not be restored as-is.
  • Audio focus is not automatically recovered
  • The app reevaluates current conditions and requests focus again only if needed
  • Auto-resume behavior follows OEM or user policy

Compliance Checklist

Each app should comply with the following:

  • Do not keep audio focus when entering Suspend mode.
  • Do not continue audio playback in the background.
  • Do not preemptively request or retain audio focus solely for Resume.

Example (Kotlin)

This example demonstrates Suspend-entry handling and Resume handling.

import android.content.Context
import android.media.AudioFocusRequest
import android.media.AudioManager
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity

class PlayerActivity : AppCompatActivity() {

private lateinit var audioManager: AudioManager

// Example player state
private var isPlaying: Boolean = false
private var lastPlaybackPositionMs: Long = 0L
private var hasAudioFocus: Boolean = false

private val focusChangeListener = AudioManager.OnAudioFocusChangeListener { focusChange ->
when (focusChange) {
AudioManager.AUDIOFOCUS_LOSS,
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
pausePlayback()
abandonAudioFocus()
}
}
}

private val audioFocusRequest: AudioFocusRequest by lazy {
AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
.setOnAudioFocusChangeListener(focusChangeListener)
.build()
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
}

fun startPlayback() {
if (!requestAudioFocus()) return

// TODO: Call actual player start
isPlaying = true
}

fun onSuspendEntering() {
// 1) Do not start new work
// 2) Stop ongoing work
pausePlayback()

// 3) Save state
savePlaybackState()

// 4) Release audio focus
abandonAudioFocus()

// 5) Clean up transient resources (network/timer/sensors) if needed
releaseTransientResources()
}

fun onResumeFromSuspend() {
// Do not auto-play immediately after Resume.
// Reevaluate current system/user conditions first.
restorePlaybackState()

val shouldAutoResume = false // Determined by OEM/app policy
if (shouldAutoResume) {
startPlayback()
}
}

private fun requestAudioFocus(): Boolean {
val result = audioManager.requestAudioFocus(audioFocusRequest)
hasAudioFocus = result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED
return hasAudioFocus
}

private fun abandonAudioFocus() {
if (!hasAudioFocus) return
audioManager.abandonAudioFocusRequest(audioFocusRequest)
hasAudioFocus = false
}

private fun pausePlayback() {
if (!isPlaying) return

// TODO: Read current position from actual player
lastPlaybackPositionMs = getCurrentPlaybackPosition()

// TODO: Call actual player pause/stop
isPlaying = false
}

private fun savePlaybackState() {
// TODO: Persist via SharedPreferences/DB/ViewModel, etc.
// Example: playback position, current content ID, play state
}

private fun restorePlaybackState() {
// TODO: Restore persisted state
}

private fun releaseTransientResources() {
// TODO: Cancel timers, unregister sensors/listeners, clear temporary wake locks, etc.
}

private fun getCurrentPlaybackPosition(): Long {
// TODO: Return position from actual player
return lastPlaybackPositionMs
}
}