Skip to content

Commit

Permalink
Send ConnectionState as in-process bundle if possible
Browse files Browse the repository at this point in the history
#minor-release

PiperOrigin-RevId: 573849858
  • Loading branch information
marcbaechinger authored and copybara-github committed Oct 16, 2023
1 parent 681eade commit d5f093f
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@
import static androidx.media3.common.util.Assertions.checkNotNull;

import android.app.PendingIntent;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import androidx.annotation.Nullable;
import androidx.core.app.BundleCompat;
import androidx.media3.common.Bundleable;
import androidx.media3.common.Player;
import androidx.media3.common.util.BundleUtil;
import androidx.media3.common.util.BundleableUtil;
import androidx.media3.common.util.Util;
import com.google.common.collect.ImmutableList;
Expand Down Expand Up @@ -69,13 +71,13 @@ public ConnectionState(
this.libraryVersion = libraryVersion;
this.sessionInterfaceVersion = sessionInterfaceVersion;
this.sessionBinder = sessionBinder;
this.sessionActivity = sessionActivity;
this.customLayout = customLayout;
this.sessionCommands = sessionCommands;
this.playerCommandsFromSession = playerCommandsFromSession;
this.playerCommandsFromPlayer = playerCommandsFromPlayer;
this.sessionActivity = sessionActivity;
this.tokenExtras = tokenExtras;
this.playerInfo = playerInfo;
this.customLayout = customLayout;
}

// Bundleable implementation.
Expand All @@ -90,8 +92,9 @@ public ConnectionState(
private static final String FIELD_TOKEN_EXTRAS = Util.intToStringMaxRadix(6);
private static final String FIELD_PLAYER_INFO = Util.intToStringMaxRadix(7);
private static final String FIELD_SESSION_INTERFACE_VERSION = Util.intToStringMaxRadix(8);
private static final String FIELD_IN_PROCESS_BINDER = Util.intToStringMaxRadix(10);

// Next field key = 10
// Next field key = 11

@Override
public Bundle toBundle() {
Expand Down Expand Up @@ -119,10 +122,24 @@ public Bundle toBundle() {
return bundle;
}

/**
* Returns a {@link Bundle} that stores a direct object reference to this class for in-process
* sharing.
*/
public Bundle toBundleInProcess() {
Bundle bundle = new Bundle();
BundleUtil.putBinder(bundle, FIELD_IN_PROCESS_BINDER, new InProcessBinder());
return bundle;
}

/** Object that can restore a {@link ConnectionState} from a {@link Bundle}. */
public static final Creator<ConnectionState> CREATOR = ConnectionState::fromBundle;

private static ConnectionState fromBundle(Bundle bundle) {
@Nullable IBinder inProcessBinder = BundleUtil.getBinder(bundle, FIELD_IN_PROCESS_BINDER);
if (inProcessBinder instanceof InProcessBinder) {
return ((InProcessBinder) inProcessBinder).getConnectionState();
}
int libraryVersion = bundle.getInt(FIELD_LIBRARY_VERSION, /* defaultValue= */ 0);
int sessionInterfaceVersion =
bundle.getInt(FIELD_SESSION_INTERFACE_VERSION, /* defaultValue= */ 0);
Expand Down Expand Up @@ -169,4 +186,10 @@ private static ConnectionState fromBundle(Bundle bundle) {
tokenExtras == null ? Bundle.EMPTY : tokenExtras,
playerInfo);
}

private final class InProcessBinder extends Binder {
public ConnectionState getConnectionState() {
return ConnectionState.this;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,10 @@ public void connect(IMediaController caller, ControllerInfo controllerInfo) {
}
try {
caller.onConnected(
sequencedFutureManager.obtainNextSequenceNumber(), state.toBundle());
sequencedFutureManager.obtainNextSequenceNumber(),
caller instanceof MediaControllerStub
? state.toBundleInProcess()
: state.toBundle());
connected = true;
} catch (RemoteException e) {
// Controller may be died prematurely.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,21 @@
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.MediaItem;
import androidx.media3.common.MediaLibraryInfo;
import androidx.media3.common.Player;
import androidx.media3.common.Rating;
import androidx.media3.common.StarRating;
import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.session.MediaSession.ConnectionResult.AcceptedResultBuilder;
import androidx.media3.session.MediaSession.ControllerInfo;
import androidx.media3.test.session.R;
import androidx.media3.test.session.common.HandlerThreadTestRule;
import androidx.media3.test.session.common.MainLooperTestRule;
import androidx.media3.test.session.common.TestUtils;
import androidx.media3.test.utils.TestExoPlayerBuilder;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
Expand Down Expand Up @@ -650,7 +653,7 @@ public ListenableFuture<List<MediaItem>> onAddMediaItems(
RemoteMediaController controller =
controllerTestRule.createRemoteController(session.getToken());

controller.setMediaItems(mediaItems, /* startWindowIndex= */ 1, /* startPositionMs= */ 1234);
controller.setMediaItems(mediaItems, /* startIndex= */ 1, /* startPositionMs= */ 1234);
player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS_WITH_START_INDEX, TIMEOUT_MS);

assertThat(requestedMediaItems.get()).containsExactlyElementsIn(mediaItems).inOrder();
Expand Down Expand Up @@ -950,7 +953,7 @@ public ListenableFuture<MediaSession.MediaItemsWithStartPosition> onSetMediaItem
new MediaSession.MediaItemsWithStartPosition(
updateMediaItemsWithLocalConfiguration(mediaItems),
startIndex,
/* startPosition= */ 200));
/* startPositionMs= */ 200));
}
};
MediaSession session =
Expand All @@ -959,7 +962,7 @@ public ListenableFuture<MediaSession.MediaItemsWithStartPosition> onSetMediaItem
RemoteMediaController controller =
controllerTestRule.createRemoteController(session.getToken());

controller.setMediaItems(mediaItems, /* startWindowIndex= */ 1, /* startPositionMs= */ 100);
controller.setMediaItems(mediaItems, /* startIndex= */ 1, /* startPositionMs= */ 100);
player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS_WITH_START_INDEX, TIMEOUT_MS);

assertThat(requestedMediaItems.get()).containsExactlyElementsIn(mediaItems).inOrder();
Expand Down Expand Up @@ -999,7 +1002,7 @@ public ListenableFuture<MediaSession.MediaItemsWithStartPosition> onSetMediaItem
RemoteMediaController controller =
controllerTestRule.createRemoteController(session.getToken());

controller.setMediaItems(mediaItems, /* startWindowIndex= */ 1, /* startPositionMs= */ 100);
controller.setMediaItems(mediaItems, /* startIndex= */ 1, /* startPositionMs= */ 100);
player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS_WITH_RESET_POSITION, TIMEOUT_MS);

assertThat(requestedMediaItems.get()).containsExactlyElementsIn(mediaItems).inOrder();
Expand Down Expand Up @@ -1045,7 +1048,7 @@ public ListenableFuture<MediaSession.MediaItemsWithStartPosition> onSetMediaItem
player.currentPosition = 200;

// Re-set media items with start index and position as current index and position
controller.setMediaItems(mediaItems, C.INDEX_UNSET, /* startPosition= */ 0);
controller.setMediaItems(mediaItems, C.INDEX_UNSET, /* startPositionMs= */ 0);
player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS_WITH_START_INDEX, TIMEOUT_MS);

assertThat(requestedMediaItems.get()).containsExactlyElementsIn(mediaItems).inOrder();
Expand Down Expand Up @@ -1201,6 +1204,66 @@ public void onDisconnected(MediaSession session, ControllerInfo controller) {
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
}

@Test
public void seekToNextMediaItem_inProcessController_correctMediaItemTransitionsEvents()
throws Exception {
MediaItem mediaItem1 =
new MediaItem.Builder().setMediaId("id1").setUri("http://www.example.com/1").build();
MediaItem mediaItem2 =
new MediaItem.Builder().setMediaId("id2").setUri("http://www.example.com/2").build();
ExoPlayer testPlayer =
threadTestRule
.getHandler()
.postAndSync(
() -> {
ExoPlayer exoPlayer = new TestExoPlayerBuilder(context).build();
exoPlayer.setMediaItems(ImmutableList.of(mediaItem1, mediaItem2));
return exoPlayer;
});
List<String> capturedMediaItemIds = new ArrayList<>();
List<Player.Events> capturedEvents = new ArrayList<>();
List<String> eventOrder = new ArrayList<>();
CountDownLatch latch = new CountDownLatch(1);
MediaSession session =
sessionTestRule.ensureReleaseAfterTest(
new MediaSession.Builder(context, testPlayer)
.setId("seekToNextMediaItem_inProcessController_correctMediaItemTransitionsEvents")
.build());
MediaController controller =
new MediaController.Builder(ApplicationProvider.getApplicationContext(), session.getToken())
.setApplicationLooper(threadTestRule.getHandler().getLooper())
.buildAsync()
.get();
controller.addListener(
new Player.Listener() {
@Override
public void onMediaItemTransition(@Nullable MediaItem mediaItem, int reason) {
capturedMediaItemIds.add(controller.getCurrentMediaItem().mediaId);
eventOrder.add("onMediaItemTransition");
}

@Override
public void onEvents(Player player, Player.Events events) {
if (events.contains(Player.EVENT_MEDIA_ITEM_TRANSITION)) {
capturedMediaItemIds.add(controller.getCurrentMediaItem().mediaId);
capturedEvents.add(events);
eventOrder.add("onEvents");
latch.countDown();
}
}
});

threadTestRule.getHandler().postAndSync(testPlayer::seekToNextMediaItem);

assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(capturedMediaItemIds).containsExactly("id2", "id2").inOrder();
assertThat(eventOrder).containsExactly("onMediaItemTransition", "onEvents").inOrder();
assertThat(capturedEvents).hasSize(1);
assertThat(capturedEvents.get(0).size()).isEqualTo(2);
assertThat(capturedEvents.get(0).contains(Player.EVENT_MEDIA_ITEM_TRANSITION)).isTrue();
assertThat(capturedEvents.get(0).contains(Player.EVENT_POSITION_DISCONTINUITY)).isTrue();
}

private static MediaItem updateMediaItemWithLocalConfiguration(MediaItem mediaItem) {
return mediaItem.buildUpon().setUri(METADATA_MEDIA_URI).build();
}
Expand Down

0 comments on commit d5f093f

Please sign in to comment.