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

What approaches are you recommending when dealing with manufacturers' specific battery optimizations? #1928

Open
ziem opened this issue Nov 29, 2024 · 9 comments
Assignees
Labels

Comments

@ziem
Copy link

ziem commented Nov 29, 2024

Hello!
We are facing some playback-related issues on Samsung devices (mostly). People are complaining that the playback stops after a couple of minutes. Some apps (like Spotify) seem to work well without any interruptions and actions from users. On the other hand, PocketCasts is displaying a snackbar with the message "Your battery restrictions may cause playback interruptions" with a "go to settings" button.

Is there any way to mimic Spotify's behavior without the need to advise users to go to battery settings? What are your recommendations when dealing with such problems?

Thanks in advance for all your comments.

@Tolriq
Copy link
Contributor

Tolriq commented Nov 29, 2024

Become very famous and have Samsung and other OEMs have special exceptions for you :)

Else do as we all do, link to settings and link to https://dontkillmyapp.com/ and slowly trying to educate users.

@NikSatyr
Copy link

We faced a similar issue and in our case the problem was the background mode of the playback service. Android battery optimization implies limiting the internet access in case your app has no foreground presence (either UI or service). So although the connectivity manager reports that the network is available for your app, the network requests will actually fail with an exception:

  1. https://developer.android.com/training/monitoring-device-state/doze-standby#restrictions
  2. https://developer.android.com/training/monitoring-device-state/doze-standby#understand_app_standby

So for us it was enough to make sure that the service is always able to be in foreground during the playback: this ensures that we are allowed to access the internet even with the battery optimization. We had some problematic logic that put the service into background on an incoming phone call, and after that we could never successfully return to the foreground. After we've fixed that, we no longer hear from the users about this issue.

So my personal recommendation would be to search for the failed network requests using the Crashlytics and determining if your playback service is in foreground.

@marcbaechinger
Copy link
Contributor

marcbaechinger commented Nov 29, 2024

We had some problematic logic that put the service into background on an incoming phone call,
and after that we could never successfully return to the foreground.

Was this because you didn't have the permission to get back to the foreground after you lost and then received audio focus back? So you played in the background when receiving the audio focus back? And now to circumvent this, you keep your service in the foreground when loosing audio focus, like with an only transient loss?

(I'm asking because I'm just curious and collecting evidence for things we should improve/think about/discuss with system health)

@NikSatyr
Copy link

@marcbaechinger yes, this is pretty much correct. let me elaborate on this scenario:

  1. We have an active playback, the service is in FG
  2. We receive a phone call; we do not rely on the default AudioFocusManager from media3, but use a slightly customized solution; so, upon receiving a call, we pause the playback, resulting in the service being moved to BG
  3. After we detect the call has ended, we issue Player#play which is supposed to bring the service to FG. However, from the system's perspective, this move to FG is initiated not by the user's action (e.g. notification, widget or Activity), so this is denied and the service stays in BG
  4. After the player runs out of loaded buffer, it fails to load anything from the network, since the Doze mode causes exceptions on network calls if there's nothing in FG (and since the service failed to move to FG, it's obviously in BG)

We also had another custom scenario with the similar implications: when encountering a network error (unrelated to the Doze mode), the default media3 logic bring the service to BG. Combined with our custom retry logic, when we restore the connection and call Player#prepare & Player#play, we are also denied the FG mode.

The remedy was to prevent going to BG for both of these cases.

@marcbaechinger
Copy link
Contributor

Thanks a lot for your explanation and confirmation. We are aware of the playback error case being problematic as well.
Very helpful to hear that evidence! Thank you!

@Tolriq
Copy link
Contributor

Tolriq commented Nov 29, 2024

It's the same issue with unplug / replug of headset that pause (so out of FGS) and can't get it back after.

Even worse when targeting Android 15 and broken AudioFocus, even with and FGS you can't request AudioFocus back.
For cases where it's the system that remove audioFocus like a call, it give it back after so less an issue, but if you give up audio focus on pause, you can't recover it unless the user press the notification/media session surface. (It's supposedly fixed in a future version, but 0 communication about it so ...)

Currently it's better to keep FGS on pause/errors for long duration to allow users to always resume properly (Quite counter productive).

@marcbaechinger marcbaechinger self-assigned this Nov 29, 2024
@tonihei
Copy link
Collaborator

tonihei commented Dec 3, 2024

What approaches are you recommending when dealing with manufacturers' specific battery optimizations?

Part of the issue is that many OEMs have their own "battery saving" mechanisms in place that try to remove apps and services that they assume are no longer required. The best way to enforce more consistency is to define the exact behavior better and enforce it via Android certification tests. There are some efforts under way to make the foreground rules for MediaSession-based services more well-defined in future Android versions, which will be accompanied by more certification tests all manufacturers need to follow. This likely won't cover any OEM-specific battery saving modes though and can't fix any existing systems, so to some extent the effect will probably always be there. And even the most well behaved system can kill an active foreground service if it is under too much memory pressure for example.

The main recommendation for apps is really:

  • Assume that the system can kill your service (for whatever reason).
    • This likely means you should store the last playback state regularly or explicitly when the service is killed.
    • If you can, opt-in to playback resumption so that the user can restart playback at any future point with media keys or via the media carousel in the system UI.

Currently it's better to keep FGS on pause/errors for long duration to allow users to always resume properly
when we restore the connection and call Player#prepare & Player#play, we are also denied the FG mode

We are planning to make a change to Media3 to automatically keep the service in foreground for a few minutes (likely 10) after entering a paused/idle/error/stopped state. This should allow apps to handle any automatic retries and makes it easier for a user to resume manually if they want to without running into super-restrictive manufacturer settings immediately.

This makes sense for a few minutes while the playback is still fresh in the mind of the user. However, it is really working as intended that your service gets stopped and killed after this grace period if there is no ongoing playback and ideally there should never be a need for you to stay in a foreground state forever even if there is no ongoing playback.

The intended flow is that all trigger actions to continue playback (e.g. media buttons, playback resumption, new connections from other apps) should give you the exemption to restart playback from the background. If you have use cases where you don't get an exemption, but you think this is a valid use case Android should support, please let us know and we should treat it as a feature request or bug and fix it explicitly. The discussion in #1815 is an example for such a case we are trying to clarify. In the end we aim to get to a state where no app should feel the need to stay in the foreground state beyond a few minutes after playback stopped or paused.

upon receiving a call, we pause the playback, resulting in the service being moved to BG (@NikSatyr )

Just wanted to address this particular case: an incoming phone call should result in a "transient" audio focus loss, even if you handle it manually. Whenever there is transient focus loss, you should just stay in a foreground state for as long as it lasts even if paused. Media3 implements this by default if you use our audio focus management, but it's definitely the expected app behavior to do the same even if you handle audio focus manually. So what you labelled as a "remedy" is in fact the expected and right solution for this case.

@ziem
Copy link
Author

ziem commented Dec 13, 2024

We store the progress so resuming it is not a problem. In our case, the problem is that some of our users report that the playback stops every X minutes when they listen. This happens mostly on Samsung devices.

But, we have investigated the issue, and it is indeed caused by Samsung's battery optimizations. We are following @Tolriq's advice to link to the settings and educate our users.

In addition, we don't do anything special in the service extending the MediaSessionService class, but any improvements addressing this or similar issues are welcomed. Maybe you could expose a callback with the information that the background playback is limited or optimizations enabled and thus can cause playback interruptions. I'm just not sure if it's a media3 library responsibility.

@tonihei
Copy link
Collaborator

tonihei commented Dec 13, 2024

Maybe you could expose a callback with the information that the background playback is limited or optimizations enabled and thus can cause playback interruptions. I'm just not sure if it's a media3 library responsibility.

That's a more generic Android question. Sounds like PowerManager isPowerSaveMode (to detect if the mode is enabled at the moment) and isIgnoringBatteryOptimizations (to detect if your app is exempted by the user) might be useful, but for more details it's better to check other generic help sites like StackOverflow etc.

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

6 participants