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

android.app.ForegroundServiceStartNotAllowedException - Service.startForeground() not allowed due to mAllowStartForeground false #2231

Open
AjayGol opened this issue Jan 1, 2024 · 31 comments
Labels

Comments

@AjayGol
Copy link

AjayGol commented Jan 1, 2024

Describe the Bug
Fatal Exception: java.lang.RuntimeException
Unable to start service com.doublesymmetry.trackplayer.service.MusicService@43cf99 with null: android.app.ForegroundServiceStartNotAllowedException: Service.startForeground() not allowed due to mAllowStartForeground false: service com.app.android/com.doublesymmetry.trackplayer.service.MusicService

Steps To Reproduce
TBH it did not happen to me on debug, but it seems that is happening to my users in production. I have reports from Firebase Crashlytics.

Screenshot 2023-12-31 at 9 30 58 AM

Screenshot 2023-12-31 at 9 31 19 AM

OS: macOS 13.5.2
CPU: (8) arm64 Apple M1
react-native: ^0.68.0 => ^0.68.0
xCode: 15(15A240d)/usr/bin/xcodebuild

Using react-native-track-player@^4.0.1

This is happen on real devices(All Google phone), most of them android 14.

@AjayGol AjayGol added the Bug label Jan 1, 2024
@mihaibulic2
Copy link

i'm also experiencing this issue

@lovegaoshi
Copy link
Contributor

duplicate of #2184
try disabling battery optimization

@mihaibulic2
Copy link

We can't just ask/expect users to disable battery optimizations, it's our problem as the devs, not the user's

@lovegaoshi
Copy link
Contributor

this is a UX improvement set by google since android12. as much as youd like to call this a dev problem unfortunately there aint many options other than listed here:
https://developer.android.com/develop/background-work/services/foreground-services#background-start-restriction-exemptions

realistically i see 4 options:

SYSTEM_ALERT_WINDOW
battery opt off
exact alarm
worker

given the flutter folks recommended the first two and didnt implement the latter two, i dont have high hopes these will turn out working right, but if u do pls submit a pr

@justinhandley
Copy link
Contributor

I posted a solution to this here: #2198 - I'm trying to see if we can work it into a patch for RNTP this week - all the code is there that keeps the app awake while audio is playing, but right now it is just app changes and not a pluggable part of RNTP.

@lovegaoshi
Copy link
Contributor

lovegaoshi commented Jan 4, 2024 via email

@NadeemKhanFh
Copy link

Hello, anyone managed to fix this issue, we are also experiencing this issue on production.

@justinhandley
Copy link
Contributor

It isn't simple, but I believe that it is related to this: #2198 - the workaround I put in place there keeps the device from sleeping while audio is playing - it does use more battery, but it gets away from having to ask people toadjust their battery settings. We are in production and are no longer seeing this error.

@MenamAfzaal
Copy link

Hi...
i'm also experiencing this issue and Firebase Crashlytics 100% stats shows this happens when app is in background state

@lovegaoshi
Copy link
Contributor

okay, I'm willing to investigate further, there's clearly something wrong with how the MusicService is started. I'm not an android expert but if I just observe what's in dev options -> running services:
apparently the MusicService is only started or at least observed when the playback is currently in play for RNTP; you can see how youtube or any other music apps will sometimes only show 1 process but 0 service, and the service will kick in when the playback is started. IMO the service should linger because the notification still exists, but it seems to get killed randomly. when that happened and once playback is triggered again, service will attempt to onStartCommand and StartForeground which presumably triggers this error when the app is in cached process.
The most obvious way to prevent this is to keep process alive, not cached, I'm seeing this behavior for AIMP and youtube (revanced) as their process never goes into caching. I dunno how realistically this applies to RN apps however, especially since I made terrible RN apps that are battery hogs. outside that one can disable battery optimization that the running process will never go into cache as well, which has been suggested in this thread and before.
TBH I'm very perplexed by when a service would be registered or not, I couldnt get podverse to show up at all, AIMP and YTB doesnt show for a while then I was able to see services attached to them...

@mihaibulic2
Copy link

Thanks for offering to investigate — I was hoping to look more into it too, but I've been busy with work / it doesn't look like I'll be getting free time any time soon. But If anayone has figured a solid way to repro, I'm happy to try out any PRs.

@lovegaoshi
Copy link
Contributor

ok. For those interested:

enable dev options and watch the RNTP app in the running state and cache state. When playing the process should have musicService attached. when the app goes into the cache state,

notification posted with id=1, ongoing=false
Notification has been stopped

will be triggered and foreground service is therefore lost. when the app is resumed, the active foreground service is no longer attached to the activity, or there's something buggy about this that AppKilledBehavior will no longer connect to the current MusicService; now swiping to close app will not kill playback if configured.

Can MusicService even be kept as foreground when app goes into cached? it doesnt seem like so

Also I have my app on unrestricted battery for a good 5 hours, and it used ~1% battery and ~250MB RAM; AIMP on the other hand used 80MB RAM and ~0.5% battery. I dont think its unrealistic to ask for unrestricted battery use.

@lovegaoshi
Copy link
Contributor

if anyone can disable stopForeground like this
lovegaoshi@9afe35b
and report back findings it would be nice. I cannot reproduce the error message what so ever

@chrisfougner
Copy link

We saw a spike in this crash starting yesterday. They're all Android 14 on Google Pixel devices (6a and 8). Not sure exactly how to create a minimal reproducible sample, but maybe see if it's easier to reproduce on one of those devices if you have one available.

@lovegaoshi
Copy link
Contributor

ur welcome to try
1145e2d
for android 14 though i doubt it matters. unless someone can reliably reproduce this error there aint much to do other than not stopping tje foreground service to begin with

@iphonic
Copy link

iphonic commented Feb 9, 2024

@dcvz @lovegaoshi Anything on this seems many users getting affected, today only 5 users have the same issue.

@iphonic
Copy link

iphonic commented Feb 9, 2024

ur welcome to try 1145e2d for android 14 though i doubt it matters. unless someone can reliably reproduce this error there aint much to do other than not stopping tje foreground service to begin with

It is happening in Android 14 too, see attached, I can give you log
Screenshot 2024-02-09 at 1 44 02 PM

@lovegaoshi
Copy link
Contributor

you're triggering this via setupPlayer? so this happens at app start up?

@iphonic
Copy link

iphonic commented Feb 9, 2024

you're triggering this via setupPlayer? so this happens at app start up?

As per the stacktrace and the session log we have, it doesn't crash at startup, but when it tries to play audio for the first time

Fatal Exception: android.app.ForegroundServiceStartNotAllowedException: startForegroundService() not allowed due to mAllowStartForeground false: service com.hellomind/com.doublesymmetry.trackplayer.service.MusicService
at android.app.ForegroundServiceStartNotAllowedException$1.createFromParcel(ForegroundServiceStartNotAllowedException.java:54)
at android.app.ForegroundServiceStartNotAllowedException$1.createFromParcel(ForegroundServiceStartNotAllowedException.java:50)
at android.os.Parcel.readParcelableInternal(Parcel.java:4882)
at android.os.Parcel.readParcelable(Parcel.java:4864)
at android.os.Parcel.createExceptionOrNull(Parcel.java:3064)
at android.os.Parcel.createException(Parcel.java:3053)
at android.os.Parcel.readException(Parcel.java:3036)
at android.os.Parcel.readException(Parcel.java:2978)
at android.app.IActivityManager$Stub$Proxy.startService(IActivityManager.java:6437)
at android.app.ContextImpl.startServiceCommon(ContextImpl.java:2002)
at android.app.ContextImpl.startForegroundService(ContextImpl.java:1967)
at android.content.ContextWrapper.startForegroundService(ContextWrapper.java:847)
at android.content.ContextWrapper.startForegroundService(ContextWrapper.java:847)
at com.doublesymmetry.trackplayer.module.MusicModule.setupPlayer(MusicModule.kt:239)
at java.lang.reflect.Method.invoke(Method.java)
at com.facebook.react.bridge.JavaMethodWrapper.invoke(JavaMethodWrapper.java:372)
at com.facebook.react.bridge.JavaModuleWrapper.invoke(JavaModuleWrapper.java:188)
at com.facebook.jni.NativeRunnable.run(NativeRunnable.java)
at android.os.Handler.handleCallback(Handler.java:958)
at android.os.Handler.dispatchMessage(Handler.java:99)
at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:27)
at android.os.Looper.loopOnce(Looper.java:230)
at android.os.Looper.loop(Looper.java:319)
at com.facebook.react.bridge.queue.MessageQueueThreadImpl$4.run(MessageQueueThreadImpl.java:228)

@lovegaoshi
Copy link
Contributor

the trace log specifically occurs at MM.setupPlayer which is the RN module, where TP.setupPlayer calls it, not when audio is first played; do you use await TP.setupPlayer? and why is your app no longer in foreground when the first audio plays?

@iphonic
Copy link

iphonic commented Feb 9, 2024

the trace log specifically occurs at MM.setupPlayer which is the RN module, where TP.setupPlayer calls it, not when audio is first played; do you use await TP.setupPlayer? and why is your app no longer in foreground when the first audio plays?

Yes using await TrackPlayer.setupPlayer(); The app is in Foreground, audio can't be started from the background.

So basically the setup is like await TrackPlayer.setupPlayer(); is defined in App.js in componentDidMount() and the Audio plays at a later stage on a different screen having a list of sessions. However I can't replicate any crash in Android 12, it seems happening in Android 14, works fine in iOS.

@lovegaoshi
Copy link
Contributor

so your app reliably crushes with this exception on android 14?
is your component rerendered somehow and triggering componentDidMount again? if you console.log within that is it called twice?
and if you insert a timber.d right at context.startforegroundservice(intent) (MM L239) is this called twice? if you also put a console.log inside TrackPlayer.setupPlayer is that called twice too?

@iphonic
Copy link

iphonic commented Feb 9, 2024

so your app reliably crushes with this exception on android 14? is your component rerendered somehow and triggering componentDidMount again? if you console.log within that is it called twice? and if you insert a timber.d right at context.startforegroundservice(intent) (MM L239) is this called twice? if you also put a console.log inside TrackPlayer.setupPlayer is that called twice too?

No, it doesn't get called twice.

@lovegaoshi
Copy link
Contributor

then when is your TP.setupPlayer called? if its truly on component render your app cannot be in background at all

@iphonic
Copy link

iphonic commented Feb 9, 2024

then when is your TP.setupPlayer called? if its truly on component render your app cannot be in background at all

The app can play Audio in the background but once the Audio has been started, not before.

@lovegaoshi
Copy link
Contributor

lovegaoshi commented Feb 9, 2024

wdym play audio in the background, via the notification panel?
i'm still not clear why/how youre triggering TP.setupPlayer. if you put a console.log within TP.setupPlayer theoretically you should see it once upon app start, thats it. since youre saying it doesnt fire twice but this is observed with the traceback, something doesnt add up

@iphonic
Copy link

iphonic commented Feb 10, 2024

wdym play audio in the background, via the notification panel? i'm still not clear why/how youre triggering TP.setupPlayer. if you put a console.log within TP.setupPlayer theoretically you should see it once upon app start, thats it. since youre saying it doesnt fire twice but this is observed with the traceback, something doesnt add up

I am breaking configurations in steps as follows

Step1
index.js <=== Entry point of the Application

import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';
AppRegistry.registerComponent(appName, () => App);

Step2
Inside App.js

import TrackPlayer from 'react-native-track-player';
import {PlaybackService} from './playbackservice';

TrackPlayer.registerPlaybackService(() => PlaybackService);

export default class App extends Component {
  constructor(props) {
     super(props);
   }
  async componentDidMount() { <=== This is called once in the whole lifecycle of the Application
      await TrackPlayer.setupPlayer();
  }
  render(){
     ...
   }
}

Step3
In a Different screen say ScreenA.js on button click

 async onPlayAction = (e) => { 
       await TrackPlayer.reset();
       await TrackPlayer.add(tracks);
       await TrackPlayer.play();
 }

Let me know where I am doing wrong? Thanks.

@iphonic
Copy link

iphonic commented Apr 5, 2024

wdym play audio in the background, via the notification panel? i'm still not clear why/how youre triggering TP.setupPlayer. if you put a console.log within TP.setupPlayer theoretically you should see it once upon app start, thats it. since youre saying it doesnt fire twice but this is observed with the traceback, something doesnt add up

I am breaking configurations in steps as follows

Step1 index.js <=== Entry point of the Application

import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';
AppRegistry.registerComponent(appName, () => App);

Step2 Inside App.js

import TrackPlayer from 'react-native-track-player';
import {PlaybackService} from './playbackservice';

TrackPlayer.registerPlaybackService(() => PlaybackService);

export default class App extends Component {
  constructor(props) {
     super(props);
   }
  async componentDidMount() { <=== This is called once in the whole lifecycle of the Application
      await TrackPlayer.setupPlayer();
  }
  render(){
     ...
   }
}

Step3 In a Different screen say ScreenA.js on button click

 async onPlayAction = (e) => { 
       await TrackPlayer.reset();
       await TrackPlayer.add(tracks);
       await TrackPlayer.play();
 }

Let me know where I am doing wrong? Thanks.

@lovegaoshi Anything on above, the issue still exist? Thanks.

@lovegaoshi
Copy link
Contributor

lovegaoshi commented Apr 5, 2024 via email

@taromorimoto
Copy link

Has anyone found a way to fix or somehow get around this error? I'm seeing these identical errors inPlay Store and Firebase Crashlytics for Android 14. Using react-native-track-player 4.1.1.

@devartwa
Copy link

Has anyone found a way to fix or somehow get around this error? I'm seeing these identical errors inPlay Store and Firebase Crashlytics for Android 14. Using react-native-track-player 4.1.1.

Same here!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

10 participants