diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaNotificationManager.java b/libraries/session/src/main/java/androidx/media3/session/MediaNotificationManager.java index 149badad18..0845d662cd 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaNotificationManager.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaNotificationManager.java @@ -57,7 +57,6 @@ import java.util.concurrent.TimeoutException; private static final String TAG = "MediaNtfMng"; private static final int MSG_USER_ENGAGED_TIMEOUT = 1; - private static final long USER_ENGAGED_TIMEOUT_MS = 600_000; private final MediaSessionService mediaSessionService; private final MediaNotification.Provider mediaNotificationProvider; @@ -73,6 +72,7 @@ import java.util.concurrent.TimeoutException; private boolean startedInForeground; private boolean isUserEngaged; private boolean isUserEngagedTimeoutEnabled; + private long userEngagedTimeoutMs; public MediaNotificationManager( MediaSessionService mediaSessionService, @@ -88,6 +88,7 @@ import java.util.concurrent.TimeoutException; controllerMap = new HashMap<>(); startedInForeground = false; isUserEngagedTimeoutEnabled = true; + userEngagedTimeoutMs = MediaSessionService.DEFAULT_FOREGROUND_SERVICE_TIMEOUT_MS; } public void addSession(MediaSession session) { @@ -191,6 +192,10 @@ import java.util.concurrent.TimeoutException; return startedInForeground; } + public void setUserEngagedTimeoutMs(long userEngagedTimeoutMs) { + this.userEngagedTimeoutMs = userEngagedTimeoutMs; + } + @Override public boolean handleMessage(Message msg) { if (msg.what == MSG_USER_ENGAGED_TIMEOUT) { @@ -206,8 +211,9 @@ import java.util.concurrent.TimeoutException; /* package */ boolean shouldRunInForeground(boolean startInForegroundWhenPaused) { boolean isUserEngaged = isAnySessionUserEngaged(startInForegroundWhenPaused); - if (this.isUserEngaged && !isUserEngaged && isUserEngagedTimeoutEnabled) { - mainHandler.sendEmptyMessageDelayed(MSG_USER_ENGAGED_TIMEOUT, USER_ENGAGED_TIMEOUT_MS); + boolean useTimeout = isUserEngagedTimeoutEnabled && userEngagedTimeoutMs > 0; + if (this.isUserEngaged && !isUserEngaged && useTimeout) { + mainHandler.sendEmptyMessageDelayed(MSG_USER_ENGAGED_TIMEOUT, userEngagedTimeoutMs); } else if (isUserEngaged) { mainHandler.removeMessages(MSG_USER_ENGAGED_TIMEOUT); } diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java index 91d7323c89..83ebd4ad05 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java @@ -159,6 +159,12 @@ public abstract class MediaSessionService extends Service { /** The action for {@link Intent} filter that must be declared by the service. */ public static final String SERVICE_INTERFACE = "androidx.media3.session.MediaSessionService"; + /** + * The default timeout for a session to stay in a foreground service state after it paused, + * stopped, failed or ended. + */ + @UnstableApi public static final long DEFAULT_FOREGROUND_SERVICE_TIMEOUT_MS = 600_000; + private static final String TAG = "MSessionService"; private final Object lock; @@ -471,6 +477,27 @@ public abstract class MediaSessionService extends Service { /* maxCommandsForMediaItems= */ 0); } + /** + * Sets the timeout for a session to stay in a foreground service state after it paused, stopped, + * failed or ended. + * + *

Has no effect on already running timeouts. + * + *

The default and maximum value is {@link #DEFAULT_FOREGROUND_SERVICE_TIMEOUT_MS}. If a larger + * value is provided, it will be clamped down to {@link #DEFAULT_FOREGROUND_SERVICE_TIMEOUT_MS}. + * + * @param foregroundServiceTimeoutMs The timeout in milliseconds. + */ + @UnstableApi + public final void setForegroundServiceTimeoutMs(long foregroundServiceTimeoutMs) { + getMediaNotificationManager() + .setUserEngagedTimeoutMs( + Util.constrainValue( + foregroundServiceTimeoutMs, + /* min= */ 0, + /* max= */ DEFAULT_FOREGROUND_SERVICE_TIMEOUT_MS)); + } + /** * Returns whether there is a session with ongoing user-engaged playback that is run in a * foreground service. @@ -478,9 +505,10 @@ public abstract class MediaSessionService extends Service { *

It is only possible to terminate the service with {@link #stopSelf()} if this method returns * {@code false}. * - *

Note that sessions are kept in foreground and this method returns {@code true} for a period - * of 10 minutes after they paused, stopped or failed. Use {@link #pauseAllPlayersAndStopSelf()} - * to pause all ongoing playbacks immediately and terminate the service. + *

Note that sessions are kept in foreground and this method returns {@code true} for the + * {@linkplain #setForegroundServiceTimeoutMs foreground service timeout} after they paused, + * stopped, failed or ended. Use {@link #pauseAllPlayersAndStopSelf()} to pause all ongoing + * playbacks immediately and terminate the service. */ @UnstableApi public boolean isPlaybackOngoing() {