Skip to content

Commit b82be5b

Browse files
marcbaechingercopybara-github
authored andcommitted
Don't override playback exception when creating the playback state
#cherrypick PiperOrigin-RevId: 785793329
1 parent 176e67a commit b82be5b

File tree

4 files changed

+55
-26
lines changed

4 files changed

+55
-26
lines changed

RELEASENOTES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@
7171
* Change the default value for
7272
`MediaLibrarySession.Builder.setLibraryErrorReplicationMode` to non
7373
fatal.
74+
* Fix a bug that causes a player's first playback error to be incorrectly
75+
treated as a persistent custom exception. This prevents the application
76+
from recovering.
7477
* UI:
7578
* Add `ProgressStateWithTickInterval` class and the corresponding
7679
`rememberProgressStateWithTickInterval` Composable to

libraries/session/src/main/java/androidx/media3/session/MediaSessionLegacyStub.java

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@
148148
private ImmutableList<CommandButton> mediaButtonPreferences;
149149
private SessionCommands availableSessionCommands;
150150
private Player.Commands availablePlayerCommands;
151-
@Nullable private PlaybackException playbackException;
151+
@Nullable private PlaybackException customPlaybackException;
152152
@Nullable private Player.Commands playerCommandsForErrorState;
153153

154154
@SuppressWarnings({
@@ -269,7 +269,7 @@ public MediaSessionLegacyStub(
269269
*/
270270
public void setAvailableCommands(
271271
SessionCommands sessionCommands, Player.Commands playerCommands) {
272-
if (playbackException != null) {
272+
if (customPlaybackException != null) {
273273
return;
274274
}
275275
boolean commandGetTimelineChanged =
@@ -361,7 +361,7 @@ public void setPlatformMediaButtonPreferences(
361361
}
362362

363363
/**
364-
* Sets or clears an playback exception override for the platform session.
364+
* Sets or clears a playback exception override for the platform session.
365365
*
366366
* @param playbackException The {@link PlaybackException} or null.
367367
* @param playerCommandsForErrorState The available {@link Player.Commands} while the exception
@@ -373,7 +373,7 @@ public void setPlaybackException(
373373
checkArgument(
374374
(playbackException == null && playerCommandsForErrorState == null)
375375
|| (playbackException != null && playerCommandsForErrorState != null));
376-
this.playbackException = playbackException;
376+
customPlaybackException = playbackException;
377377
this.playerCommandsForErrorState = playerCommandsForErrorState;
378378
if (playbackException != null) {
379379
updateLegacySessionPlaybackState(sessionImpl.getPlayerWrapper());
@@ -1292,7 +1292,7 @@ public ControllerLegacyCbForBroadcast() {
12921292
* @return True if updates should be skipped.
12931293
*/
12941294
public boolean skipLegacySessionPlaybackStateUpdates() {
1295-
return playbackException != null;
1295+
return customPlaybackException != null;
12961296
}
12971297

12981298
@Override
@@ -1765,7 +1765,7 @@ private static ComponentName getServiceComponentByAction(Context context, String
17651765

17661766
private PlaybackStateCompat createPlaybackStateCompat(PlayerWrapper player) {
17671767
LegacyError legacyError = this.legacyError;
1768-
if (playbackException == null && legacyError != null && legacyError.isFatal) {
1768+
if (customPlaybackException == null && legacyError != null && legacyError.isFatal) {
17691769
// A fatal legacy error automatically set by Media3 upon a calling
17701770
// MediaLibrarySession.Callback according to the configured LibraryErrorReplicationMode.
17711771
Bundle extras = new Bundle(legacyError.extras);
@@ -1783,17 +1783,17 @@ private PlaybackStateCompat createPlaybackStateCompat(PlayerWrapper player) {
17831783
.setExtras(legacyError.extras)
17841784
.build();
17851785
}
1786-
if (playbackException == null) {
1787-
// The actual error from the player, if any.
1788-
playbackException = player.getPlayerError();
1789-
}
1786+
// The custom error from the session if present, or the actual error from the player, if any.
1787+
@Nullable
1788+
PlaybackException publicPlaybackException =
1789+
customPlaybackException != null ? customPlaybackException : player.getPlayerError();
17901790
boolean canReadPositions =
17911791
player.isCommandAvailable(Player.COMMAND_GET_CURRENT_MEDIA_ITEM)
17921792
&& !player.isCurrentMediaItemLive();
17931793
boolean shouldShowPlayButton =
1794-
playbackException != null || Util.shouldShowPlayButton(player, playIfSuppressed);
1794+
publicPlaybackException != null || Util.shouldShowPlayButton(player, playIfSuppressed);
17951795
int state =
1796-
playbackException != null
1796+
publicPlaybackException != null
17971797
? PlaybackStateCompat.STATE_ERROR
17981798
: LegacyConversions.convertToPlaybackStateCompatState(player, shouldShowPlayButton);
17991799
// Always advertise ACTION_SET_RATING.
@@ -1824,8 +1824,9 @@ private PlaybackStateCompat createPlaybackStateCompat(PlayerWrapper player) {
18241824
: MediaSessionCompat.QueueItem.UNKNOWN_ID;
18251825
float playbackSpeed = player.getPlaybackParameters().speed;
18261826
float sessionPlaybackSpeed = player.isPlaying() && canReadPositions ? playbackSpeed : 0f;
1827-
Bundle extras = playbackException != null ? new Bundle(playbackException.extras) : new Bundle();
1828-
if (playbackException == null && legacyError != null) {
1827+
Bundle extras =
1828+
publicPlaybackException != null ? new Bundle(publicPlaybackException.extras) : new Bundle();
1829+
if (publicPlaybackException == null && legacyError != null) {
18291830
extras.putAll(legacyError.extras);
18301831
}
18311832
extras.putAll(legacyExtras);
@@ -1879,10 +1880,10 @@ private PlaybackStateCompat createPlaybackStateCompat(PlayerWrapper player) {
18791880
.build());
18801881
}
18811882
}
1882-
if (playbackException != null) {
1883+
if (publicPlaybackException != null) {
18831884
builder.setErrorMessage(
1884-
LegacyConversions.convertToLegacyErrorCode(playbackException),
1885-
playbackException.getMessage());
1885+
LegacyConversions.convertToLegacyErrorCode(publicPlaybackException),
1886+
publicPlaybackException.getMessage());
18861887
} else if (legacyError != null) {
18871888
builder.setErrorMessage(legacyError.code, legacyError.message);
18881889
}

libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerCompatCallbackWithMediaSessionTest.java

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ public void onSessionReady() {
239239
}
240240

241241
@Test
242-
public void playerError_notified() throws Exception {
242+
public void playerError_errorEmittedAndResolved_correctPlaybackStatesReceived() throws Exception {
243243
Bundle extras = new Bundle();
244244
extras.putString("key-1", "value-1");
245245
PlaybackException testPlayerError =
@@ -248,27 +248,49 @@ public void playerError_notified() throws Exception {
248248
/* cause= */ null,
249249
PlaybackException.ERROR_CODE_AUTHENTICATION_EXPIRED,
250250
extras);
251-
252251
CountDownLatch latch = new CountDownLatch(1);
253252
AtomicReference<PlaybackStateCompat> playbackStateCompatRef = new AtomicReference<>();
254-
MediaControllerCompat.Callback callback =
253+
MediaControllerCompat.Callback errorCallback =
255254
new MediaControllerCompat.Callback() {
256255
@Override
257256
public void onPlaybackStateChanged(PlaybackStateCompat state) {
258257
playbackStateCompatRef.set(state);
259258
latch.countDown();
260259
}
261260
};
262-
controllerCompat.registerCallback(callback, handler);
261+
controllerCompat.registerCallback(errorCallback, handler);
263262

264263
session.getMockPlayer().notifyPlayerError(testPlayerError);
264+
265265
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
266-
PlaybackStateCompat state = playbackStateCompatRef.get();
267-
assertThat(state.getState()).isEqualTo(PlaybackStateCompat.STATE_ERROR);
268-
assertThat(state.getErrorCode())
266+
PlaybackStateCompat errorState = playbackStateCompatRef.get();
267+
assertThat(errorState.getState()).isEqualTo(PlaybackStateCompat.STATE_ERROR);
268+
assertThat(errorState.getErrorCode())
269269
.isEqualTo(PlaybackStateCompat.ERROR_CODE_AUTHENTICATION_EXPIRED);
270-
assertThat(state.getErrorMessage().toString()).isEqualTo(testPlayerError.getMessage());
271-
assertThat(state.getExtras().getString("key-1")).isEqualTo("value-1");
270+
assertThat(errorState.getErrorMessage().toString()).isEqualTo(testPlayerError.getMessage());
271+
assertThat(errorState.getExtras().getString("key-1")).isEqualTo("value-1");
272+
// Resolve the exception and assert the playback state transition
273+
controllerCompat.unregisterCallback(errorCallback);
274+
CountDownLatch bufferingLatch = new CountDownLatch(1);
275+
AtomicReference<PlaybackStateCompat> bufferingPlaybackStateCompatRef = new AtomicReference<>();
276+
MediaControllerCompat.Callback bufferingCallback =
277+
new MediaControllerCompat.Callback() {
278+
@Override
279+
public void onPlaybackStateChanged(PlaybackStateCompat state) {
280+
bufferingPlaybackStateCompatRef.set(state);
281+
bufferingLatch.countDown();
282+
}
283+
};
284+
controllerCompat.registerCallback(bufferingCallback, handler);
285+
286+
session.getMockPlayer().notifyPlaybackStateChanged(Player.STATE_BUFFERING);
287+
288+
assertThat(bufferingLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
289+
PlaybackStateCompat bufferingState = bufferingPlaybackStateCompatRef.get();
290+
assertThat(bufferingState.getState()).isEqualTo(PlaybackStateCompat.STATE_PAUSED);
291+
assertThat(bufferingState.getErrorCode()).isEqualTo(0);
292+
assertThat(bufferingState.getErrorMessage()).isNull();
293+
assertThat(bufferingState.getExtras().getString("key-1")).isNull();
272294
}
273295

274296
@SuppressWarnings("deprecation") // Using PlaybackStateCompat

libraries/test_session_current/src/main/java/androidx/media3/session/MockPlayer.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,9 @@ public void notifyPlaybackStateChanged(@State int playbackState) {
633633
return;
634634
}
635635
boolean wasPlaying = isPlaying();
636+
if (playbackState != STATE_IDLE) {
637+
this.playerError = null;
638+
}
636639
this.playbackState = playbackState;
637640
boolean isPlaying = isPlaying();
638641
for (Listener listener : listeners) {

0 commit comments

Comments
 (0)