ryanheise / audio_service

Flutter plugin to play audio in the background while the screen is off.
806 stars 481 forks source link

`androidResumeOnClick` false does nothing #638

Open nt4f04uNd opened 3 years ago

nt4f04uNd commented 3 years ago

Which API doesn't behave as documented, and how does it misbehave? as titled

Minimal reproduction project example project with androidResumeOnClick set to false

To Reproduce (i.e. user steps, not code) send hook key like this

 adb shell input keyevent 79

Error messages no

Expected behavior should not resume (and by the way because notification buttons are handled via the MediaButtonReceiver, the notification should become unaccessible too)

Screenshots no

Runtime Environment (please complete the following information if relevant): see flutter doctor

Flutter SDK version

flutter doctor -v ``` [✓] Flutter (Channel master, 2.1.0-13.0.pre.386, on Microsoft Windows [Version 10.0.19041.867], locale ru-RU) • Flutter version 2.1.0-13.0.pre.386 at c:\dev\src\flutter • Framework revision 66af44c9fe (54 minutes ago), 2021-04-01 10:22:37 -0700 • Engine revision 9651d11fe8 • Dart version 2.13.0 (build 2.13.0-190.0.dev) [✓] Android toolchain - develop for Android devices (Android SDK version 30.0.2) • Android SDK at C:\Users\danya\AppData\Local\Android\sdk • Platform android-30, build-tools 30.0.2 • Java binary at: C:\Program Files\Android\Android Studio\jre\bin\java • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b01) • All Android licenses accepted. [✓] Chrome - develop for the web • Chrome at C:\Program Files\Google\Chrome\Application\chrome.exe [✓] Visual Studio - develop for Windows (Visual Studio Community 2019 16.7.7) • Visual Studio at C:\Program Files (x86)\Microsoft Visual Studio\2019\Community • Visual Studio Community 2019 version 16.7.30621.155 • Windows 10 SDK version 10.0.19041.0 [✓] Android Studio (version 4.0) • Android Studio at C:\Program Files\Android\Android Studio • Flutter plugin version 50.0.1 • Dart plugin version 193.7547 • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b01) [✓] IntelliJ IDEA Community Edition (version 2020.3) • IntelliJ at C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2020.3.3 • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart [✓] VS Code (version 1.55.0) • VS Code at C:\Users\danya\AppData\Local\Programs\Microsoft VS Code • Flutter extension version 3.21.0 [✓] Connected device (3 available) • Windows (desktop) • windows • windows-x64 • Microsoft Windows [Version 10.0.19041.867] • Chrome (web) • chrome • web-javascript • Google Chrome 89.0.4389.90 • Edge (web) • edge • web-javascript • Microsoft Edge 89.0.774.63 • No issues found! ```

Additional context i think this option should be removed, since we always can override click to achieve the same effect

ryanheise commented 3 years ago

I think it did do something on older version of Android, where if the media session is no longer active and the user clicks the media button, then the OS will route the click through to the app. But simply overriding click is not enough because the click won't be routed through unless we register with the OS that we want it to be in this inactive state. Things have definitely changed in Android 11 and I don't have an easy way of testing this anymore but I would probably wait a bit before phasing this out.

nt4f04uNd commented 3 years ago

android 9 - does nothing androrid q and 11 same

nt4f04uNd commented 3 years ago

But simply overriding click is not enough because the click won't be routed through unless we register with the OS that we want it to be in this inactive state

i believe you gotta drop the media session or something like that to do so. agree this is not quite important for now

ryanheise commented 3 years ago

Some relevant information:

https://developer.android.com/guide/topics/media-apps/mediabuttons#restarting-inactive-mediasessions

kmod-midori commented 3 years ago

Yeah, it seems that my application is indeed being restarted in the background when a media button on the headphone is pressed (this is Android 10, not 11),while the app is not in the task list, without the notification. I use my app as a daily driver and I have noticed this multiple times. Is it somehow related?

ryanheise commented 3 years ago

@chengyuhui it could be interesting to experiment with that option to see if it does anything, but from memory I didn't have much success with it when I originally implemented that code for the old version of audio_service which ran the background audio task in a separate isolate. In the multi-isolate approach there were technical roadblocks preventing me from relaunching the background FlutterEngine to wake up the media session anyway, which is part of the reason for this new one-isolate design. But this new one-isolate design also coincided with the release of Android 11, and having upgraded my device, that's really now the only Android OS version I'm testing.

If people still need to test Android 10, I may rely on those of you who use it to report whether it supports this feature, and perhaps also contribute pull requests to fix any Android 10 behaviours.

kmod-midori commented 3 years ago

Just caught this on my debugging app:

04-20 22:28:41.319  1947  8533 D MediaSessionService: Sending KeyEvent { action=ACTION_DOWN, keyCode=KEYCODE_MEDIA_PLAY, scanCode=0, metaState=0, flags=0x0, repeatCount=0, eventTime=0, downTime=0, deviceId=-1, source=0x0, displayId=0 } to the last known PendingIntent PendingIntent{4961a06: PendingIntentRecord{67b18c7 com.santiego.vtbmusic.debug broadcastIntent}}
04-20 22:28:41.321  8071  8436 V Avrcp_ext: Exit handleMessage
04-20 22:28:41.322  8071  8071 V Avrcp_ext: recordKeyDispatched: KeyEvent { action=ACTION_DOWN, keyCode=KEYCODE_MEDIA_PLAY, scanCode=0, metaState=0, flags=0x0, repeatCount=0, eventTime=0, downTime=0, deviceId=-1, source=0x0, displayId=0 } dispatched to com.santiego.vtbmusic.debug
04-20 22:28:41.322  8071  8071 V Avrcp_ext: Player 8 package: com.santiego.vtbmusic.debug
04-20 22:28:41.322  8071  8071 W Avrcp_ext: Trigger setAddressedMediaSessionPkg recordKeyDispatchcom.santiego.vtbmusic.debug
04-20 22:28:41.344  8071  8163 W bt_btif : handle_rc_passthrough_cmd: Send fake play release command
04-20 22:28:41.347  8071  8163 D Avrcp_ext: Enter handlePassthroughCmdRequestFromNative
04-20 22:28:41.349  8071  8163 D Avrcp_ext: Exit handlePassthroughCmdRequestFromNative
04-20 22:28:41.351  8071  8163 D bt_btif : btif_av_state_opened_handler event:BTA_AV_REMOTE_CMD_EVT flags 0 peer_sep 1 and index 0 reconfig_event: 0
04-20 22:28:41.352  8071  8163 D bt_btif : btif_rc_handler: event: BTA_AV_REMOTE_CMD_EVT
04-20 22:28:41.352  8071  8163 W bt_btif : BTHF: btif_hf_check_if_sco_connected(): No SCO connection up
04-20 22:28:41.352  8071  8163 W bt_btif : BTHF: btif_hf_check_if_sco_connected(): No SCO connection up
04-20 22:28:41.352  8071  8163 W bt_btif : handle_rc_passthrough_cmd: Ignore release command
04-20 22:28:41.355  8071  8436 V Avrcp_ext: AvrcpMessageHandler: received message=13
04-20 22:28:41.356  8071  8436 V Avrcp_ext: MSG_NATIVE_REQ_PASS_THROUGH: id=68 st=0
04-20 22:28:41.359  8071  8436 V Avrcp_ext: MSG_NATIVE_REQ_PASS_THROUGH 9E:BF:8C:9F:DC:EC
04-20 22:28:41.360  8071  8436 I Avrcp_ext: device found at index 0
04-20 22:28:41.361  8071  8436 D Avrcp_ext: passthrough from device: 9E:BF:8C:9F:DC:EC
04-20 22:28:41.363   805  8320 I vendor.qti.bluetooth@1.0-ibs_handler: ProcessIbsCmd: Received IBS_SLEEP_IND: 0xFE
04-20 22:28:41.364  8071  8436 D Avrcp_ext: Active device: 9E:BF:8C:9F:DC:EC
04-20 22:28:41.367  8071  8436 D Avrcp_ext: ignore_play: false
04-20 22:28:41.368  8071  8436 D Avrcp_ext: Avrcp current play state: 2 isMusicActive: false A2dp state: 11 Cached passthrough command: 126
04-20 22:28:41.369  8071  8436 D BATService: getBATService(): service is NULL
04-20 22:28:41.370  8071  8436 D Avrcp_ext: cached passthrough: 126current passthrough: 126
04-20 22:28:41.372  1947  8533 D MediaSessionService: Sending KeyEvent { action=ACTION_UP, keyCode=KEYCODE_MEDIA_PLAY, scanCode=0, metaState=0, flags=0x0, repeatCount=0, eventTime=0, downTime=0, deviceId=-1, source=0x0, displayId=0 } to the last known PendingIntent PendingIntent{4961a06: PendingIntentRecord{67b18c7 com.santiego.vtbmusic.debug broadcastIntent}}
04-20 22:28:41.380  8071  8436 V Avrcp_ext: Exit handleMessage
04-20 22:28:41.384  8071  8071 V Avrcp_ext: recordKeyDispatched: KeyEvent { action=ACTION_UP, keyCode=KEYCODE_MEDIA_PLAY, scanCode=0, metaState=0, flags=0x0, repeatCount=0, eventTime=0, downTime=0, deviceId=-1, source=0x0, displayId=0 } dispatched to com.santiego.vtbmusic.debug
04-20 22:28:41.384  8071  8071 V Avrcp_ext: Player 8 package: com.santiego.vtbmusic.debug
04-20 22:28:41.384  8071  8071 W Avrcp_ext: Trigger setAddressedMediaSessionPkg recordKeyDispatchcom.santiego.vtbmusic.debug
04-20 22:28:41.398   705   705 D Zygote  : Forked child process 19261
04-20 22:28:41.400   704 29185 D OemNetd : setPidForPackage: packageName=com.santiego.vtbmusic.debug, pid=19261, pid=10414
04-20 22:28:41.400  1947  2063 I ActivityManager: Start proc 19261:com.santiego.vtbmusic.debug/u0a414 for broadcast {com.santiego.vtbmusic.debug/com.ryanheise.audioservice.MediaButtonReceiver} caller=com.santiego.vtbmusic.debug
04-20 22:28:41.424 19261 19261 I .vtbmusic.debu: Late-enabling -Xcheck:jni
04-20 22:28:41.470 19261 19261 E .vtbmusic.debu: Unknown bits set in runtime_flags: 0x8000
04-20 22:28:41.512  1947  8533 D CompatibilityInfo: mCompatibilityFlags - 0
04-20 22:28:41.512  1947  8533 D CompatibilityInfo: applicationDensity - 440
04-20 22:28:41.512  1947  8533 D CompatibilityInfo: applicationScale - 1.0
04-20 22:28:41.516  1947  8533 D CompatibilityInfo: mCompatibilityFlags - 0
04-20 22:28:41.516  1947  8533 D CompatibilityInfo: applicationDensity - 440
04-20 22:28:41.516  1947  8533 D CompatibilityInfo: applicationScale - 1.0
04-20 22:28:41.519  1947  2356 I MiuiNetworkPolicy: updateUidState uid = 10414, uidState = 12
04-20 22:28:41.651 19261 19261 I Perf    : Connecting to perf service.
04-20 22:28:41.723 19261 19261 I FeatureParser: can't find polaris.xml in assets/device_features/,it may be in /system/etc/device_features
04-20 22:28:41.749 19261 19261 E libc    : Access denied finding property "ro.vendor.df.effect.conflict"
04-20 22:28:41.750 19261 19261 E libc    : Access denied finding property "ro.vendor.knock.type"
04-20 22:28:41.771 19261 19261 D MediaBrowserCompat: Connecting to a MediaBrowserService.
04-20 22:28:41.775   539   539 E SELinux : avc:  denied  { find } for interface=vendor.qti.hardware.servicetracker::IServicetracker sid=u:r:system_server:s0 pid=1947 scontext=u:r:system_server:s0 tcontext=u:object_r:default_android_hwservice:s0 tclass=hwservice_manager permissive=0
04-20 22:28:41.781  1947  8533 D CompatibilityInfo: mCompatibilityFlags - 0
04-20 22:28:41.781  1947  8533 D CompatibilityInfo: applicationDensity - 440
04-20 22:28:41.781  1947  8533 D CompatibilityInfo: applicationScale - 1.0
04-20 22:28:41.782   539   539 E SELinux : avc:  denied  { find } for interface=vendor.qti.hardware.servicetracker::IServicetracker sid=u:r:system_server:s0 pid=1947 scontext=u:r:system_server:s0 tcontext=u:object_r:default_android_hwservice:s0 tclass=hwservice_manager permissive=0
04-20 22:28:41.793 19261 19261 I System.out: ### onCreate
04-20 22:28:41.827 19261 19283 E Perf    : Fail to get file list com.santiego.vtbmusic.debug
04-20 22:28:41.829 19261 19283 E Perf    : getFolderSize() : Exception_1 = java.lang.NullPointerException: Attempt to get length of null array
04-20 22:28:41.829 19261 19283 E Perf    : Fail to get file list oat
04-20 22:28:41.829 19261 19283 E Perf    : getFolderSize() : Exception_1 = java.lang.NullPointerException: Attempt to get length of null array
04-20 22:28:41.834  1947  8533 D MediaSessionService: Media button session is changed to com.santiego.vtbmusic.debug/media-session (userId=0)
04-20 22:28:41.835  8071  8071 V Avrcp_ext: Set active media session com.santiego.vtbmusic.debug
04-20 22:28:41.835  8071  8071 D HeadsetService: getHeadsetService(): returning com.android.bluetooth.hfp.HeadsetService@5be0acf
04-20 22:28:41.836  8071  8071 D HeadsetService:  isAudioOn: The number of audio connected devices 0
04-20 22:28:41.836  8071  8071 D HeadsetService: isScoOrCallActive(): Call Active:falseCall is Ringing:falseSCO is Active:false
04-20 22:28:41.836  8071  8071 D HeadsetService:  isAudioOn: The number of audio connected devices 0
04-20 22:28:41.836  8071  8071 W Avrcp_ext: playState object null, sending STOPPED
04-20 22:28:41.836  8071  8071 V Avrcp_ext: isBrowseSupported for com.santiego.vtbmusic.debug: true
04-20 22:28:41.837  8071  8071 D Avrcp_ext: update #8:MediaPlayerInfo com.santiego.vtbmusic.debug (as 'VtuberMusic') Type = 1, SubType = 0, Status = 0 Feature Bits [40 41 42 44 45 47 48 58 59 62 65 67 68] Controller: MediaController (com.santiego.vtbmusic.debug@e510b14) null
04-20 22:28:41.837  8071  8071 V Avrcp_ext: updateNewIds: Addressed:8 to 8, Browse:0 to 0
04-20 22:28:41.837  8071  8071 D Avrcp_ext: updateCurrentController: null to MediaController (com.santiego.vtbmusic.debug@e510b14) null
04-20 22:28:41.839  8071  8071 V Avrcp_ext: updateCurrentMediaState: mMediaController: MediaController (com.santiego.vtbmusic.debug@e510b14) null
04-20 22:28:41.840  8071  8071 V Avrcp_ext: isMusicActive: false getBluetoothPlayState: 2
04-20 22:28:41.840  8071  8071 V Avrcp_ext: updateCurrentMediaState: isPlaying = false
04-20 22:28:41.840  8071  8071 D Avrcp_ext: updateCurrentMediaState: playState is null, mAudioManagerIsPlaying=false, isMusicActive=false
04-20 22:28:41.841 19261 19261 I System.out: ### AudioService will resume on click
04-20 22:28:41.841  8071  8071 V Avrcp_ext: updateCurrentMediaState: get media attributes: 
04-20 22:28:41.842  8071  8071 I Avrcp_ext: device found at index 0
04-20 22:28:41.842  8071  8071 V Avrcp_ext: updateCurrentMediaState: addr: [B@f59ffbd
04-20 22:28:41.842  8071  8071 V Avrcp_ext: Media update: id -1➡-1? [MediaAttributes: none] : [MediaAttributes: none]
04-20 22:28:41.842  8071  8071 V Avrcp_ext: newPlayStatus:2mReportedPlayStatus:2
04-20 22:28:41.845  8071  8071 V Avrcp_ext: updatePlaybackState, state: PlaybackState {state=2, position=-1, buffered position=0, speed=0.0, updated=941659819, actions=0, custom actions=[], active item id=-1, error=null} device: null
04-20 22:28:41.846 19261 19261 I System.out: ### Creating new FlutterEngine
04-20 22:28:41.846  8071  8071 V Avrcp_ext: Device: Plus 5.0 : old state: PlaybackState {state=2, position=-1, buffered position=0, speed=0.0, updated=941006811, actions=0, custom actions=[], active item id=-1, error=null}
04-20 22:28:41.846  8071  8071 V Avrcp_ext: Device: no name: 
04-20 22:28:41.846  8071  8071 V Avrcp_ext: Device: no name: 

You can see that process 19261 is created by the system for our application from the receiver. The audio actually went on for a moment without notification before eventually being killed by the system.

ryanheise commented 3 years ago

I wonder why it got killed. Anything in the log about that? Maybe your app didn't trigger the commencement of the foreground service in time?

kmod-midori commented 3 years ago

Upon further investigation I later see this:

04-20 22:29:10.718 19261 19261 E MethodChannel#com.ryanheise.audio_service.handler.methods: java.lang.IllegalStateException: Not allowed to start service Intent { cmp=com.santiego.vtbmusic.debug/com.ryanheise.audioservice.AudioService }: app is in background uid UidRecord{b62c7c7 u0a414 CEM  idle change:cached procs:1 seq(0,0,0)}
04-20 22:29:10.718 19261 19261 E MethodChannel#com.ryanheise.audio_service.handler.methods:     at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1626)
04-20 22:29:10.718 19261 19261 E MethodChannel#com.ryanheise.audio_service.handler.methods:     at android.app.ContextImpl.startService(ContextImpl.java:1581)
04-20 22:29:10.718 19261 19261 E MethodChannel#com.ryanheise.audio_service.handler.methods:     at android.content.ContextWrapper.startService(ContextWrapper.java:679)
04-20 22:29:10.718 19261 19261 E MethodChannel#com.ryanheise.audio_service.handler.methods:     at com.ryanheise.audioservice.AudioService.enterPlayingState(AudioService.java:530)
04-20 22:29:10.718 19261 19261 E MethodChannel#com.ryanheise.audio_service.handler.methods:     at com.ryanheise.audioservice.AudioService.setState(AudioService.java:396)
04-20 22:29:10.718 19261 19261 E MethodChannel#com.ryanheise.audio_service.handler.methods:     at com.ryanheise.audioservice.AudioServicePlugin$AudioHandlerInterface.onMethodCall(AudioServicePlugin.java:840)
04-20 22:29:10.718 19261 19261 E MethodChannel#com.ryanheise.audio_service.handler.methods:     at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:233)
04-20 22:29:10.718 19261 19261 E MethodChannel#com.ryanheise.audio_service.handler.methods:     at io.flutter.embedding.engine.dart.DartMessenger.handleMessageFromDart(DartMessenger.java:85)
04-20 22:29:10.718 19261 19261 E MethodChannel#com.ryanheise.audio_service.handler.methods:     at io.flutter.embedding.engine.FlutterJNI.handlePlatformMessage(FlutterJNI.java:818)
04-20 22:29:10.718 19261 19261 E MethodChannel#com.ryanheise.audio_service.handler.methods:     at android.os.MessageQueue.nativePollOnce(Native Method)
04-20 22:29:10.718 19261 19261 E MethodChannel#com.ryanheise.audio_service.handler.methods:     at android.os.MessageQueue.next(MessageQueue.java:336)
04-20 22:29:10.718 19261 19261 E MethodChannel#com.ryanheise.audio_service.handler.methods:     at android.os.Looper.loop(Looper.java:183)
04-20 22:29:10.718 19261 19261 E MethodChannel#com.ryanheise.audio_service.handler.methods:     at android.app.ActivityThread.main(ActivityThread.java:7589)
04-20 22:29:10.718 19261 19261 E MethodChannel#com.ryanheise.audio_service.handler.methods:     at java.lang.reflect.Method.invoke(Native Method)
04-20 22:29:10.718 19261 19261 E MethodChannel#com.ryanheise.audio_service.handler.methods:     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)
04-20 22:29:10.718 19261 19261 E MethodChannel#com.ryanheise.audio_service.handler.methods:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:953)
04-20 22:29:10.734 19261 19288 E flutter : [ERROR:flutter/lib/ui/ui_dart_state.cc(186)] Unhandled Exception: PlatformException(error, Not allowed to start service Intent { cmp=com.santiego.vtbmusic.debug/com.ryanheise.audioservice.AudioService }: app is in background uid UidRecord{b62c7c7 u0a414 CEM  idle change:cached procs:1 seq(0,0,0)}, null, java.lang.IllegalStateException: Not allowed to start service Intent { cmp=com.santiego.vtbmusic.debug/com.ryanheise.audioservice.AudioService }: app is in background uid UidRecord{b62c7c7 u0a414 CEM  idle change:cached procs:1 seq(0,0,0)}
04-20 22:29:10.734 19261 19288 E flutter :  at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1626)
04-20 22:29:10.734 19261 19288 E flutter :  at android.app.ContextImpl.startService(ContextImpl.java:1581)
04-20 22:29:10.734 19261 19288 E flutter :  at android.content.ContextWrapper.startService(ContextWrapper.java:679)
04-20 22:29:10.734 19261 19288 E flutter :  at com.ryanheise.audioservice.AudioService.enterPlayingState(AudioService.java:530)
04-20 22:29:10.734 19261 19288 E flutter :  at com.ryanheise.audioservice.AudioService.setState(AudioService.java:396)
04-20 22:29:10.734 19261 19288 E flutter :  at com.ryanheise.audioservice.AudioServicePlugin$AudioHandlerInterface.onMethodCall(AudioServicePlugin.java:840)
04-20 22:29:10.734 19261 19288 E flutter :  at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:233)
04-20 22:29:10.734 19261 19288 E flutter :  at io.flutter.embedding.engine.dart.DartMessenger.handleMessageFromDart(DartMessenger.java:85)
04-20 22:29:10.734 19261 19288 E flutter :  at io.flutter.embedding.engine.FlutterJNI.handlePlatformMessage(FlutterJNI.java:818)
04-20 22:29:10.734 19261 19288 E flutter :  at android.os.MessageQueue.nativePollOnce(Native Method)
04-20 22:29:10.734 19261 19288 E flutter :  at android.os.MessageQueue.next(MessageQueue.java:336)
04-20 22:29:10.734 19261 19288 E flutter :  at android.os.Looper.loop(Looper.java:183)
04-20 22:29:10.734 19261 19288 E flutter :  at android.app.ActivityThread.main(ActivityThread.java:7589)
04-20 22:29:10.734 19261 19288 E flutter :  at java.lang.reflect.Method.invoke(Native Method)
04-20 22:29:10.734 19261 19288 E flutter :  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)
04-20 22:29:10.734 19261 19288 E flutter :  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:953)
04-20 22:29:10.734 19261 19288 E flutter : )
04-20 22:29:10.734 19261 19288 E flutter : #0      StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:581:7)
04-20 22:29:10.734 19261 19288 E flutter : #1      MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:158:18)
04-20 22:29:10.734 19261 19288 E flutter : <asynchronous suspension>
04-20 22:29:10.734 19261 19288 E flutter : #2      MethodChannelAudioService.setState (package:audio_service_platform_interface/method_channel_audio_service.dart:19:5)
04-20 22:29:10.734 19261 19288 E flutter : <asynchronous suspension>
04-20 22:29:10.734 19261 19288 E flutter : #3      AudioService.init.<anonymous closure> (package:audio_service/audio_service.dart:941:7)
04-20 22:29:10.734 19261 19288 E flutter : <asynchronous suspension>
04-20 22:29:10.734 19261 19288 E flutter : 
04-20 22:29:10.838 19261 19261 W .vtbmusic.debu: Accessing hidden method Landroid/media/AudioTrack;->getLatency()I (greylist, reflection, allowed)
04-20 22:29:10.847 19261 19261 I ExoPlayerImpl: Init b8ff390 [ExoPlayerLib/2.13.1] [polaris, MIX 2S, Xiaomi, 29]
04-20 22:29:10.864 19261 19261 D AudioManager: getStreamVolume isRestricted mode = 0

Which explains a lot, as a foreground service can not appear from nowhere.

Playing is triggered via

04-20 22:29:10.651 19261 19261 I System.out: ### onMediaButtonEvent: KeyEvent { action=ACTION_DOWN, keyCode=KEYCODE_MEDIA_PLAY, scanCode=0, metaState=0, flags=0x0, repeatCount=0, eventTime=0, downTime=0, deviceId=-1, source=0x0, displayId=0 }
04-20 22:29:10.652 19261 19261 I System.out: ### listener = com.ryanheise.audioservice.AudioServicePlugin$AudioHandlerInterface@7f44e15
04-20 22:29:10.652 19261 19261 I System.out: ### calling onClick
04-20 22:29:10.653 19261 19261 I System.out: ### sending click map: {button=0}
04-20 22:29:10.654 19261 19261 I System.out: ### called onClick
04-20 22:29:10.657 19261 19288 I flutter : ### received click: {button: 0}
04-20 22:29:10.657 19261 19288 I flutter : button value is "0"
04-20 22:29:10.657 19261 19288 I flutter : type: int
04-20 22:29:10.657 19261 19288 I flutter : ### calling handler.click(MediaButton.media)
04-20 22:29:10.657 19261 19288 I flutter : ### Play
ryanheise commented 3 years ago

OK, this has to do with starting a foreground service from the background which is something I've not implemented yet since there are some restrictions on the context from which a foreground service can be started from the background. I guess as a consequence, that is tripping up the androidResumeOnClick feature. Note that Android 11's media session resumption feature is able to start the foreground service directly which is why I think it doesn't run into the same limitation.

Anyway, in terms of fixing this, I came across this S/O post:

https://stackoverflow.com/questions/51863600/java-lang-illegalstateexception-not-allowed-to-start-service-intent-from-activ

It indicates that the service can't be started in the foreground from the activity's onCreate, it must wait until at least after onResume.

kmod-midori commented 3 years ago

I actually noticed that some requests related to my app's home page is being triggered, so something has certainly started.

However, the activity is never visible the whole time, is it ever considered foreground so that we may start services?

ryanheise commented 3 years ago

So what is the full test scenario in terms of launching the app, clicking this and that, then running into the error?

Do we know if the activity is at least created, even if not visible?

kmod-midori commented 3 years ago

It looks like only the receiver and service are started, and the activity was nowhere to be found.

ryanheise commented 3 years ago

I expect this can be fixed by using startForegroundService.

In AudioService.java:

    private boolean enterPlayingState() {
        // CHANGE THIS TO startServiceForeground
        startService(new Intent(AudioService.this, AudioService.class));
        if (!mediaSession.isActive())
            mediaSession.setActive(true);

        acquireWakeLock();
        mediaSession.setSessionActivity(contentIntent);
        internalStartForeground();
        return true;
    }

Do you want to give it a try?

kmod-midori commented 3 years ago

I will have a try tomorrow, as it's just past midnight here and I have no access to my workstation.

nt4f04uNd commented 3 years ago

why did you not use startForegroundService in the first place?

ryanheise commented 3 years ago

Historical reasons which predate the announcement of the startForegroundService API, combined with the fact that after it was introduced I didn't actually need it for my own use cases. I suppose attention to this was just waiting for someone such as yourself to come along and make it into an issue ;-)

kmod-midori commented 3 years ago

Can confirm that the service is started normally with correct notification after this change.

ryanheise commented 3 years ago

Perfect! By the way, I'm happy for you to share that fix in the form of a pull request and merge it (if you'd like to). Otherwise, I can add the code myself.

kmod-midori commented 3 years ago

I opted not to close this issue as the original problem seems to be still there.

nt4f04uNd commented 3 years ago

we should move this into onStartCommand doing this - is dangerous, since service may be started before we startForeground, which will cause a crash with startForegroundService

https://github.com/ryanheise/audio_service/blob/e52c962a1c67dccf955fab7987325cf7bd627f9b/audio_service/android/src/main/java/com/ryanheise/audioservice/AudioService.java#L545-L548

ryanheise commented 3 years ago

I don't see any harm in replacing startService by startForegroundService, all else being equal.

In the current code startService is always called before startForeground, and there are no conceivable variations where sometimes things might not be called in the right order.

nt4f04uNd commented 3 years ago

startForegroundService gets called before internalStartForeground if it happens that service's onStartCommand completes before startForeground, which we can't be sure won't happen, this wall cause a crash

there are no conceivable variations where sometimes things might not be called in the right order

i'm not sure what you mean

ryanheise commented 3 years ago

The only requirement I'm aware of is that you have an ANR's worth of time after calling startForegroundService before you must call startForeground, and we meet that requirement. I haven't read anywhere that it is tied to the completion of onStartCommand, and the uamp sample app doesn't seem to code around such a requirement:

https://github.com/android/uamp/blob/main/common/src/main/java/com/example/android/uamp/media/MusicService.kt

serhii-k commented 1 year ago

@ryanheise Sorry for touching this really old issue. I have found it from the source comments. It seems the androidResumeOnClick now should work as expected. At least on new Android devices. Can you please give your opinion on this? Maybe you have already fixed this issue.

ryanheise commented 1 year ago

I'm not sure if this option is useful on recent versions of Android since notifications associated with media sessions aren't handled by the OS as ordinary notifications, they appear in the carousel even after the media session is stopped, unless you go to the system settings to force those notifications to be cleared. So my understanding is that this is an option for older versions of Android.

The only comment I've left in the code is about whether we should be using STOP_FOREGROUND_DETACH instead of zero if we want that notification to persist as long as possible, but I'm not going to change it unless someone who is developing for an older version of Android comes with a use case to clarify how they would ideally want it to work.

serhii-k commented 1 year ago

Thank you very much for your explanation, @ryanheise!