From 64a5068faf939c8e0d39b6be220d066977f67c42 Mon Sep 17 00:00:00 2001 From: nift4 Date: Thu, 9 Oct 2025 12:44:10 +0200 Subject: [PATCH 1/2] Add action to startSelfIntent to easily distinguish it as app I was debugging start commands I shouldn't be recieving, and the startSelfIntent with empty action was sort of confusing because it wasn't immediately obvious where it came from. Adding a unique action string doesn't have any ill effects and allows easily identifying these intents in onStartCommand(). --- .../media3/session/DefaultActionFactory.java | 5 +- .../session/MediaNotificationManager.java | 1 + .../media3/session/MediaSessionService.java | 48 +++++++++++++++++-- 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/libraries/session/src/main/java/androidx/media3/session/DefaultActionFactory.java b/libraries/session/src/main/java/androidx/media3/session/DefaultActionFactory.java index 5ef08f3bf5a..db5f9d7da84 100644 --- a/libraries/session/src/main/java/androidx/media3/session/DefaultActionFactory.java +++ b/libraries/session/src/main/java/androidx/media3/session/DefaultActionFactory.java @@ -48,7 +48,6 @@ /** The default {@link MediaNotification.ActionFactory}. */ /* package */ final class DefaultActionFactory implements MediaNotification.ActionFactory { - private static final String ACTION_CUSTOM = "androidx.media3.session.CUSTOM_NOTIFICATION_ACTION"; private static final String EXTRAS_KEY_ACTION_CUSTOM = "androidx.media3.session.EXTRAS_KEY_CUSTOM_NOTIFICATION_ACTION"; public static final String EXTRAS_KEY_ACTION_CUSTOM_EXTRAS = @@ -162,7 +161,7 @@ private int toKeyCode(@Player.Command long action) { @SuppressWarnings("PendingIntentMutability") // We can't use SaferPendingIntent private PendingIntent createCustomActionPendingIntent( MediaSession mediaSession, String action, Bundle extras) { - Intent intent = new Intent(ACTION_CUSTOM); + Intent intent = new Intent(MediaSessionService.ACTION_CUSTOM_NOTIFICATION_ACTION); intent.setData(mediaSession.getImpl().getUri()); intent.setComponent(new ComponentName(service, service.getClass())); intent.putExtra(EXTRAS_KEY_ACTION_CUSTOM, action); @@ -182,7 +181,7 @@ public boolean isMediaAction(Intent intent) { /** Returns whether {@code intent} was part of a {@link #createCustomAction custom action }. */ public boolean isCustomAction(Intent intent) { - return ACTION_CUSTOM.equals(intent.getAction()); + return MediaSessionService.ACTION_CUSTOM_NOTIFICATION_ACTION.equals(intent.getAction()); } /** 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 ee4ced00a7d..371623c0792 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaNotificationManager.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaNotificationManager.java @@ -91,6 +91,7 @@ public MediaNotificationManager( mainHandler = Util.createHandler(Looper.getMainLooper(), /* callback= */ this); mainExecutor = (runnable) -> Util.postOrRun(mainHandler, runnable); startSelfIntent = new Intent(mediaSessionService, mediaSessionService.getClass()); + startSelfIntent.setAction(MediaSessionService.ACTION_START_SELF); controllerMap = new HashMap<>(); startedInForeground = false; isUserEngagedTimeoutEnabled = true; 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 b1522774d01..fa3af2ba572 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java @@ -166,6 +166,39 @@ default void onForegroundServiceStartNotAllowedException() {} /** The action for {@link Intent} filter that must be declared by the service. */ public static final String SERVICE_INTERFACE = "androidx.media3.session.MediaSessionService"; + /** + * The action of an {@link Intent} sent to this service by itself when it is already running, but + * wishes to move into the foreground state. + * + *

This action is not intended for starting the service from anywhere except custom foreground + * service handling. Most apps will never need to send this action themselves. + * + *

If an {@link Intent} with this action is received in a subclass' {@link + * #onStartCommand(Intent, int, int)} method, the intent should be passed to the superclass + * (MediaSessionService) implementation. No further action is required to handle it. + */ + @UnstableApi + public static final String ACTION_START_SELF = + "androidx.media3.session.MediaSessionService.ACTION_START_SELF"; + + /** + * The action of an {@link Intent} sent to this service by the system when a custom notification + * or session action was clicked. + * + *

If an {@link Intent} with this action is received in a subclass' {@link + * #onStartCommand(Intent, int, int)} method, the intent should be passed to the superclass + * (MediaSessionService) implementation. + * + *

This {@link Intent} is not intended to be intercepted by subclasses. If subclasses wish to + * handle custom notification actions, {@link + * MediaNotification.Provider#handleCustomCommand(MediaSession, String, Bundle)} or {@link + * MediaSession.Callback#onCustomCommand(MediaSession, ControllerInfo, SessionCommand, Bundle)} + * are appropriate. + */ + @UnstableApi + public static final String ACTION_CUSTOM_NOTIFICATION_ACTION = + "androidx.media3.session.CUSTOM_NOTIFICATION_ACTION"; + /** * The default timeout for a session to stay in a foreground service state after it paused, * stopped, failed or ended. @@ -439,9 +472,18 @@ public IBinder onBind(@Nullable Intent intent) { /** * Called when a component calls {@link android.content.Context#startService(Intent)}. * - *

The default implementation handles the incoming media button events. In this case, the - * intent will have the action {@link Intent#ACTION_MEDIA_BUTTON}. Override this method if this - * service also needs to handle actions other than {@link Intent#ACTION_MEDIA_BUTTON}. + *

The default implementation handles the following events: + * + *

+ * + *

Override this method if this service also needs to handle actions other than those mentioned + * above. * *

This method will be called on the main thread. */ From 2bd532cbe010495af1205120214a8c542476a883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20B=C3=A4chinger?= Date: Wed, 15 Oct 2025 16:58:51 +0000 Subject: [PATCH 2/2] Make action string protected and rephrase JavaDoc --- .../media3/session/MediaSessionService.java | 68 +++++++------------ 1 file changed, 23 insertions(+), 45 deletions(-) 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 fa3af2ba572..0c0a4151114 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java @@ -166,39 +166,6 @@ default void onForegroundServiceStartNotAllowedException() {} /** The action for {@link Intent} filter that must be declared by the service. */ public static final String SERVICE_INTERFACE = "androidx.media3.session.MediaSessionService"; - /** - * The action of an {@link Intent} sent to this service by itself when it is already running, but - * wishes to move into the foreground state. - * - *

This action is not intended for starting the service from anywhere except custom foreground - * service handling. Most apps will never need to send this action themselves. - * - *

If an {@link Intent} with this action is received in a subclass' {@link - * #onStartCommand(Intent, int, int)} method, the intent should be passed to the superclass - * (MediaSessionService) implementation. No further action is required to handle it. - */ - @UnstableApi - public static final String ACTION_START_SELF = - "androidx.media3.session.MediaSessionService.ACTION_START_SELF"; - - /** - * The action of an {@link Intent} sent to this service by the system when a custom notification - * or session action was clicked. - * - *

If an {@link Intent} with this action is received in a subclass' {@link - * #onStartCommand(Intent, int, int)} method, the intent should be passed to the superclass - * (MediaSessionService) implementation. - * - *

This {@link Intent} is not intended to be intercepted by subclasses. If subclasses wish to - * handle custom notification actions, {@link - * MediaNotification.Provider#handleCustomCommand(MediaSession, String, Bundle)} or {@link - * MediaSession.Callback#onCustomCommand(MediaSession, ControllerInfo, SessionCommand, Bundle)} - * are appropriate. - */ - @UnstableApi - public static final String ACTION_CUSTOM_NOTIFICATION_ACTION = - "androidx.media3.session.CUSTOM_NOTIFICATION_ACTION"; - /** * The default timeout for a session to stay in a foreground service state after it paused, * stopped, failed or ended. @@ -240,6 +207,27 @@ default void onForegroundServiceStartNotAllowedException() {} */ @UnstableApi public static final int SHOW_NOTIFICATION_FOR_IDLE_PLAYER_AFTER_STOP_OR_ERROR = 3; + /** + * The action of an {@link Intent} sent to this service by itself when it is already running, but + * wishes to move into the foreground state. + * + *

To start the service, an app must build a {@link MediaController} or a {@link MediaBrowser} + * that binds to the service instead. Starting the service with {@link + * Context#startForegroundService(Intent)} is highly discouraged. + */ + @UnstableApi + protected static final String ACTION_START_SELF = + "androidx.media3.session.MediaSessionService.ACTION_START_SELF"; + + /** + * The action of an {@link Intent} sent to this service by the system from the media controls + * notification in case the {@link android.app.PendingIntent} was build by {@link + * DefaultMediaNotificationProvider} that uses {@link DefaultActionFactory}. + */ + @UnstableApi + protected static final String ACTION_CUSTOM_NOTIFICATION_ACTION = + "androidx.media3.session.CUSTOM_NOTIFICATION_ACTION"; + private static final String TAG = "MSessionService"; private final Object lock; @@ -472,18 +460,8 @@ public IBinder onBind(@Nullable Intent intent) { /** * Called when a component calls {@link android.content.Context#startService(Intent)}. * - *

The default implementation handles the following events: - * - *

- * - *

Override this method if this service also needs to handle actions other than those mentioned - * above. + *

Apps normally don't need to override this method. If you think you need to override this + * method, file a bug on GitHub. * *

This method will be called on the main thread. */