Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug] When requesting audio focus to the application, the focus is immediately lost #615

Open
fabiorbap opened this issue Jan 30, 2025 · 6 comments
Labels
bug Something isn't working enhancement New feature or request

Comments

@fabiorbap
Copy link

fabiorbap commented Jan 30, 2025

Describe the bug

I have a class which sets a TtsNavigator through

        val ttsNavigator = ttsNavigatorFactory?.createNavigator(
            this,
            initialLocator = locator,
            initialPreferences = preferencesManager.value
        )

When I play it, I request the audio focus because I want to control what happens when there's a gain/loss of audio focus (with incoming messages, calls etc).
However, whenever I run ttsNavigator.play() and request the focus, it's immediately lost. This is the code related to audio focusing:

    private val focusRequest
        @RequiresApi(Build.VERSION_CODES.O)
        get() = AudioFocusRequest.Builder(AUDIOFOCUS_GAIN).run {
            setAudioAttributes(AudioAttributes.Builder().run {
                setUsage(AudioAttributes.USAGE_MEDIA)
                setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
                setWillPauseWhenDucked(true)
                build()
            })
            setAcceptsDelayedFocusGain(true)
            setOnAudioFocusChangeListener(afChangeListener)
            build()
        }

    private val afChangeListener = AudioManager.OnAudioFocusChangeListener { focusChange ->
        when (focusChange) {
            AUDIOFOCUS_LOSS -> {
                abandonAudioFocusRequest()
                navigatorNow.value?.pause()
            }
            AUDIOFOCUS_LOSS_TRANSIENT, AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> navigatorNow.value?.pause()
            AUDIOFOCUS_GAIN -> navigatorNow.value?.play()
        }
    }

    @Suppress("DEPRECATION")
    private fun abandonAudioFocusRequest() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            audioManager?.abandonAudioFocusRequest(focusRequest)
        } else {
            audioManager?.abandonAudioFocus(afChangeListener)
        }
    }

    @Suppress("DEPRECATION")
    private fun requestAudioFocus(): Boolean {
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            audioManager?.requestAudioFocus(focusRequest) == AUDIOFOCUS_REQUEST_GRANTED
        } else {
            audioManager?.requestAudioFocus(
                afChangeListener,
                AudioManager.STREAM_MUSIC,
                AudioManager.AUDIOFOCUS_GAIN
            ) == AUDIOFOCUS_REQUEST_GRANTED
        }
    }

    fun play() {
        navigatorNow.value?.play()
        requestAudioFocus()
    }

As mentioned, when I start playing the TTS, I get AUDIOFOCUS_LOSS here:

        when (focusChange) {
            AUDIOFOCUS_LOSS -> {
                abandonAudioFocusRequest()
                navigatorNow.value?.pause()
            }
            AUDIOFOCUS_LOSS_TRANSIENT, AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> navigatorNow.value?.pause()
            AUDIOFOCUS_GAIN -> navigatorNow.value?.play()
        }
    }

Once I lose the audio focus, I can't do anything with the listener anymore.
One thing that I noticed in the logs is that it seems that Readium "steals" the focus when I hit play. I find this a bit weird, because Readium is being executed in my application, and so it should not steal the focus.
I saw in the Readium code that there is audio focus management.
Here are some logs that I found:

requestAudioFocus() from uid/pid 11799/7267 AA=USAGE_MEDIA/CONTENT_TYPE_SPEECH clientId=android.media.AudioManager@becd5abee.<my class>$$ExternalSyntheticLambda0@a334408 callingPack=<package> req=1 flags=0x3 sdk=34
requestAudioFocus() from uid/pid 11799/7267 AA=USAGE_MEDIA/CONTENT_TYPE_SPEECH clientId=android.media.AudioManager@becd5aborg.readium.navigator.media.tts.session.AudioFocusManager$AudioFocusListener@b212187 callingPack=<package> req=1 flags=0x2 sdk=34
dispatching onAudioFocusChange(-1) to android.media.AudioManager@becd5abee.<my class>$$ExternalSyntheticLambda0@a334408
abandonAudioFocus() from uid/pid 11799/7267 clientId=android.media.AudioManager@becd5abee.<my class>$$ExternalSyntheticLambda0@a334408
abandonAudioFocus, clientId = android.media.AudioManager@becd5abee.<my class>$$ExternalSyntheticLambda0@a334408
dispatching onAudioFocusChange(1) to android.media.AudioManager@becd5aborg.readium.navigator.media.tts.session.AudioFocusManager$AudioFocusListener@b212187

You can see that after my class requests the audio focus, Readium gets it soon after, I don't know why. Once that happens my app never gets the focus back.
It's curious that callingPack for both is my app's package, so I don't understand why my app loses the focus.
I don't know if I'm missing something, or if this is a bug, but please let me know what's the right way to manage audio focus so that I have full control over it while I'm using TTS.

How to reproduce?

  1. Open any book
  2. Initiate the TTS and start playing it
  3. Perform any interruption with sound (notification, message, incoming call)
  4. The TTS will not stop when the interruption happens, and it is not possible to set an audio focus listener for that either

Readium version

3.0.0

Android API version

12 (API 32), 13 (API 33), 14 (API 34)

Additional context

@fabiorbap fabiorbap added bug Something isn't working triage Triage needed by maintainers labels Jan 30, 2025
@fabiorbap fabiorbap changed the title [Bug] When setting an audio focus listener, the focus is immediately lost [Bug] When requesting audio focus to the application, the focus is immediately lost Jan 30, 2025
@qnga
Copy link
Member

qnga commented Jan 31, 2025

Hello,
Automatic focus management by Readium has more of a feature than of a bug. The implementation is heavily inspired by ExoPlayer which features the same. I guess you need to disable it.

When you call asMediaPlayer on your TtsNavigator, you get an instance of Player. Have you tried to call setAudioAttributes with handleAudioFocus=false? You can get the current audio attributes with getAudioAttributes and pass them back.

On a different topic, can you confirm that the TTS doesn't stop when an interruption happens with no custom focus listener meddling in? That sounds like a bug and would deserve opening a separate issue.

@qnga
Copy link
Member

qnga commented Jan 31, 2025

I looked better at the code, my suggestion can't work right now: the handleAudioFocus parameter is ignored. So there is some work to do.

@qnga qnga added enhancement New feature or request and removed bug Something isn't working triage Triage needed by maintainers labels Jan 31, 2025
@fabiorbap
Copy link
Author

@qnga

When you call asMediaPlayer on your TtsNavigator, you get an instance of Player. Have you tried to call setAudioAttributes with handleAudioFocus=false? You can get the current audio attributes with getAudioAttributes and pass them back.

I tried this and then implemented the custom listener, but nothing changed :(

On a different topic, can you confirm that the TTS doesn't stop when an interruption happens with no custom focus listener meddling in? That sounds like a bug and would deserve opening a separate issue.

I started playing the TTS, and then sent a notification to my device, the TTS kept playing as if no interruption happened. I tested this in Android 13 and 14.
I even tried it in the test app and the behavior was the same - the TTS didn't stop with any sound interruption (notification)
Looks like it is a bug, right?

I looked better at the code, my suggestion can't work right now: the handleAudioFocus parameter is ignored. So there is some work to do.

Yes, I tried setting it to false, but no change happened.
What I find weird is that with Readium 2.3.0 I could set a custom listener, and it would work as expected, I tested it in Android 12, 13, and 14.
When I migrated to 3.0.0, this started happening, the custom listener didn't work properly, it always receives AUDIOFOCUS_LOSS whenever it starts playing. It does a quick switch: the app briefly gains focus (1) and then immediately loses it (-1).
I don't know if it's because of any particular implementation I made or because of Readium. It feels like it's Readium because I'm simply requesting audio focus to the system, there's nothing else I think I do in the code that would cause this immediate audio focus loss.

To test this, I also tried requesting the focus with a delay, to see if there was a racing condition, or if requesting the audio focus after the Readium request would change something, but it didn't. After the TTS started playing, I set a 5-second delay to request the focus. Yet the same thing happened, after requesting the focus 5 seconds later, the app regained focus yet lost it immediately.

I believe this is a bug, right? Do you have any other suggestions as to what I could do to make this work? If you need any more information, let me know!

@qnga
Copy link
Member

qnga commented Jan 31, 2025

Thanks for the detailed report! There definitely could be a bunch of Readium-related issues. Unfortunately, this requires a lot of investigation so I can't help you more right now.

@qnga qnga added the bug Something isn't working label Jan 31, 2025
@fabiorbap
Copy link
Author

fabiorbap commented Jan 31, 2025

@qnga Thank you for the help.
Can I expect that this bug will be investigated and possibly fixed at any time in the future?
If I can help in any way, let me know!

@qnga
Copy link
Member

qnga commented Feb 1, 2025

Yes, but at some entirely indefinite time, unfortunately.

The code should be quite straightforward if you're familiar with ExoPlayer and Media APIs, so feel free to open a PR. The goals are as follows:

  • Make the toolkit handle correctly audio focus as any other app by default.
  • Enable apps to disable audio focus management with setAttributes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants