Commit 5528baaa by bachinger Committed by christosts

Do not assume a valid queue in 3rd party sessions

This change fixes an issue that can be reproduced when
a controller `onConnect` creates a `QueueTimeline` out
of the state of a legacy session and then `prepare` is called.

`activeQueueItemId`, `metadata` and the `queue` of the legacy
session are used when a `QueueTimeline` is created. The change
adds unit tests to cover the different combinatoric cases these
properties being set or unset.

PiperOrigin-RevId: 505731288
(cherry picked from commit 4a9cf7d0)
parent bfc4ed4d
......@@ -54,6 +54,8 @@
onto Player ([#156](https://github.com/androidx/media/issues/156)).
* Avoid double tap detection for non-Bluetooth media button events
([#233](https://github.com/androidx/media/issues/233)).
* Make `QueueTimeline` more robust in case of a shady legacy session state
([#241](https://github.com/androidx/media/issues/241)).
* Metadata:
* Parse multiple null-separated values from ID3 frames, as permitted by
ID3 v2.4.
......
......@@ -1828,6 +1828,8 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
+ " MediaItem.");
MediaItem fakeMediaItem =
MediaUtils.convertToMediaItem(newLegacyPlayerInfo.mediaMetadataCompat, ratingType);
// Ad a tag to make sure the fake media item can't have an equal instance by accident.
fakeMediaItem = fakeMediaItem.buildUpon().setTag(new Object()).build();
currentTimeline = currentTimeline.copyWithFakeMediaItem(fakeMediaItem);
currentMediaItemIndex = currentTimeline.getWindowCount() - 1;
} else {
......@@ -1842,7 +1844,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
if (hasMediaMetadataCompat) {
MediaItem mediaItem =
MediaUtils.convertToMediaItem(
currentTimeline.getMediaItemAt(currentMediaItemIndex).mediaId,
checkNotNull(currentTimeline.getMediaItemAt(currentMediaItemIndex)).mediaId,
newLegacyPlayerInfo.mediaMetadataCompat,
ratingType);
currentTimeline =
......@@ -1999,7 +2001,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
MediaItem oldCurrentMediaItem =
checkStateNotNull(oldControllerInfo.playerInfo.getCurrentMediaItem());
int oldCurrentMediaItemIndexInNewTimeline =
((QueueTimeline) newControllerInfo.playerInfo.timeline).findIndexOf(oldCurrentMediaItem);
((QueueTimeline) newControllerInfo.playerInfo.timeline).indexOf(oldCurrentMediaItem);
if (oldCurrentMediaItemIndexInNewTimeline == C.INDEX_UNSET) {
// Old current item is removed.
discontinuityReason = Player.DISCONTINUITY_REASON_REMOVE;
......
......@@ -42,4 +42,5 @@ interface IRemoteMediaSessionCompat {
void sendSessionEvent(String sessionTag, String event, in Bundle extras);
void setCaptioningEnabled(String sessionTag, boolean enabled);
void setSessionExtras(String sessionTag, in Bundle extras);
int getCallbackMethodCount(String sessionTag, String methodName);
}
......@@ -49,9 +49,13 @@ import java.util.concurrent.Executor;
@UnstableApi
public class MediaSessionCompatProviderService extends Service {
public static final String METHOD_ON_PREPARE_FROM_MEDIA_ID = "onPrepareFromMediaId";
public static final String METHOD_ON_PREPARE = "onPrepare";
private static final String TAG = "MSCProviderService";
Map<String, MediaSessionCompat> sessionMap = new HashMap<>();
Map<String, CallCountingCallback> callbackMap = new HashMap<>();
RemoteMediaSessionCompatStub sessionBinder;
TestHandler handler;
......@@ -88,7 +92,10 @@ public class MediaSessionCompatProviderService extends Service {
() -> {
MediaSessionCompat session =
new MediaSessionCompat(MediaSessionCompatProviderService.this, sessionTag);
CallCountingCallback callback = new CallCountingCallback(sessionTag);
session.setCallback(callback);
sessionMap.put(sessionTag, session);
callbackMap.put(sessionTag, callback);
});
} catch (Exception e) {
Log.e(TAG, "Exception occurred while creating MediaSessionCompat", e);
......@@ -212,15 +219,61 @@ public class MediaSessionCompatProviderService extends Service {
}
@Override
public void setCaptioningEnabled(String sessionTag, boolean enabled) throws RemoteException {
public void setCaptioningEnabled(String sessionTag, boolean enabled) {
MediaSessionCompat session = sessionMap.get(sessionTag);
session.setCaptioningEnabled(enabled);
}
@Override
public void setSessionExtras(String sessionTag, Bundle extras) throws RemoteException {
public void setSessionExtras(String sessionTag, Bundle extras) {
MediaSessionCompat session = sessionMap.get(sessionTag);
session.setExtras(extras);
}
@Override
public int getCallbackMethodCount(String sessionTag, String methodName) {
CallCountingCallback callCountingCallback = callbackMap.get(sessionTag);
if (callCountingCallback != null) {
Integer count = callCountingCallback.callbackCallCounters.get(methodName);
return count != null ? count : 0;
}
return 0;
}
}
private class CallCountingCallback extends MediaSessionCompat.Callback {
private final String sessionTag;
private final Map<String, Integer> callbackCallCounters;
public CallCountingCallback(String sessionTag) {
this.sessionTag = sessionTag;
callbackCallCounters = new HashMap<>();
}
@Override
public void onPrepareFromMediaId(String mediaId, Bundle extras) {
countCallbackCall(METHOD_ON_PREPARE_FROM_MEDIA_ID);
sessionMap
.get(sessionTag)
.setMetadata(
new MediaMetadataCompat.Builder()
.putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, mediaId)
.build());
}
@Override
public void onPrepare() {
countCallbackCall(METHOD_ON_PREPARE);
sessionMap.get(sessionTag).setMetadata(new MediaMetadataCompat.Builder().build());
}
private void countCallbackCall(String callbackName) {
int count = 0;
if (callbackCallCounters.containsKey(callbackName)) {
count = callbackCallCounters.get(callbackName);
}
callbackCallCounters.put(callbackName, ++count);
}
}
}
......@@ -111,6 +111,10 @@ public class RemoteMediaSessionCompat {
binder.setPlaybackToLocal(sessionTag, stream);
}
public int getCallbackMethodCount(String callbackMethodName) throws RemoteException {
return binder.getCallbackMethodCount(sessionTag, callbackMethodName);
}
/**
* Since we cannot pass VolumeProviderCompat directly, we pass volumeControl, maxVolume,
* currentVolume instead.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment