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:
- Immediately stop ongoing playback
- Abandon audio focus
- Save playback state (play/pause state, playback position, etc.)
- Release audio-related resources (player, track, etc.)
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
}
}