Unverified Commit ead18e75 authored by Sebastian Kaspari's avatar Sebastian Kaspari
Browse files

Issue #4033: Abandon audio focus when media service is shutting down.

parent 2a63b608
Loading
Loading
Loading
Loading
+19 −34
Original line number Diff line number Diff line
@@ -4,9 +4,6 @@

package mozilla.components.feature.media.focus

import android.content.Context
import android.media.AudioAttributes
import android.media.AudioFocusRequest
import android.media.AudioManager
import android.os.Build
import mozilla.components.feature.media.ext.getMedia
@@ -23,19 +20,34 @@ import mozilla.components.support.base.log.logger.Logger
 * https://developer.android.com/guide/topics/media-apps/audio-focus
 */
internal class AudioFocus(
    private val context: Context
    val audioManager: AudioManager
) : AudioManager.OnAudioFocusChangeListener {
    private val logger = Logger("AudioFocus")
    private var playDelayed = false
    private var resumeOnFocusGain = false

    private val audioFocusController = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        AudioFocusControllerV26(audioManager, this)
    } else {
        AudioFocusControllerV21(audioManager, this)
    }

    @Synchronized
    fun request(state: MediaState) {
        val result = requestAudioFocusCompat(context, this)
        val result = audioFocusController.request()
        processAudioFocusResult(state, result)
    }

    @Synchronized
    fun abandon() {
        audioFocusController.abandon()
        playDelayed = false
        resumeOnFocusGain = false
    }

    private fun processAudioFocusResult(state: MediaState, result: Int) {
        logger.debug("processAudioFocusResult($result)")

        when (result) {
            AudioManager.AUDIOFOCUS_REQUEST_GRANTED -> {
                // Granted: Gecko already started playing media.
@@ -60,6 +72,8 @@ internal class AudioFocus(

    @Synchronized
    override fun onAudioFocusChange(focusChange: Int) {
        logger.debug("onAudioFocusChange($focusChange)")

        val state = MediaStateMachine.state

        when (focusChange) {
@@ -93,32 +107,3 @@ internal class AudioFocus(
        }
    }
}

private fun requestAudioFocusCompat(
    context: Context,
    listener: AudioManager.OnAudioFocusChangeListener
): Int {
    val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager

    return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
        @Suppress("DEPRECATION")
        audioManager.requestAudioFocus(
            listener,
            AudioManager.STREAM_MUSIC,
            AudioManager.AUDIOFOCUS_GAIN
        )
    } else {
        audioManager.requestAudioFocus(
            AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
                .setAudioAttributes(
                    AudioAttributes.Builder()
                        .setUsage(AudioAttributes.USAGE_MEDIA)
                        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                        .build()
                )
                .setWillPauseWhenDucked(false)
                .setOnAudioFocusChangeListener(listener)
                .build()
        )
    }
}
+13 −0
Original line number Diff line number Diff line
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package mozilla.components.feature.media.focus

/**
 * A controller that knows how to request and abandon audio focus.
 */
internal interface AudioFocusController {
    fun request(): Int
    fun abandon()
}
+28 −0
Original line number Diff line number Diff line
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package mozilla.components.feature.media.focus

import android.media.AudioManager

/**
 * [AudioFocusController] implementation for Android API 21+.
 */
@Suppress("DEPRECATION")
internal class AudioFocusControllerV21(
    private val audioManager: AudioManager,
    private val listener: AudioManager.OnAudioFocusChangeListener
) : AudioFocusController {
    override fun request(): Int {
        return audioManager.requestAudioFocus(
            listener,
            AudioManager.STREAM_MUSIC,
            AudioManager.AUDIOFOCUS_GAIN
        )
    }

    override fun abandon() {
        audioManager.abandonAudioFocus(listener)
    }
}
+40 −0
Original line number Diff line number Diff line
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package mozilla.components.feature.media.focus

import android.annotation.TargetApi
import android.media.AudioAttributes
import android.media.AudioFocusRequest
import android.media.AudioManager

import android.os.Build

/**
 * [AudioFocusController] implementation for Android API 26+.
 */
@TargetApi(Build.VERSION_CODES.O)
internal class AudioFocusControllerV26(
    private val audioManager: AudioManager,
    listener: AudioManager.OnAudioFocusChangeListener
) : AudioFocusController {
    private val request = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
        .setAudioAttributes(
            AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_MEDIA)
                .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                .build()
        )
        .setWillPauseWhenDucked(false)
        .setOnAudioFocusChangeListener(listener)
        .build()

    override fun request(): Int {
        return audioManager.requestAudioFocus(request)
    }

    override fun abandon() {
        audioManager.abandonAudioFocusRequest(request)
    }
}
+3 −1
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@ import android.app.Service
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.media.AudioManager
import android.os.IBinder
import android.support.v4.media.MediaMetadataCompat
import android.support.v4.media.session.MediaSessionCompat
@@ -36,7 +37,7 @@ internal class MediaService : Service() {
    private val logger = Logger("MediaService")
    private val notification = MediaNotification(this)
    private val mediaSession by lazy { MediaSessionCompat(this, "MozacMedia") }
    private val audioFocus = AudioFocus(this)
    private val audioFocus by lazy { AudioFocus(getSystemService(Context.AUDIO_SERVICE) as AudioManager) }

    override fun onCreate() {
        super.onCreate()
@@ -102,6 +103,7 @@ internal class MediaService : Service() {
    }

    private fun shutdown() {
        audioFocus.abandon()
        mediaSession.release()
        stopSelf()
    }
Loading