This change includes a change in the `IMediaController.aidl` file and needs
to provide backwards compatibility for when a client connects that is of an older or
newer version of the current service implementation.
This CL proposes to create a new AIDL method `onPlayerInfoChangedWithExtensions`
that is easier to extend in the future because it does use an `Bundle` rather than
primitives. A `Bundle` can be changed in a backward/forwards compatible way
in case we need further changes.
The compatibility handling is provided in `MediaSessionStub` and `MediaControllerStub`. The approach is not based on specific AIDL/Binder features but implemented fully in application code.
Issue: androidx/media#102
#minor-release
PiperOrigin-RevId: 490483068
The direct executor is not the proper way to determine on what thread to run the
`Future.Listener` and the `MediaControllerCreationListener` because the listener
may call the controller passed as argument which must happen on the same thread
that built the controller. This change makes sure this is the case.
PiperOrigin-RevId: 490478587
This avoids that apps have to depend on the legacy compat support
library when they want to make this conversion.
Also add a version to both helper methods that takes a Looper to
give apps the option to use an existing Looper, which should be
much faster than spinning up a new thread for every method call.
Issue: androidx/media#171
PiperOrigin-RevId: 490441913
When receiving the `onTimelineChanged` callback, we convert the timeline to the list of `QueueItem`s, where decoding a bitmap is needed for building each of the `QueueItem`s. The strategy is similar to what we did in <unknown commit> for list of `MediaBrowserCompat.MediaItem` - set the queue item list until the bitmaps decoding for all the `MediaItem`s are completed.
PiperOrigin-RevId: 490283587
When we currently call SessionToken.createSessionToken with a legacy
token, we call the package manager to get the process UID. This
requires visiblity to the target package, which may not be available
unless the target runs a service known to the controller app.
However, when connecting to a Media3, this UID doesn't have to be
known, so we can move the call closer to where it's needed to
avoid the unncessary visibility check.
In addition, a legacy session may reply with unknown result code
to the session token request, which we should handle as well.
One of the constructor can be removed since it was only used from
a test.
PiperOrigin-RevId: 489917706
* Transforms the `ListenableFuture<LibraryResult<MediaItem>>` and `ListenableFuture<LibraryResult<List<MediaItem>>>` to `ListenableFuture<MediaBrowserCompat.MediaItem>` and `ListenableFuture<List<MediaBrowserCompat.MediaItem>>`, and the result will be sent out when `ListenableFuture` the `MediaBrowserCompat.MediaItem` (or the list of it) is fulfilled.
* Add `artworkData` to the tests in `MediaBrowserCompatWithMediaLibraryServiceTest`.
PiperOrigin-RevId: 489205547
The MediaControllerImplBase listener invocations currently use the
class member state that can change if one of the listener method
implementations changes the state recursively.
Updating the listener invocations to use a final local variable
ensures all listeners get consistent updates.
PiperOrigin-RevId: 487503373
Creating the PlaybackStateCompat from a media3 Player state is done
already by the MediaSessionConnector (and used widely). The media3
session module should set the same states under the same circumstances
to ensure compatiblity and consistency.
PlaybackStateCompat changes made in media3 session:
- Use STATE_STOPPED when player is ended instead of STATE_PAUSED
- Use STATE_PLAYING when playback is suppressed temporarily.
- Set the playback speed to 0 if the player is not playing.
- Add extras for mediaId and user-set playback speed.
Part of the problem was that Player.isPlaying() was used to check
the state. Unfortunately, MockPlayer.isPlaying() is implemented in
a way that makes it hard to test these changes, because the value
is set independently of playbackState, playWhenReady and suppression
reason. To be able to write consistent, logical tests, this change
also removes the independent setting of isPlaying in MockPlayer to
align it better with a real player. This requires to update some
other tests to use alternative methods.
PiperOrigin-RevId: 487500859
Accepting a PlayerInfo while the MediaController is masking its state
means we are reverting all masking changes we've made earlier. This
only makes sense if the update already contains the masked operation.
If multiple operations are in flight (or are sent from the session
while they are in flight), we need to wait until all of them are
handled before accepting new updates.
In cases where a new update from the session excludes the Timeline
and the masked state is incompatible with the new update, we also
risk an exception if we accept the update too early.
PiperOrigin-RevId: 487266899
This is the follow-up commit where the onEvents callback
raised by MediaController contains the missing events, for the
case where MediaController is connected to a legacy MediaSession.
#minor-release
PiperOrigin-RevId: 487231996
The method getCurrentPosition() may return a lesser position during pause than the previous retrieved value due to ipc call delay in playerInfo update. Users see track position jump backwards at pause. Fixed to return last estimated position while paused if have not received updated playerInfo. Code is deduped to point getContentPosition() to getCurrentPosition() when !isPlayingAd.
PiperOrigin-RevId: 486617341
* Add `Listener` in `MediaSession` with method `onNotificationRefreshRequired(MediaSession)`.
* Add `MediaSessionService` as the listener of the `MediaSession` when `MediaSession` is added to `MediaSessionService`
* Load bitmap when update metadata in `MediaSessionLegacyStub` and call `onNotificationRefreshRequired` when bitmap asynchronously arrives.
PiperOrigin-RevId: 485376145
This root extra needs to be set by apps manually in media1 and we
can do that automatically in Media3 based on the available session
commands.
#minor-release
PiperOrigin-RevId: 484286833
In Media3 there is the useful concept of connection hints that a
client can set when building the session and that are sent to the
service and passed to the `Callback.onConnect()` method when the
browser connects.
These connection hints are then included in the `ControllerInfo`
object that later will be passed to every callback method and the
implementor can then take decisions specific to these connection
hints.
These connection hints are not available in media1. However, when
an app creates a `MediaBrowserCompat` object, the constructor takes
a rootHint object that is sent to
`MediaBrowserServiceCompat.onGetRoot()`.
This change uses the browser rootHints as the connection hints when
creating the `ControllerInfo` for legacy browsers and makes them
available to the `MediaLibrarySession.Callback` domain methods in
the same way as connection hints of a Media3 browser.
PiperOrigin-RevId: 484220748
This is the first commit out of two. This change adds the missing event
flags for the onEvents() callback when MediaController is connected to a
media3 session (see MediaControllerImplBase). I updated the
MediaControllerListenerTest and MediaControllerStateMaskingTest with
assertions that on onEvents() is called alongside individual
Player.Listener callbacks.
There will be a follow-up change for the case where a MediaController is
connected to a legacy MediaSession (MediaControllerImplLegacy). I've
split this in two separate changes to make the size of the commit
manageable for reviewing.
#minor-release
PiperOrigin-RevId: 481933437
Adds root extras and metadata extras to MockMediaLibraryService and MockMediaBrowserCompatService and completed test cases for asserting
interoperability with a media1 or Media3 browser.
PiperOrigin-RevId: 480854842
One of the tests in MediaBrowserListenerTest caused the remote
browser service to crash and then just timed out. As this asserts
nothing useful besides checking that the timeout method is working,
we can remove the test.
Crashing the remote browser service had the side effect of letting
subsequent tests in the same class fail because the previous session
was never released and was still present in the static MediaSession
SESSION_ID_TO_SESSION_MAP instance, which prevented the creation
of new sessions with the same id. This is only an issue in test
runs because a real process would also lose its static variables
when it crashes.
PiperOrigin-RevId: 476905337
The assertion asserts against a `Period` and an `AdPlaybackState` which actually
asserts against a resolved ad which is what `ExoPlayerImplInternal` does later and
what gives us a `SEEK_ADJUSTMENT`. However, this assertion is not required at the
moment of masking, because we are sure that the resolved seek results in a content
period and never an ad period.
#minor-release
Issue: androidx/media#122
PiperOrigin-RevId: 471827072
We create an empty CueGroup in many places as default or
where none is needed. Instead, we can define a constant
for this purpose and reuse it.
PiperOrigin-RevId: 467944841
When the player finishes playback and reaches the STATE_ENDED,
the notification remains visible with a pause button and the
service is kept in the foreground. This is a bug.
With this change, when the player reaches the STATE_ENDED, the
service is stopped from the foreground and a notification is shown
with a play button. If the play icon is tapped, the player will restart
playback of the last played item. Playing the last played item again
is the existing behavior when play/pause commands are received from
the legacy MediaSession (e.g. BT headset buttons).
#minor-release
Issue: google/ExoPlayer#112
PiperOrigin-RevId: 467231509
Once a controller bound to a MediaSessionService unbinds, this service
may be destroyed immediately if this was the last bound controller and
the service isn't started in the foreground.
At the time of releasing the controller, there may still be pending
commands that need to be handled by the session service. These commands
may cause the session service to post a foreground notification to
keep it alive. So to avoid the destruction of the service while these
commands are still handled, we need to keep the controller bound.
We also add a timeout in case the session tasks are never completed
by the session implementation. In case the controller is destroyed,
the unbinding happens automatically by the system.
PiperOrigin-RevId: 463802220
We currently clear all pending messages, including the one that flushes
pending commands to the MediaSession. To ensure all commands that have
been called before controller.release() are still sent, we can manually
trigger the flush message from the release call.
Related to handling the final flush because disconnecting the controller,
MediaSessionStub didn't post the removal of the controller to the
session thread, creating a race condition between removing the controller
and actually handling the flush.
Issue: androidx/media#99
PiperOrigin-RevId: 462342860
Some commands are run asynchronously and subsequent commands need
to wait until the previous one finished. This can be supported
by returning a Future for each command and using the existing
command execution logic to wait for each Future to complete.
As some MediaSessionStub code is now executed delayed to when it
was originally created, we also need to check if the session is
not released before triggering any actions or sending result codes.
Issue: androidx/media#85
PiperOrigin-RevId: 462101136
After this change the current tracks are sent to the controller as part of
`PlayerInfo` and call `Listener.onTracksChanged()` in case of a change in tracks.
PiperOrigin-RevId: 461578695
This code path is now completely redundant as the same effect can be
achieved by using player.setMediaItem.
#minor-release
PiperOrigin-RevId: 455115567
These calls were not implemented so far as they require a mix of
initial prepareFrom/playFrom calls and addQueueItem. We can also
support clients without queue handling to set single MediaItems.
To make the calls consistent and predictable in the session,
we need to ensure that none of the play/pause/addQueueItem/
removeQueueItem/prepare/playFromXYZ/prepareFromXYZ are called
before the controller is prepared and has media.
#minor-release
PiperOrigin-RevId: 455110246
Some Player implementations have no playlist capability but can still
set a MediaItem for playback. Examples are a MediaController connected
to a legacy MediaSession, ExoPlayer up to 2.12 or MediaPlayer.
To indicate this capability, we need an allowed command in addition
to COMMAND_CHANGE_MEDIA_ITEMS that just allows to set a single item
that replaces everything that is currently played.
#minor-release
PiperOrigin-RevId: 453879626
These legacy callbacks are currently forwarded to onSetMediaUri which
will be removed in the future.
Also make sure to only call player.prepare/play after the items have
been set.
The calls to onAddQueueItem are also forwarded to onAddMediaItems to
actually allow a session to resolve these items to playable media, which
wasn't possible so far.
PiperOrigin-RevId: 453625204
This change makes MediaSessionPermissionTest.removeMediaItems()
non-flaky. This is a quick fix though it'd be good to refactor this
test file.
PiperOrigin-RevId: 453162764
This provides an (unstable) API for apps to broadcast session extras
Bundle to all connected controllers and set the extras in the legacy
session.
Similar to the custom layout, the extras Bundle is not part of the
Media3 session state. This means that when a Media3 controller
connects to the session after the broadcast, the extras needs to be
sent to that controller in `MediaSession.Callback.onPostConnect(MediaSession session, ControllerInfo controller)`.
PiperOrigin-RevId: 451871731