audio_service icon indicating copy to clipboard operation
audio_service copied to clipboard

AudioService.init blocking

Open YBill opened this issue 2 months ago • 3 comments

Platforms exhibiting the bug

  • [x] Android
  • [ ] iOS
  • [ ] web

Devices exhibiting the bug

I haven't been able to reproduce the issue locally, but many users online have reported audio playback issues. I added tracking data online and found that a large number of users are blocked after executing AudioService.init, ultimately entering the onTimeout block. I can't reproduce the issue locally, but many users online are experiencing this issue. Furthermore, tracking data reveals that it's not always a problem; a problem this time might resolve after the next reboot. The same code doesn't affect any iOS devices, but it affects nearly all Android devices and the Android system. I suspect it's an issue in the AudioServicePlugin, but I haven't found the problem in the code. Could you provide some advice on where the issue lies? Thank you very much.

Minimal reproduction project

Minimal reproduction project
audio_service: ^0.18.18
...
import com.ryanheise.audioservice.AudioServiceActivity
class MainActivity: AudioServiceActivity() {
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>
    <application
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:launchMode="singleTop" />
        <service
            android:name="com.ryanheise.audioservice.AudioService"
            android:exported="true"
            android:foregroundServiceType="mediaPlayback"
            tools:ignore="Instantiatable">
            <intent-filter>
                <action android:name="android.media.browse.MediaBrowserService" />
            </intent-filter>
        </service>
       <receiver
           android:name="com.ryanheise.audioservice.MediaButtonReceiver"
           android:exported="true"
           tools:ignore="Instantiatable">
           <intent-filter>
               <action android:name="android.intent.action.MEDIA_BUTTON" />
           </intent-filter>
       </receiver>
    </application>
</manifest>
  Future<void> _initPlayer() async {
    LogD(tag: TAG, "------ _initPlayer");
    try {
      const int timeoutSeconds = 15;
      _audioHandler = await AudioService.init(
              builder: () {
                LogD(tag: TAG, "------ AudioPlayerHandlerImpl builder called");
                return AudioPlayerHandlerImpl();
              },
              config: const AudioServiceConfig(
                  androidNotificationChannelId: 'channel_summary_reading',
                  androidNotificationChannelName: 'Summary playback',
                  androidNotificationOngoing: true,
                  androidNotificationIcon: 'drawable/ic_notification',
                  rewindInterval: Duration(seconds: 5)))
          .timeout(
        const Duration(seconds: timeoutSeconds),
        onTimeout: () {
          LogE(tag: TAG, "------ AudioService.init timeout after $timeoutSeconds seconds");
          throw Exception("AudioService.init timeout after $timeoutSeconds seconds");
        },
      );
    } catch (e) {
      LogE(tag: TAG, "------ unexpected error in _initPlayer: ${e.toString()}");
      throw e;
    } finally {
      LogD(tag: TAG, "------ _initPlayer finally");
    }
  }

Steps to reproduce

After entering my project, I will perform AudioService.init initialization

Expected results

Expected initialization success or failure

Actual results

Actually, after calling AudioService.init, it is blocked and there will never be any result. I added a timeout and it will eventually enter onTimeout.

Screenshots or Video

Screenshots / Video demonstration

[Upload media here]

Logs

Logs

Flutter Doctor output

Doctor output
$ flutter doctor                   
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, 3.29.3, on Microsoft Windows [版本 10.0.26100.6584], locale zh-CN)
[√] Windows Version (11 专业版 64-bit, 24H2, 2009)
[√] Android toolchain - develop for Android devices (Android SDK version 35.0.0)
[√] Chrome - develop for the web
[!] Visual Studio - develop Windows apps (Visual Studio Community 2022 17.11.2)
    X Visual Studio is missing necessary components. Please re-run the Visual Studio installer for the "Desktop development with C++" workload, and include these components:
        MSVC v142 - VS 2019 C++ x64/x86 build tools
         - If there are multiple build tool versions available, install the latest
        C++ CMake tools for Windows
        Windows 10 SDK
[√] Android Studio (version 2024.3)
[√] Android Studio (version 2025.1.4)
[√] Android Studio (version 2021.1)
[√] IntelliJ IDEA Community Edition (version 2023.2)
[√] Connected device (4 available)
[√] Network resources

! Doctor found issues in 1 category.

YBill avatar Oct 13 '25 07:10 YBill

The first step would really be a reproduction project, even if you were to dig deeper yourself, but certainly for me to investigate.

ryanheise avatar Oct 13 '25 15:10 ryanheise

Thanks for your reply. I am still trying to reproduce it, but it hasn't happened yet. I added some tracking points in the online package to collect more clues.

YBill avatar Oct 14 '25 06:10 YBill

I found that when I use the "workmanager" library, the audio_service library encounters an initialization blocking issue. Specifically, the FlutterEngine is created twice with timestamps (1761305360453, 1761305360701), destroyed once with a timestamp of (1761305361449), and attached to the activity once with a timestamp of (1761306195136).

The onAttachedToEngine callback is triggered twice. During the first execution of onAttachedToEngine, the size of clientInterfaces becomes 1 and mediaBrowser is null, so connect() is called to start the connection. During the second execution of onAttachedToEngine, the size of clientInterfaces becomes 2 and mediaBrowser is not null. Then, in onDetachedFromEngine, since the size of clientInterfaces is 2, disconnect is not executed, but applicationContext is set to null at this point. Later, in the onConnected method of connectionCallback, applicationContext is null, so mediaController is null. Subsequently, in onAttachedToActivity, since mediaBrowser is not null, reconnection is not performed, resulting in mediaController remaining null indefinitely. This causes a permanent block after calling AudioService.init.

The above analysis is based on the monitoring data I added online. It seems that the problem is caused by the FlutterEngine created by the workmanager library. In fact, it arises from the mixed use of static resources (mediaBrowser/mediaController) and instance callbacks (connectionCallback/applicationContext) in the audio_service library, which leads to chaotic state management in a multi-engine environment. This issue should be reproducible.

YBill avatar Nov 03 '25 03:11 YBill