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​

danger

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.)
danger

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​

danger

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