Add platform token to Media3 SessionToken
Access is package-private and it will allow the media controller logic to interact with the underlying platform session directly if needed. Interop: When a MediaController connects to an older session (before this change), it won't get the platform token from the session directly. Many controllers will be set up with a platform or compat token though and we can simply keep the already known token and use it. The only cases where we still don't have a platform token in the MediaController are the cases where the controller is created with a SessionToken based on a ComponentName. PiperOrigin-RevId: 678230977
This commit is contained in:
parent
d8dc513431
commit
43765b7567
@ -18,6 +18,7 @@ package androidx.media3.session;
|
|||||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
|
import android.media.session.MediaSession.Token;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
@ -57,6 +58,8 @@ import java.util.List;
|
|||||||
|
|
||||||
public final ImmutableList<CommandButton> customLayout;
|
public final ImmutableList<CommandButton> customLayout;
|
||||||
|
|
||||||
|
@Nullable public final Token platformToken;
|
||||||
|
|
||||||
public ConnectionState(
|
public ConnectionState(
|
||||||
int libraryVersion,
|
int libraryVersion,
|
||||||
int sessionInterfaceVersion,
|
int sessionInterfaceVersion,
|
||||||
@ -68,7 +71,8 @@ import java.util.List;
|
|||||||
Player.Commands playerCommandsFromPlayer,
|
Player.Commands playerCommandsFromPlayer,
|
||||||
Bundle tokenExtras,
|
Bundle tokenExtras,
|
||||||
Bundle sessionExtras,
|
Bundle sessionExtras,
|
||||||
PlayerInfo playerInfo) {
|
PlayerInfo playerInfo,
|
||||||
|
@Nullable Token platformToken) {
|
||||||
this.libraryVersion = libraryVersion;
|
this.libraryVersion = libraryVersion;
|
||||||
this.sessionInterfaceVersion = sessionInterfaceVersion;
|
this.sessionInterfaceVersion = sessionInterfaceVersion;
|
||||||
this.sessionBinder = sessionBinder;
|
this.sessionBinder = sessionBinder;
|
||||||
@ -80,6 +84,7 @@ import java.util.List;
|
|||||||
this.tokenExtras = tokenExtras;
|
this.tokenExtras = tokenExtras;
|
||||||
this.sessionExtras = sessionExtras;
|
this.sessionExtras = sessionExtras;
|
||||||
this.playerInfo = playerInfo;
|
this.playerInfo = playerInfo;
|
||||||
|
this.platformToken = platformToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String FIELD_LIBRARY_VERSION = Util.intToStringMaxRadix(0);
|
private static final String FIELD_LIBRARY_VERSION = Util.intToStringMaxRadix(0);
|
||||||
@ -94,8 +99,9 @@ import java.util.List;
|
|||||||
private static final String FIELD_PLAYER_INFO = Util.intToStringMaxRadix(7);
|
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_SESSION_INTERFACE_VERSION = Util.intToStringMaxRadix(8);
|
||||||
private static final String FIELD_IN_PROCESS_BINDER = Util.intToStringMaxRadix(10);
|
private static final String FIELD_IN_PROCESS_BINDER = Util.intToStringMaxRadix(10);
|
||||||
|
private static final String FIELD_PLATFORM_TOKEN = Util.intToStringMaxRadix(12);
|
||||||
|
|
||||||
// Next field key = 12
|
// Next field key = 13
|
||||||
|
|
||||||
public Bundle toBundleForRemoteProcess(int controllerInterfaceVersion) {
|
public Bundle toBundleForRemoteProcess(int controllerInterfaceVersion) {
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
@ -121,6 +127,9 @@ import java.util.List;
|
|||||||
intersectedCommands, /* excludeTimeline= */ false, /* excludeTracks= */ false)
|
intersectedCommands, /* excludeTimeline= */ false, /* excludeTracks= */ false)
|
||||||
.toBundleForRemoteProcess(controllerInterfaceVersion));
|
.toBundleForRemoteProcess(controllerInterfaceVersion));
|
||||||
bundle.putInt(FIELD_SESSION_INTERFACE_VERSION, sessionInterfaceVersion);
|
bundle.putInt(FIELD_SESSION_INTERFACE_VERSION, sessionInterfaceVersion);
|
||||||
|
if (platformToken != null) {
|
||||||
|
bundle.putParcelable(FIELD_PLATFORM_TOKEN, platformToken);
|
||||||
|
}
|
||||||
return bundle;
|
return bundle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,6 +185,7 @@ import java.util.List;
|
|||||||
playerInfoBundle == null
|
playerInfoBundle == null
|
||||||
? PlayerInfo.DEFAULT
|
? PlayerInfo.DEFAULT
|
||||||
: PlayerInfo.fromBundle(playerInfoBundle, sessionInterfaceVersion);
|
: PlayerInfo.fromBundle(playerInfoBundle, sessionInterfaceVersion);
|
||||||
|
@Nullable Token platformToken = bundle.getParcelable(FIELD_PLATFORM_TOKEN);
|
||||||
return new ConnectionState(
|
return new ConnectionState(
|
||||||
libraryVersion,
|
libraryVersion,
|
||||||
sessionInterfaceVersion,
|
sessionInterfaceVersion,
|
||||||
@ -187,7 +197,8 @@ import java.util.List;
|
|||||||
playerCommandsFromPlayer,
|
playerCommandsFromPlayer,
|
||||||
tokenExtras == null ? Bundle.EMPTY : tokenExtras,
|
tokenExtras == null ? Bundle.EMPTY : tokenExtras,
|
||||||
sessionExtras == null ? Bundle.EMPTY : sessionExtras,
|
sessionExtras == null ? Bundle.EMPTY : sessionExtras,
|
||||||
playerInfo);
|
playerInfo,
|
||||||
|
platformToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class InProcessBinder extends Binder {
|
private final class InProcessBinder extends Binder {
|
||||||
|
@ -36,6 +36,7 @@ import android.content.Intent;
|
|||||||
import android.content.ServiceConnection;
|
import android.content.ServiceConnection;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.graphics.SurfaceTexture;
|
import android.graphics.SurfaceTexture;
|
||||||
|
import android.media.session.MediaSession;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
@ -2619,6 +2620,8 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
|||||||
CommandButton.copyWithUnavailableButtonsDisabled(
|
CommandButton.copyWithUnavailableButtonsDisabled(
|
||||||
result.customLayout, sessionCommands, intersectedPlayerCommands);
|
result.customLayout, sessionCommands, intersectedPlayerCommands);
|
||||||
playerInfo = result.playerInfo;
|
playerInfo = result.playerInfo;
|
||||||
|
MediaSession.Token platformToken =
|
||||||
|
result.platformToken == null ? token.getPlatformToken() : result.platformToken;
|
||||||
try {
|
try {
|
||||||
// Implementation for the local binder is no-op,
|
// Implementation for the local binder is no-op,
|
||||||
// so can be used without worrying about deadlock.
|
// so can be used without worrying about deadlock.
|
||||||
@ -2635,7 +2638,8 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
|||||||
result.sessionInterfaceVersion,
|
result.sessionInterfaceVersion,
|
||||||
token.getPackageName(),
|
token.getPackageName(),
|
||||||
result.sessionBinder,
|
result.sessionBinder,
|
||||||
result.tokenExtras);
|
result.tokenExtras,
|
||||||
|
platformToken);
|
||||||
sessionExtras = result.sessionExtras;
|
sessionExtras = result.sessionExtras;
|
||||||
getInstance().notifyAccepted();
|
getInstance().notifyAccepted();
|
||||||
}
|
}
|
||||||
|
@ -830,7 +830,7 @@ public class MediaSession {
|
|||||||
return impl.getId();
|
return impl.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the {@link SessionToken} for creating {@link MediaController}. */
|
/** Returns the {@link SessionToken} for creating {@link MediaController} instances. */
|
||||||
public final SessionToken getToken() {
|
public final SessionToken getToken() {
|
||||||
return impl.getToken();
|
return impl.getToken();
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@ import android.content.ComponentName;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.media.session.MediaSession.Token;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.DeadObjectException;
|
import android.os.DeadObjectException;
|
||||||
@ -215,6 +216,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
.appendPath(id)
|
.appendPath(id)
|
||||||
.appendPath(String.valueOf(SystemClock.elapsedRealtime()))
|
.appendPath(String.valueOf(SystemClock.elapsedRealtime()))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
sessionLegacyStub =
|
||||||
|
new MediaSessionLegacyStub(
|
||||||
|
/* session= */ thisRef, sessionUri, applicationHandler, tokenExtras);
|
||||||
|
|
||||||
|
Token platformToken = (Token) sessionLegacyStub.getSessionCompat().getSessionToken().getToken();
|
||||||
sessionToken =
|
sessionToken =
|
||||||
new SessionToken(
|
new SessionToken(
|
||||||
Process.myUid(),
|
Process.myUid(),
|
||||||
@ -223,10 +230,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
MediaSessionStub.VERSION_INT,
|
MediaSessionStub.VERSION_INT,
|
||||||
context.getPackageName(),
|
context.getPackageName(),
|
||||||
sessionStub,
|
sessionStub,
|
||||||
tokenExtras);
|
tokenExtras,
|
||||||
|
platformToken);
|
||||||
|
|
||||||
sessionLegacyStub =
|
|
||||||
new MediaSessionLegacyStub(/* session= */ thisRef, sessionUri, applicationHandler);
|
|
||||||
// For PlayerWrapper, use the same default commands as the proxy controller gets when the app
|
// For PlayerWrapper, use the same default commands as the proxy controller gets when the app
|
||||||
// doesn't overrides the default commands in `onConnect`. When the default is overridden by the
|
// doesn't overrides the default commands in `onConnect`. When the default is overridden by the
|
||||||
// app in `onConnect`, the default set here will be overridden with these values.
|
// app in `onConnect`, the default set here will be overridden with these values.
|
||||||
|
@ -134,7 +134,8 @@ import org.checkerframework.checker.initialization.qual.Initialized;
|
|||||||
private int sessionFlags;
|
private int sessionFlags;
|
||||||
|
|
||||||
@SuppressWarnings("PendingIntentMutability") // We can't use SaferPendingIntent
|
@SuppressWarnings("PendingIntentMutability") // We can't use SaferPendingIntent
|
||||||
public MediaSessionLegacyStub(MediaSessionImpl session, Uri sessionUri, Handler handler) {
|
public MediaSessionLegacyStub(
|
||||||
|
MediaSessionImpl session, Uri sessionUri, Handler handler, Bundle tokenExtras) {
|
||||||
sessionImpl = session;
|
sessionImpl = session;
|
||||||
Context context = sessionImpl.getContext();
|
Context context = sessionImpl.getContext();
|
||||||
sessionManager = MediaSessionManager.getSessionManager(context);
|
sessionManager = MediaSessionManager.getSessionManager(context);
|
||||||
@ -204,7 +205,7 @@ import org.checkerframework.checker.initialization.qual.Initialized;
|
|||||||
sessionCompatId,
|
sessionCompatId,
|
||||||
Util.SDK_INT < 31 ? receiverComponentName : null,
|
Util.SDK_INT < 31 ? receiverComponentName : null,
|
||||||
Util.SDK_INT < 31 ? mediaButtonIntent : null,
|
Util.SDK_INT < 31 ? mediaButtonIntent : null,
|
||||||
session.getToken().getExtras());
|
/* sessionInfo= */ tokenExtras);
|
||||||
if (Util.SDK_INT >= 31 && broadcastReceiverComponentName != null) {
|
if (Util.SDK_INT >= 31 && broadcastReceiverComponentName != null) {
|
||||||
Api31.setMediaButtonBroadcastReceiver(sessionCompat, broadcastReceiverComponentName);
|
Api31.setMediaButtonBroadcastReceiver(sessionCompat, broadcastReceiverComponentName);
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,7 @@ import static androidx.media3.session.SessionError.ERROR_UNKNOWN;
|
|||||||
import static androidx.media3.session.SessionError.INFO_CANCELLED;
|
import static androidx.media3.session.SessionError.INFO_CANCELLED;
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
|
import android.media.session.MediaSession.Token;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
@ -521,6 +522,8 @@ import java.util.concurrent.ExecutionException;
|
|||||||
PlayerWrapper playerWrapper = sessionImpl.getPlayerWrapper();
|
PlayerWrapper playerWrapper = sessionImpl.getPlayerWrapper();
|
||||||
PlayerInfo playerInfo = playerWrapper.createPlayerInfoForBundling();
|
PlayerInfo playerInfo = playerWrapper.createPlayerInfoForBundling();
|
||||||
playerInfo = generateAndCacheUniqueTrackGroupIds(playerInfo);
|
playerInfo = generateAndCacheUniqueTrackGroupIds(playerInfo);
|
||||||
|
Token platformToken =
|
||||||
|
(Token) sessionImpl.getSessionCompat().getSessionToken().getToken();
|
||||||
ConnectionState state =
|
ConnectionState state =
|
||||||
new ConnectionState(
|
new ConnectionState(
|
||||||
MediaLibraryInfo.VERSION_INT,
|
MediaLibraryInfo.VERSION_INT,
|
||||||
@ -539,7 +542,8 @@ import java.util.concurrent.ExecutionException;
|
|||||||
connectionResult.sessionExtras != null
|
connectionResult.sessionExtras != null
|
||||||
? connectionResult.sessionExtras
|
? connectionResult.sessionExtras
|
||||||
: sessionImpl.getSessionExtras(),
|
: sessionImpl.getSessionExtras(),
|
||||||
playerInfo);
|
playerInfo,
|
||||||
|
platformToken);
|
||||||
|
|
||||||
// Double check if session is still there, because release() can be called in
|
// Double check if session is still there, because release() can be called in
|
||||||
// another thread.
|
// another thread.
|
||||||
|
@ -25,6 +25,7 @@ import android.content.Intent;
|
|||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.ResolveInfo;
|
import android.content.pm.ResolveInfo;
|
||||||
import android.content.pm.ServiceInfo;
|
import android.content.pm.ServiceInfo;
|
||||||
|
import android.media.session.MediaSession.Token;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.HandlerThread;
|
import android.os.HandlerThread;
|
||||||
@ -147,10 +148,18 @@ public final class SessionToken {
|
|||||||
int interfaceVersion,
|
int interfaceVersion,
|
||||||
String packageName,
|
String packageName,
|
||||||
IMediaSession iSession,
|
IMediaSession iSession,
|
||||||
Bundle tokenExtras) {
|
Bundle tokenExtras,
|
||||||
|
@Nullable Token platformToken) {
|
||||||
impl =
|
impl =
|
||||||
new SessionTokenImplBase(
|
new SessionTokenImplBase(
|
||||||
uid, type, libraryVersion, interfaceVersion, packageName, iSession, tokenExtras);
|
uid,
|
||||||
|
type,
|
||||||
|
libraryVersion,
|
||||||
|
interfaceVersion,
|
||||||
|
packageName,
|
||||||
|
iSession,
|
||||||
|
tokenExtras,
|
||||||
|
platformToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a session token connected to a legacy media session. */
|
/** Creates a session token connected to a legacy media session. */
|
||||||
@ -158,12 +167,12 @@ public final class SessionToken {
|
|||||||
this.impl = new SessionTokenImplLegacy(token, packageName, uid, extras);
|
this.impl = new SessionTokenImplLegacy(token, packageName, uid, extras);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SessionToken(Bundle bundle) {
|
private SessionToken(Bundle bundle, @Nullable Token platformToken) {
|
||||||
checkArgument(bundle.containsKey(FIELD_IMPL_TYPE), "Impl type needs to be set.");
|
checkArgument(bundle.containsKey(FIELD_IMPL_TYPE), "Impl type needs to be set.");
|
||||||
@SessionTokenImplType int implType = bundle.getInt(FIELD_IMPL_TYPE);
|
@SessionTokenImplType int implType = bundle.getInt(FIELD_IMPL_TYPE);
|
||||||
Bundle implBundle = checkNotNull(bundle.getBundle(FIELD_IMPL));
|
Bundle implBundle = checkNotNull(bundle.getBundle(FIELD_IMPL));
|
||||||
if (implType == IMPL_TYPE_BASE) {
|
if (implType == IMPL_TYPE_BASE) {
|
||||||
impl = SessionTokenImplBase.fromBundle(implBundle);
|
impl = SessionTokenImplBase.fromBundle(implBundle, platformToken);
|
||||||
} else {
|
} else {
|
||||||
impl = SessionTokenImplLegacy.fromBundle(implBundle);
|
impl = SessionTokenImplLegacy.fromBundle(implBundle);
|
||||||
}
|
}
|
||||||
@ -265,12 +274,17 @@ public final class SessionToken {
|
|||||||
return impl.getBinder();
|
return impl.getBinder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable /* package */
|
||||||
|
Token getPlatformToken() {
|
||||||
|
return impl.getPlatformToken();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a token from a {@link android.media.session.MediaSession.Token} or {@code
|
* Creates a token from a {@link Token} or {@code
|
||||||
* android.support.v4.media.session.MediaSessionCompat.Token}.
|
* android.support.v4.media.session.MediaSessionCompat.Token}.
|
||||||
*
|
*
|
||||||
* @param context A {@link Context}.
|
* @param context A {@link Context}.
|
||||||
* @param token The {@link android.media.session.MediaSession.Token} or {@code
|
* @param token The {@link Token} or {@code
|
||||||
* android.support.v4.media.session.MediaSessionCompat.Token}.
|
* android.support.v4.media.session.MediaSessionCompat.Token}.
|
||||||
* @return A {@link ListenableFuture} for the {@link SessionToken}.
|
* @return A {@link ListenableFuture} for the {@link SessionToken}.
|
||||||
*/
|
*/
|
||||||
@ -281,11 +295,11 @@ public final class SessionToken {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a token from a {@link android.media.session.MediaSession.Token} or {@code
|
* Creates a token from a {@link Token} or {@code
|
||||||
* android.support.v4.media.session.MediaSessionCompat.Token}.
|
* android.support.v4.media.session.MediaSessionCompat.Token}.
|
||||||
*
|
*
|
||||||
* @param context A {@link Context}.
|
* @param context A {@link Context}.
|
||||||
* @param token The {@link android.media.session.MediaSession.Token} or {@code
|
* @param token The {@link Token} or {@code
|
||||||
* android.support.v4.media.session.MediaSessionCompat.Token}..
|
* android.support.v4.media.session.MediaSessionCompat.Token}..
|
||||||
* @param completionLooper The {@link Looper} on which the returned {@link ListenableFuture}
|
* @param completionLooper The {@link Looper} on which the returned {@link ListenableFuture}
|
||||||
* completes. This {@link Looper} can't be used to call {@code future.get()} on the returned
|
* completes. This {@link Looper} can't be used to call {@code future.get()} on the returned
|
||||||
@ -300,7 +314,7 @@ public final class SessionToken {
|
|||||||
|
|
||||||
private static MediaSessionCompat.Token createCompatToken(
|
private static MediaSessionCompat.Token createCompatToken(
|
||||||
Parcelable platformOrLegacyCompatToken) {
|
Parcelable platformOrLegacyCompatToken) {
|
||||||
if (platformOrLegacyCompatToken instanceof android.media.session.MediaSession.Token) {
|
if (platformOrLegacyCompatToken instanceof Token) {
|
||||||
return MediaSessionCompat.Token.fromToken(platformOrLegacyCompatToken);
|
return MediaSessionCompat.Token.fromToken(platformOrLegacyCompatToken);
|
||||||
}
|
}
|
||||||
// Assume this is an android.support.v4.media.session.MediaSessionCompat.Token.
|
// Assume this is an android.support.v4.media.session.MediaSessionCompat.Token.
|
||||||
@ -346,7 +360,7 @@ public final class SessionToken {
|
|||||||
// Remove timeout callback.
|
// Remove timeout callback.
|
||||||
handler.removeCallbacksAndMessages(null);
|
handler.removeCallbacksAndMessages(null);
|
||||||
try {
|
try {
|
||||||
future.set(SessionToken.fromBundle(resultData));
|
future.set(SessionToken.fromBundle(resultData, (Token) compatToken.getToken()));
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
// Fallback to a legacy token if we receive an unexpected result, e.g. a legacy
|
// Fallback to a legacy token if we receive an unexpected result, e.g. a legacy
|
||||||
// session acknowledging commands by a success callback.
|
// session acknowledging commands by a success callback.
|
||||||
@ -476,6 +490,9 @@ public final class SessionToken {
|
|||||||
Object getBinder();
|
Object getBinder();
|
||||||
|
|
||||||
Bundle toBundle();
|
Bundle toBundle();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
Token getPlatformToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String FIELD_IMPL_TYPE = Util.intToStringMaxRadix(0);
|
private static final String FIELD_IMPL_TYPE = Util.intToStringMaxRadix(0);
|
||||||
@ -506,6 +523,14 @@ public final class SessionToken {
|
|||||||
/** Restores a {@code SessionToken} from a {@link Bundle}. */
|
/** Restores a {@code SessionToken} from a {@link Bundle}. */
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public static SessionToken fromBundle(Bundle bundle) {
|
public static SessionToken fromBundle(Bundle bundle) {
|
||||||
return new SessionToken(bundle);
|
return new SessionToken(bundle, /* platformToken= */ null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restores a {@code SessionToken} from a {@link Bundle}, setting the provided {@code
|
||||||
|
* platformToken} if not already set.
|
||||||
|
*/
|
||||||
|
private static SessionToken fromBundle(Bundle bundle, Token platformToken) {
|
||||||
|
return new SessionToken(bundle, platformToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ import static androidx.media3.common.util.Assertions.checkNotEmpty;
|
|||||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
|
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
|
import android.media.session.MediaSession;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
@ -48,6 +49,8 @@ import com.google.common.base.Objects;
|
|||||||
|
|
||||||
private final Bundle extras;
|
private final Bundle extras;
|
||||||
|
|
||||||
|
@Nullable private final MediaSession.Token platformToken;
|
||||||
|
|
||||||
public SessionTokenImplBase(ComponentName serviceComponent, int uid, int type) {
|
public SessionTokenImplBase(ComponentName serviceComponent, int uid, int type) {
|
||||||
this(
|
this(
|
||||||
uid,
|
uid,
|
||||||
@ -58,7 +61,8 @@ import com.google.common.base.Objects;
|
|||||||
/* serviceName= */ serviceComponent.getClassName(),
|
/* serviceName= */ serviceComponent.getClassName(),
|
||||||
/* componentName= */ serviceComponent,
|
/* componentName= */ serviceComponent,
|
||||||
/* iSession= */ null,
|
/* iSession= */ null,
|
||||||
/* extras= */ Bundle.EMPTY);
|
/* extras= */ Bundle.EMPTY,
|
||||||
|
/* platformToken= */ null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SessionTokenImplBase(
|
public SessionTokenImplBase(
|
||||||
@ -68,7 +72,8 @@ import com.google.common.base.Objects;
|
|||||||
int interfaceVersion,
|
int interfaceVersion,
|
||||||
String packageName,
|
String packageName,
|
||||||
IMediaSession iSession,
|
IMediaSession iSession,
|
||||||
Bundle tokenExtras) {
|
Bundle tokenExtras,
|
||||||
|
@Nullable MediaSession.Token platformToken) {
|
||||||
this(
|
this(
|
||||||
uid,
|
uid,
|
||||||
type,
|
type,
|
||||||
@ -78,7 +83,8 @@ import com.google.common.base.Objects;
|
|||||||
/* serviceName= */ "",
|
/* serviceName= */ "",
|
||||||
/* componentName= */ null,
|
/* componentName= */ null,
|
||||||
iSession.asBinder(),
|
iSession.asBinder(),
|
||||||
checkNotNull(tokenExtras));
|
checkNotNull(tokenExtras),
|
||||||
|
platformToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SessionTokenImplBase(
|
private SessionTokenImplBase(
|
||||||
@ -90,7 +96,8 @@ import com.google.common.base.Objects;
|
|||||||
String serviceName,
|
String serviceName,
|
||||||
@Nullable ComponentName componentName,
|
@Nullable ComponentName componentName,
|
||||||
@Nullable IBinder iSession,
|
@Nullable IBinder iSession,
|
||||||
Bundle extras) {
|
Bundle extras,
|
||||||
|
@Nullable MediaSession.Token platformToken) {
|
||||||
this.uid = uid;
|
this.uid = uid;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.libraryVersion = libraryVersion;
|
this.libraryVersion = libraryVersion;
|
||||||
@ -100,6 +107,7 @@ import com.google.common.base.Objects;
|
|||||||
this.componentName = componentName;
|
this.componentName = componentName;
|
||||||
this.iSession = iSession;
|
this.iSession = iSession;
|
||||||
this.extras = extras;
|
this.extras = extras;
|
||||||
|
this.platformToken = platformToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -112,7 +120,8 @@ import com.google.common.base.Objects;
|
|||||||
packageName,
|
packageName,
|
||||||
serviceName,
|
serviceName,
|
||||||
componentName,
|
componentName,
|
||||||
iSession);
|
iSession,
|
||||||
|
platformToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -127,8 +136,9 @@ import com.google.common.base.Objects;
|
|||||||
&& interfaceVersion == other.interfaceVersion
|
&& interfaceVersion == other.interfaceVersion
|
||||||
&& TextUtils.equals(packageName, other.packageName)
|
&& TextUtils.equals(packageName, other.packageName)
|
||||||
&& TextUtils.equals(serviceName, other.serviceName)
|
&& TextUtils.equals(serviceName, other.serviceName)
|
||||||
&& Util.areEqual(componentName, other.componentName)
|
&& Objects.equal(componentName, other.componentName)
|
||||||
&& Util.areEqual(iSession, other.iSession);
|
&& Objects.equal(iSession, other.iSession)
|
||||||
|
&& Objects.equal(platformToken, other.platformToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -203,6 +213,12 @@ import com.google.common.base.Objects;
|
|||||||
return iSession;
|
return iSession;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public MediaSession.Token getPlatformToken() {
|
||||||
|
return platformToken;
|
||||||
|
}
|
||||||
|
|
||||||
private static final String FIELD_UID = Util.intToStringMaxRadix(0);
|
private static final String FIELD_UID = Util.intToStringMaxRadix(0);
|
||||||
private static final String FIELD_TYPE = Util.intToStringMaxRadix(1);
|
private static final String FIELD_TYPE = Util.intToStringMaxRadix(1);
|
||||||
private static final String FIELD_LIBRARY_VERSION = Util.intToStringMaxRadix(2);
|
private static final String FIELD_LIBRARY_VERSION = Util.intToStringMaxRadix(2);
|
||||||
@ -212,8 +228,9 @@ import com.google.common.base.Objects;
|
|||||||
private static final String FIELD_ISESSION = Util.intToStringMaxRadix(6);
|
private static final String FIELD_ISESSION = Util.intToStringMaxRadix(6);
|
||||||
private static final String FIELD_EXTRAS = Util.intToStringMaxRadix(7);
|
private static final String FIELD_EXTRAS = Util.intToStringMaxRadix(7);
|
||||||
private static final String FIELD_INTERFACE_VERSION = Util.intToStringMaxRadix(8);
|
private static final String FIELD_INTERFACE_VERSION = Util.intToStringMaxRadix(8);
|
||||||
|
private static final String FIELD_PLATFORM_TOKEN = Util.intToStringMaxRadix(9);
|
||||||
|
|
||||||
// Next field key = 9
|
// Next field key = 10
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Bundle toBundle() {
|
public Bundle toBundle() {
|
||||||
@ -227,11 +244,15 @@ import com.google.common.base.Objects;
|
|||||||
bundle.putParcelable(FIELD_COMPONENT_NAME, componentName);
|
bundle.putParcelable(FIELD_COMPONENT_NAME, componentName);
|
||||||
bundle.putBundle(FIELD_EXTRAS, extras);
|
bundle.putBundle(FIELD_EXTRAS, extras);
|
||||||
bundle.putInt(FIELD_INTERFACE_VERSION, interfaceVersion);
|
bundle.putInt(FIELD_INTERFACE_VERSION, interfaceVersion);
|
||||||
|
if (platformToken != null) {
|
||||||
|
bundle.putParcelable(FIELD_PLATFORM_TOKEN, platformToken);
|
||||||
|
}
|
||||||
return bundle;
|
return bundle;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Restores a {@code SessionTokenImplBase} from a {@link Bundle}. */
|
/** Restores a {@code SessionTokenImplBase} from a {@link Bundle}. */
|
||||||
public static SessionTokenImplBase fromBundle(Bundle bundle) {
|
public static SessionTokenImplBase fromBundle(
|
||||||
|
Bundle bundle, @Nullable MediaSession.Token platformToken) {
|
||||||
checkArgument(bundle.containsKey(FIELD_UID), "uid should be set.");
|
checkArgument(bundle.containsKey(FIELD_UID), "uid should be set.");
|
||||||
int uid = bundle.getInt(FIELD_UID);
|
int uid = bundle.getInt(FIELD_UID);
|
||||||
checkArgument(bundle.containsKey(FIELD_TYPE), "type should be set.");
|
checkArgument(bundle.containsKey(FIELD_TYPE), "type should be set.");
|
||||||
@ -244,6 +265,11 @@ import com.google.common.base.Objects;
|
|||||||
@Nullable IBinder iSession = BundleCompat.getBinder(bundle, FIELD_ISESSION);
|
@Nullable IBinder iSession = BundleCompat.getBinder(bundle, FIELD_ISESSION);
|
||||||
@Nullable ComponentName componentName = bundle.getParcelable(FIELD_COMPONENT_NAME);
|
@Nullable ComponentName componentName = bundle.getParcelable(FIELD_COMPONENT_NAME);
|
||||||
@Nullable Bundle extras = bundle.getBundle(FIELD_EXTRAS);
|
@Nullable Bundle extras = bundle.getBundle(FIELD_EXTRAS);
|
||||||
|
@Nullable
|
||||||
|
MediaSession.Token platformTokenFromBundle = bundle.getParcelable(FIELD_PLATFORM_TOKEN);
|
||||||
|
if (platformTokenFromBundle != null) {
|
||||||
|
platformToken = platformTokenFromBundle;
|
||||||
|
}
|
||||||
return new SessionTokenImplBase(
|
return new SessionTokenImplBase(
|
||||||
uid,
|
uid,
|
||||||
type,
|
type,
|
||||||
@ -253,6 +279,7 @@ import com.google.common.base.Objects;
|
|||||||
serviceName,
|
serviceName,
|
||||||
componentName,
|
componentName,
|
||||||
iSession,
|
iSession,
|
||||||
extras == null ? Bundle.EMPTY : extras);
|
extras == null ? Bundle.EMPTY : extras,
|
||||||
|
platformToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ import static androidx.media3.session.SessionToken.TYPE_SESSION;
|
|||||||
import static androidx.media3.session.SessionToken.TYPE_SESSION_LEGACY;
|
import static androidx.media3.session.SessionToken.TYPE_SESSION_LEGACY;
|
||||||
|
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
|
import android.media.session.MediaSession;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
@ -168,6 +169,12 @@ import com.google.common.base.Objects;
|
|||||||
return legacyToken;
|
return legacyToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public MediaSession.Token getPlatformToken() {
|
||||||
|
return legacyToken == null ? null : (MediaSession.Token) legacyToken.getToken();
|
||||||
|
}
|
||||||
|
|
||||||
private static final String FIELD_LEGACY_TOKEN = Util.intToStringMaxRadix(0);
|
private static final String FIELD_LEGACY_TOKEN = Util.intToStringMaxRadix(0);
|
||||||
private static final String FIELD_UID = Util.intToStringMaxRadix(1);
|
private static final String FIELD_UID = Util.intToStringMaxRadix(1);
|
||||||
private static final String FIELD_TYPE = Util.intToStringMaxRadix(2);
|
private static final String FIELD_TYPE = Util.intToStringMaxRadix(2);
|
||||||
|
@ -79,6 +79,15 @@ public class MediaBrowserListenerTest extends MediaControllerListenerTest {
|
|||||||
return (MediaBrowser) controllerTestRule.createController(token, connectionHints, listener);
|
return (MediaBrowser) controllerTestRule.createController(token, connectionHints, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getConnectedToken_returnSessionToken() throws Exception {
|
||||||
|
MediaBrowser browser = createBrowser();
|
||||||
|
|
||||||
|
assertThat(browser.getConnectedToken().isLegacySession()).isFalse();
|
||||||
|
assertThat(browser.getConnectedToken().getType()).isEqualTo(SessionToken.TYPE_SESSION);
|
||||||
|
assertThat(browser.getConnectedToken().getPlatformToken()).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getLibraryRoot() throws Exception {
|
public void getLibraryRoot() throws Exception {
|
||||||
LibraryParams params =
|
LibraryParams params =
|
||||||
|
@ -35,6 +35,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
|
|||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.ClassRule;
|
import org.junit.ClassRule;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
@ -148,19 +149,27 @@ public class SessionTokenTest {
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
// TODO(b/194458970): Make the callback of session and controller on the same thread work and
|
// TODO(b/194458970): Make the callback of session and controller on the same thread work and
|
||||||
// remove the threadTestRule
|
// remove the threadTestRule
|
||||||
|
AtomicReference<android.media.session.MediaSession.Token> platformToken =
|
||||||
|
new AtomicReference<>();
|
||||||
MediaSession session =
|
MediaSession session =
|
||||||
threadTestRule
|
threadTestRule
|
||||||
.getHandler()
|
.getHandler()
|
||||||
.postAndSync(
|
.postAndSync(
|
||||||
() ->
|
() -> {
|
||||||
sessionTestRule.ensureReleaseAfterTest(
|
MediaSession mediaSession =
|
||||||
new MediaSession.Builder(context, new MockPlayer.Builder().build())
|
new MediaSession.Builder(context, new MockPlayer.Builder().build())
|
||||||
.setId(TAG)
|
.setId(TAG)
|
||||||
.build()));
|
.build();
|
||||||
|
platformToken.set(mediaSession.getPlatformToken());
|
||||||
|
return sessionTestRule.ensureReleaseAfterTest(mediaSession);
|
||||||
|
});
|
||||||
|
|
||||||
SessionToken token =
|
SessionToken token =
|
||||||
SessionToken.createSessionToken(context, session.getSessionCompatToken())
|
SessionToken.createSessionToken(context, session.getSessionCompatToken())
|
||||||
.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
assertThat(token.isLegacySession()).isFalse();
|
assertThat(token.isLegacySession()).isFalse();
|
||||||
|
assertThat(token.getPlatformToken()).isEqualTo(platformToken.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
Loading…
x
Reference in New Issue
Block a user