mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Sync and map fatal and non-fatal errors from and to the legacy session
A fatal `PlaybackException` is mapped to a legacy playback state in state `STATE_ERROR` with error code, message and extras. A non-fatal error sent to controllers with `MediaSession.sendError` is synced to the legacy session by setting error code and message and merging the extras while preserving the rest of the state in sync with the session player. Vice versa, a `MediaController` connected to a legacy session receives fatal errors through `Player.onPlayerErrorChanged()` and non-fatal errors through `MediaController.Listener.onError()`. Error codes are mapped in `LegacyConversions`. Values of error codes in `@SessionError.ErrorCode` come from `@PlaybackExceptino.ErrorCode` with the exception of `@SessionError.ERROR_IO` and `@SessionError.ERROR_UNKNOWN`. These already exist in `@PlaybackException.ErrorCode` and are mapped accordingly to avoid semantic duplicates. PiperOrigin-RevId: 642595517
This commit is contained in:
parent
f2bdc08b24
commit
011ed909c0
@ -38,6 +38,13 @@
|
|||||||
* Propagate extras passed to media3's
|
* Propagate extras passed to media3's
|
||||||
`MediaSession[Builder].setSessionExtras()` to a media1 controller's
|
`MediaSession[Builder].setSessionExtras()` to a media1 controller's
|
||||||
`PlaybackStateCompat.getExtras()`.
|
`PlaybackStateCompat.getExtras()`.
|
||||||
|
* Map fatal and non-fatal errors to and from the platform session. A
|
||||||
|
`PlaybackException` is mapped to a fatal error state of the
|
||||||
|
`PlaybackStateCompat`. A `SessionError` sent to the media notification
|
||||||
|
controller with `MediaSession.sendError(ControllerInfo, SessionError)`
|
||||||
|
is mapped to a non-fatal error in `PlaybackStateCompat` which means that
|
||||||
|
error code and message are set but the state of the plaftorm session
|
||||||
|
remains different to `STATE_ERROR`.
|
||||||
* UI:
|
* UI:
|
||||||
* Add customisation of various icons in `PlayerControlView` through xml
|
* Add customisation of various icons in `PlayerControlView` through xml
|
||||||
attributes to allow different drawables per `PlayerView` instance,
|
attributes to allow different drawables per `PlayerView` instance,
|
||||||
|
20
api.txt
20
api.txt
@ -624,12 +624,17 @@ package androidx.media3.common {
|
|||||||
field public static final int ERROR_CODE_AUDIO_TRACK_OFFLOAD_INIT_FAILED = 5004; // 0x138c
|
field public static final int ERROR_CODE_AUDIO_TRACK_OFFLOAD_INIT_FAILED = 5004; // 0x138c
|
||||||
field public static final int ERROR_CODE_AUDIO_TRACK_OFFLOAD_WRITE_FAILED = 5003; // 0x138b
|
field public static final int ERROR_CODE_AUDIO_TRACK_OFFLOAD_WRITE_FAILED = 5003; // 0x138b
|
||||||
field public static final int ERROR_CODE_AUDIO_TRACK_WRITE_FAILED = 5002; // 0x138a
|
field public static final int ERROR_CODE_AUDIO_TRACK_WRITE_FAILED = 5002; // 0x138a
|
||||||
|
field public static final int ERROR_CODE_AUTHENTICATION_EXPIRED = -102; // 0xffffff9a
|
||||||
|
field public static final int ERROR_CODE_BAD_VALUE = -3; // 0xfffffffd
|
||||||
field public static final int ERROR_CODE_BEHIND_LIVE_WINDOW = 1002; // 0x3ea
|
field public static final int ERROR_CODE_BEHIND_LIVE_WINDOW = 1002; // 0x3ea
|
||||||
|
field public static final int ERROR_CODE_CONCURRENT_STREAM_LIMIT = -104; // 0xffffff98
|
||||||
|
field public static final int ERROR_CODE_CONTENT_ALREADY_PLAYING = -110; // 0xffffff92
|
||||||
field public static final int ERROR_CODE_DECODER_INIT_FAILED = 4001; // 0xfa1
|
field public static final int ERROR_CODE_DECODER_INIT_FAILED = 4001; // 0xfa1
|
||||||
field public static final int ERROR_CODE_DECODER_QUERY_FAILED = 4002; // 0xfa2
|
field public static final int ERROR_CODE_DECODER_QUERY_FAILED = 4002; // 0xfa2
|
||||||
field public static final int ERROR_CODE_DECODING_FAILED = 4003; // 0xfa3
|
field public static final int ERROR_CODE_DECODING_FAILED = 4003; // 0xfa3
|
||||||
field public static final int ERROR_CODE_DECODING_FORMAT_EXCEEDS_CAPABILITIES = 4004; // 0xfa4
|
field public static final int ERROR_CODE_DECODING_FORMAT_EXCEEDS_CAPABILITIES = 4004; // 0xfa4
|
||||||
field public static final int ERROR_CODE_DECODING_FORMAT_UNSUPPORTED = 4005; // 0xfa5
|
field public static final int ERROR_CODE_DECODING_FORMAT_UNSUPPORTED = 4005; // 0xfa5
|
||||||
|
field public static final int ERROR_CODE_DISCONNECTED = -100; // 0xffffff9c
|
||||||
field public static final int ERROR_CODE_DRM_CONTENT_ERROR = 6003; // 0x1773
|
field public static final int ERROR_CODE_DRM_CONTENT_ERROR = 6003; // 0x1773
|
||||||
field public static final int ERROR_CODE_DRM_DEVICE_REVOKED = 6007; // 0x1777
|
field public static final int ERROR_CODE_DRM_DEVICE_REVOKED = 6007; // 0x1777
|
||||||
field public static final int ERROR_CODE_DRM_DISALLOWED_OPERATION = 6005; // 0x1775
|
field public static final int ERROR_CODE_DRM_DISALLOWED_OPERATION = 6005; // 0x1775
|
||||||
@ -639,7 +644,9 @@ package androidx.media3.common {
|
|||||||
field public static final int ERROR_CODE_DRM_SCHEME_UNSUPPORTED = 6001; // 0x1771
|
field public static final int ERROR_CODE_DRM_SCHEME_UNSUPPORTED = 6001; // 0x1771
|
||||||
field public static final int ERROR_CODE_DRM_SYSTEM_ERROR = 6006; // 0x1776
|
field public static final int ERROR_CODE_DRM_SYSTEM_ERROR = 6006; // 0x1776
|
||||||
field public static final int ERROR_CODE_DRM_UNSPECIFIED = 6000; // 0x1770
|
field public static final int ERROR_CODE_DRM_UNSPECIFIED = 6000; // 0x1770
|
||||||
|
field public static final int ERROR_CODE_END_OF_PLAYLIST = -109; // 0xffffff93
|
||||||
field public static final int ERROR_CODE_FAILED_RUNTIME_CHECK = 1004; // 0x3ec
|
field public static final int ERROR_CODE_FAILED_RUNTIME_CHECK = 1004; // 0x3ec
|
||||||
|
field public static final int ERROR_CODE_INVALID_STATE = -2; // 0xfffffffe
|
||||||
field public static final int ERROR_CODE_IO_BAD_HTTP_STATUS = 2004; // 0x7d4
|
field public static final int ERROR_CODE_IO_BAD_HTTP_STATUS = 2004; // 0x7d4
|
||||||
field public static final int ERROR_CODE_IO_CLEARTEXT_NOT_PERMITTED = 2007; // 0x7d7
|
field public static final int ERROR_CODE_IO_CLEARTEXT_NOT_PERMITTED = 2007; // 0x7d7
|
||||||
field public static final int ERROR_CODE_IO_FILE_NOT_FOUND = 2005; // 0x7d5
|
field public static final int ERROR_CODE_IO_FILE_NOT_FOUND = 2005; // 0x7d5
|
||||||
@ -649,18 +656,25 @@ package androidx.media3.common {
|
|||||||
field public static final int ERROR_CODE_IO_NO_PERMISSION = 2006; // 0x7d6
|
field public static final int ERROR_CODE_IO_NO_PERMISSION = 2006; // 0x7d6
|
||||||
field public static final int ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE = 2008; // 0x7d8
|
field public static final int ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE = 2008; // 0x7d8
|
||||||
field public static final int ERROR_CODE_IO_UNSPECIFIED = 2000; // 0x7d0
|
field public static final int ERROR_CODE_IO_UNSPECIFIED = 2000; // 0x7d0
|
||||||
|
field public static final int ERROR_CODE_NOT_AVAILABLE_IN_REGION = -106; // 0xffffff96
|
||||||
|
field public static final int ERROR_CODE_NOT_SUPPORTED = -6; // 0xfffffffa
|
||||||
|
field public static final int ERROR_CODE_PARENTAL_CONTROL_RESTRICTED = -105; // 0xffffff97
|
||||||
field public static final int ERROR_CODE_PARSING_CONTAINER_MALFORMED = 3001; // 0xbb9
|
field public static final int ERROR_CODE_PARSING_CONTAINER_MALFORMED = 3001; // 0xbb9
|
||||||
field public static final int ERROR_CODE_PARSING_CONTAINER_UNSUPPORTED = 3003; // 0xbbb
|
field public static final int ERROR_CODE_PARSING_CONTAINER_UNSUPPORTED = 3003; // 0xbbb
|
||||||
field public static final int ERROR_CODE_PARSING_MANIFEST_MALFORMED = 3002; // 0xbba
|
field public static final int ERROR_CODE_PARSING_MANIFEST_MALFORMED = 3002; // 0xbba
|
||||||
field public static final int ERROR_CODE_PARSING_MANIFEST_UNSUPPORTED = 3004; // 0xbbc
|
field public static final int ERROR_CODE_PARSING_MANIFEST_UNSUPPORTED = 3004; // 0xbbc
|
||||||
|
field public static final int ERROR_CODE_PERMISSION_DENIED = -4; // 0xfffffffc
|
||||||
|
field public static final int ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED = -103; // 0xffffff99
|
||||||
field public static final int ERROR_CODE_REMOTE_ERROR = 1001; // 0x3e9
|
field public static final int ERROR_CODE_REMOTE_ERROR = 1001; // 0x3e9
|
||||||
|
field public static final int ERROR_CODE_SETUP_REQUIRED = -108; // 0xffffff94
|
||||||
|
field public static final int ERROR_CODE_SKIP_LIMIT_REACHED = -107; // 0xffffff95
|
||||||
field public static final int ERROR_CODE_TIMEOUT = 1003; // 0x3eb
|
field public static final int ERROR_CODE_TIMEOUT = 1003; // 0x3eb
|
||||||
field public static final int ERROR_CODE_UNSPECIFIED = 1000; // 0x3e8
|
field public static final int ERROR_CODE_UNSPECIFIED = 1000; // 0x3e8
|
||||||
field @androidx.media3.common.PlaybackException.ErrorCode public final int errorCode;
|
field @androidx.media3.common.PlaybackException.ErrorCode public final int errorCode;
|
||||||
field public final long timestampMs;
|
field public final long timestampMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@IntDef(open=true, value={androidx.media3.common.PlaybackException.ERROR_CODE_UNSPECIFIED, androidx.media3.common.PlaybackException.ERROR_CODE_REMOTE_ERROR, androidx.media3.common.PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW, androidx.media3.common.PlaybackException.ERROR_CODE_TIMEOUT, androidx.media3.common.PlaybackException.ERROR_CODE_FAILED_RUNTIME_CHECK, androidx.media3.common.PlaybackException.ERROR_CODE_IO_UNSPECIFIED, androidx.media3.common.PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED, androidx.media3.common.PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_TIMEOUT, androidx.media3.common.PlaybackException.ERROR_CODE_IO_INVALID_HTTP_CONTENT_TYPE, androidx.media3.common.PlaybackException.ERROR_CODE_IO_BAD_HTTP_STATUS, androidx.media3.common.PlaybackException.ERROR_CODE_IO_FILE_NOT_FOUND, androidx.media3.common.PlaybackException.ERROR_CODE_IO_NO_PERMISSION, androidx.media3.common.PlaybackException.ERROR_CODE_IO_CLEARTEXT_NOT_PERMITTED, androidx.media3.common.PlaybackException.ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE, androidx.media3.common.PlaybackException.ERROR_CODE_PARSING_CONTAINER_MALFORMED, androidx.media3.common.PlaybackException.ERROR_CODE_PARSING_MANIFEST_MALFORMED, androidx.media3.common.PlaybackException.ERROR_CODE_PARSING_CONTAINER_UNSUPPORTED, androidx.media3.common.PlaybackException.ERROR_CODE_PARSING_MANIFEST_UNSUPPORTED, androidx.media3.common.PlaybackException.ERROR_CODE_DECODER_INIT_FAILED, androidx.media3.common.PlaybackException.ERROR_CODE_DECODER_QUERY_FAILED, androidx.media3.common.PlaybackException.ERROR_CODE_DECODING_FAILED, androidx.media3.common.PlaybackException.ERROR_CODE_DECODING_FORMAT_EXCEEDS_CAPABILITIES, androidx.media3.common.PlaybackException.ERROR_CODE_DECODING_FORMAT_UNSUPPORTED, androidx.media3.common.PlaybackException.ERROR_CODE_AUDIO_TRACK_INIT_FAILED, androidx.media3.common.PlaybackException.ERROR_CODE_AUDIO_TRACK_WRITE_FAILED, androidx.media3.common.PlaybackException.ERROR_CODE_AUDIO_TRACK_OFFLOAD_INIT_FAILED, androidx.media3.common.PlaybackException.ERROR_CODE_AUDIO_TRACK_OFFLOAD_WRITE_FAILED, androidx.media3.common.PlaybackException.ERROR_CODE_DRM_UNSPECIFIED, androidx.media3.common.PlaybackException.ERROR_CODE_DRM_SCHEME_UNSUPPORTED, androidx.media3.common.PlaybackException.ERROR_CODE_DRM_PROVISIONING_FAILED, androidx.media3.common.PlaybackException.ERROR_CODE_DRM_CONTENT_ERROR, androidx.media3.common.PlaybackException.ERROR_CODE_DRM_LICENSE_ACQUISITION_FAILED, androidx.media3.common.PlaybackException.ERROR_CODE_DRM_DISALLOWED_OPERATION, androidx.media3.common.PlaybackException.ERROR_CODE_DRM_SYSTEM_ERROR, androidx.media3.common.PlaybackException.ERROR_CODE_DRM_DEVICE_REVOKED, androidx.media3.common.PlaybackException.ERROR_CODE_DRM_LICENSE_EXPIRED}) @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.TYPE_USE}) public static @interface PlaybackException.ErrorCode {
|
@IntDef(open=true, value={androidx.media3.common.PlaybackException.ERROR_CODE_INVALID_STATE, androidx.media3.common.PlaybackException.ERROR_CODE_BAD_VALUE, androidx.media3.common.PlaybackException.ERROR_CODE_PERMISSION_DENIED, androidx.media3.common.PlaybackException.ERROR_CODE_NOT_SUPPORTED, androidx.media3.common.PlaybackException.ERROR_CODE_DISCONNECTED, androidx.media3.common.PlaybackException.ERROR_CODE_AUTHENTICATION_EXPIRED, androidx.media3.common.PlaybackException.ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED, androidx.media3.common.PlaybackException.ERROR_CODE_CONCURRENT_STREAM_LIMIT, androidx.media3.common.PlaybackException.ERROR_CODE_PARENTAL_CONTROL_RESTRICTED, androidx.media3.common.PlaybackException.ERROR_CODE_NOT_AVAILABLE_IN_REGION, androidx.media3.common.PlaybackException.ERROR_CODE_SKIP_LIMIT_REACHED, androidx.media3.common.PlaybackException.ERROR_CODE_SETUP_REQUIRED, androidx.media3.common.PlaybackException.ERROR_CODE_END_OF_PLAYLIST, androidx.media3.common.PlaybackException.ERROR_CODE_CONTENT_ALREADY_PLAYING, androidx.media3.common.PlaybackException.ERROR_CODE_UNSPECIFIED, androidx.media3.common.PlaybackException.ERROR_CODE_REMOTE_ERROR, androidx.media3.common.PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW, androidx.media3.common.PlaybackException.ERROR_CODE_TIMEOUT, androidx.media3.common.PlaybackException.ERROR_CODE_FAILED_RUNTIME_CHECK, androidx.media3.common.PlaybackException.ERROR_CODE_IO_UNSPECIFIED, androidx.media3.common.PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED, androidx.media3.common.PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_TIMEOUT, androidx.media3.common.PlaybackException.ERROR_CODE_IO_INVALID_HTTP_CONTENT_TYPE, androidx.media3.common.PlaybackException.ERROR_CODE_IO_BAD_HTTP_STATUS, androidx.media3.common.PlaybackException.ERROR_CODE_IO_FILE_NOT_FOUND, androidx.media3.common.PlaybackException.ERROR_CODE_IO_NO_PERMISSION, androidx.media3.common.PlaybackException.ERROR_CODE_IO_CLEARTEXT_NOT_PERMITTED, androidx.media3.common.PlaybackException.ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE, androidx.media3.common.PlaybackException.ERROR_CODE_PARSING_CONTAINER_MALFORMED, androidx.media3.common.PlaybackException.ERROR_CODE_PARSING_MANIFEST_MALFORMED, androidx.media3.common.PlaybackException.ERROR_CODE_PARSING_CONTAINER_UNSUPPORTED, androidx.media3.common.PlaybackException.ERROR_CODE_PARSING_MANIFEST_UNSUPPORTED, androidx.media3.common.PlaybackException.ERROR_CODE_DECODER_INIT_FAILED, androidx.media3.common.PlaybackException.ERROR_CODE_DECODER_QUERY_FAILED, androidx.media3.common.PlaybackException.ERROR_CODE_DECODING_FAILED, androidx.media3.common.PlaybackException.ERROR_CODE_DECODING_FORMAT_EXCEEDS_CAPABILITIES, androidx.media3.common.PlaybackException.ERROR_CODE_DECODING_FORMAT_UNSUPPORTED, androidx.media3.common.PlaybackException.ERROR_CODE_AUDIO_TRACK_INIT_FAILED, androidx.media3.common.PlaybackException.ERROR_CODE_AUDIO_TRACK_WRITE_FAILED, androidx.media3.common.PlaybackException.ERROR_CODE_AUDIO_TRACK_OFFLOAD_INIT_FAILED, androidx.media3.common.PlaybackException.ERROR_CODE_AUDIO_TRACK_OFFLOAD_WRITE_FAILED, androidx.media3.common.PlaybackException.ERROR_CODE_DRM_UNSPECIFIED, androidx.media3.common.PlaybackException.ERROR_CODE_DRM_SCHEME_UNSUPPORTED, androidx.media3.common.PlaybackException.ERROR_CODE_DRM_PROVISIONING_FAILED, androidx.media3.common.PlaybackException.ERROR_CODE_DRM_CONTENT_ERROR, androidx.media3.common.PlaybackException.ERROR_CODE_DRM_LICENSE_ACQUISITION_FAILED, androidx.media3.common.PlaybackException.ERROR_CODE_DRM_DISALLOWED_OPERATION, androidx.media3.common.PlaybackException.ERROR_CODE_DRM_SYSTEM_ERROR, androidx.media3.common.PlaybackException.ERROR_CODE_DRM_DEVICE_REVOKED, androidx.media3.common.PlaybackException.ERROR_CODE_DRM_LICENSE_EXPIRED}) @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.TYPE_USE}) public static @interface PlaybackException.ErrorCode {
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class PlaybackParameters {
|
public final class PlaybackParameters {
|
||||||
@ -1511,7 +1525,7 @@ package androidx.media3.session {
|
|||||||
field @Nullable public final V value;
|
field @Nullable public final V value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@IntDef({androidx.media3.session.LibraryResult.RESULT_SUCCESS, androidx.media3.session.SessionError.INFO_SKIPPED, androidx.media3.session.SessionError.ERROR_UNKNOWN, androidx.media3.session.SessionError.ERROR_INVALID_STATE, androidx.media3.session.SessionError.ERROR_BAD_VALUE, androidx.media3.session.SessionError.ERROR_PERMISSION_DENIED, androidx.media3.session.SessionError.ERROR_IO, androidx.media3.session.SessionError.ERROR_SESSION_DISCONNECTED, androidx.media3.session.SessionError.ERROR_NOT_SUPPORTED, androidx.media3.session.SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED, androidx.media3.session.SessionError.ERROR_SESSION_PREMIUM_ACCOUNT_REQUIRED, androidx.media3.session.SessionError.ERROR_SESSION_CONCURRENT_STREAM_LIMIT, androidx.media3.session.SessionError.ERROR_SESSION_PARENTAL_CONTROL_RESTRICTED, androidx.media3.session.SessionError.ERROR_SESSION_NOT_AVAILABLE_IN_REGION, androidx.media3.session.SessionError.ERROR_SESSION_SKIP_LIMIT_REACHED, androidx.media3.session.SessionError.ERROR_SESSION_SETUP_REQUIRED}) @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target(java.lang.annotation.ElementType.TYPE_USE) public static @interface LibraryResult.Code {
|
@IntDef({androidx.media3.session.LibraryResult.RESULT_SUCCESS, androidx.media3.session.SessionError.INFO_CANCELLED, androidx.media3.session.SessionError.ERROR_UNKNOWN, androidx.media3.session.SessionError.ERROR_INVALID_STATE, androidx.media3.session.SessionError.ERROR_BAD_VALUE, androidx.media3.session.SessionError.ERROR_PERMISSION_DENIED, androidx.media3.session.SessionError.ERROR_IO, androidx.media3.session.SessionError.ERROR_SESSION_DISCONNECTED, androidx.media3.session.SessionError.ERROR_NOT_SUPPORTED, androidx.media3.session.SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED, androidx.media3.session.SessionError.ERROR_SESSION_PREMIUM_ACCOUNT_REQUIRED, androidx.media3.session.SessionError.ERROR_SESSION_CONCURRENT_STREAM_LIMIT, androidx.media3.session.SessionError.ERROR_SESSION_PARENTAL_CONTROL_RESTRICTED, androidx.media3.session.SessionError.ERROR_SESSION_NOT_AVAILABLE_IN_REGION, androidx.media3.session.SessionError.ERROR_SESSION_SKIP_LIMIT_REACHED, androidx.media3.session.SessionError.ERROR_SESSION_SETUP_REQUIRED}) @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target(java.lang.annotation.ElementType.TYPE_USE) public static @interface LibraryResult.Code {
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class MediaBrowser extends androidx.media3.session.MediaController {
|
public final class MediaBrowser extends androidx.media3.session.MediaController {
|
||||||
@ -1866,7 +1880,7 @@ package androidx.media3.session {
|
|||||||
field @androidx.media3.session.SessionResult.Code public final int resultCode;
|
field @androidx.media3.session.SessionResult.Code public final int resultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@IntDef({androidx.media3.session.SessionResult.RESULT_SUCCESS, androidx.media3.session.SessionError.INFO_SKIPPED, androidx.media3.session.SessionError.ERROR_UNKNOWN, androidx.media3.session.SessionError.ERROR_INVALID_STATE, androidx.media3.session.SessionError.ERROR_BAD_VALUE, androidx.media3.session.SessionError.ERROR_PERMISSION_DENIED, androidx.media3.session.SessionError.ERROR_IO, androidx.media3.session.SessionError.ERROR_SESSION_DISCONNECTED, androidx.media3.session.SessionError.ERROR_NOT_SUPPORTED, androidx.media3.session.SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED, androidx.media3.session.SessionError.ERROR_SESSION_PREMIUM_ACCOUNT_REQUIRED, androidx.media3.session.SessionError.ERROR_SESSION_CONCURRENT_STREAM_LIMIT, androidx.media3.session.SessionError.ERROR_SESSION_PARENTAL_CONTROL_RESTRICTED, androidx.media3.session.SessionError.ERROR_SESSION_NOT_AVAILABLE_IN_REGION, androidx.media3.session.SessionError.ERROR_SESSION_SKIP_LIMIT_REACHED, androidx.media3.session.SessionError.ERROR_SESSION_SETUP_REQUIRED}) @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target(java.lang.annotation.ElementType.TYPE_USE) public static @interface SessionResult.Code {
|
@IntDef({androidx.media3.session.SessionResult.RESULT_SUCCESS, androidx.media3.session.SessionError.INFO_CANCELLED, androidx.media3.session.SessionError.ERROR_UNKNOWN, androidx.media3.session.SessionError.ERROR_INVALID_STATE, androidx.media3.session.SessionError.ERROR_BAD_VALUE, androidx.media3.session.SessionError.ERROR_PERMISSION_DENIED, androidx.media3.session.SessionError.ERROR_IO, androidx.media3.session.SessionError.ERROR_SESSION_DISCONNECTED, androidx.media3.session.SessionError.ERROR_NOT_SUPPORTED, androidx.media3.session.SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED, androidx.media3.session.SessionError.ERROR_SESSION_PREMIUM_ACCOUNT_REQUIRED, androidx.media3.session.SessionError.ERROR_SESSION_CONCURRENT_STREAM_LIMIT, androidx.media3.session.SessionError.ERROR_SESSION_PARENTAL_CONTROL_RESTRICTED, androidx.media3.session.SessionError.ERROR_SESSION_NOT_AVAILABLE_IN_REGION, androidx.media3.session.SessionError.ERROR_SESSION_SKIP_LIMIT_REACHED, androidx.media3.session.SessionError.ERROR_SESSION_SETUP_REQUIRED}) @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target(java.lang.annotation.ElementType.TYPE_USE) public static @interface SessionResult.Code {
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class SessionToken {
|
public final class SessionToken {
|
||||||
|
@ -54,6 +54,20 @@ public class PlaybackException extends Exception {
|
|||||||
@IntDef(
|
@IntDef(
|
||||||
open = true,
|
open = true,
|
||||||
value = {
|
value = {
|
||||||
|
ERROR_CODE_INVALID_STATE,
|
||||||
|
ERROR_CODE_BAD_VALUE,
|
||||||
|
ERROR_CODE_PERMISSION_DENIED,
|
||||||
|
ERROR_CODE_NOT_SUPPORTED,
|
||||||
|
ERROR_CODE_DISCONNECTED,
|
||||||
|
ERROR_CODE_AUTHENTICATION_EXPIRED,
|
||||||
|
ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED,
|
||||||
|
ERROR_CODE_CONCURRENT_STREAM_LIMIT,
|
||||||
|
ERROR_CODE_PARENTAL_CONTROL_RESTRICTED,
|
||||||
|
ERROR_CODE_NOT_AVAILABLE_IN_REGION,
|
||||||
|
ERROR_CODE_SKIP_LIMIT_REACHED,
|
||||||
|
ERROR_CODE_SETUP_REQUIRED,
|
||||||
|
ERROR_CODE_END_OF_PLAYLIST,
|
||||||
|
ERROR_CODE_CONTENT_ALREADY_PLAYING,
|
||||||
ERROR_CODE_UNSPECIFIED,
|
ERROR_CODE_UNSPECIFIED,
|
||||||
ERROR_CODE_REMOTE_ERROR,
|
ERROR_CODE_REMOTE_ERROR,
|
||||||
ERROR_CODE_BEHIND_LIVE_WINDOW,
|
ERROR_CODE_BEHIND_LIVE_WINDOW,
|
||||||
@ -93,6 +107,50 @@ public class PlaybackException extends Exception {
|
|||||||
})
|
})
|
||||||
public @interface ErrorCode {}
|
public @interface ErrorCode {}
|
||||||
|
|
||||||
|
// Policy errors (-1 to -999)
|
||||||
|
|
||||||
|
/** Caused by a command that cannot be completed because the current state is not valid. */
|
||||||
|
public static final int ERROR_CODE_INVALID_STATE = -2;
|
||||||
|
|
||||||
|
/** Caused by an argument that is illegal. */
|
||||||
|
public static final int ERROR_CODE_BAD_VALUE = -3;
|
||||||
|
|
||||||
|
/** Caused by a command that is not allowed. */
|
||||||
|
public static final int ERROR_CODE_PERMISSION_DENIED = -4;
|
||||||
|
|
||||||
|
/** Caused by a command that is not supported. */
|
||||||
|
public static final int ERROR_CODE_NOT_SUPPORTED = -6;
|
||||||
|
|
||||||
|
/** Caused by a disconnected component. */
|
||||||
|
public static final int ERROR_CODE_DISCONNECTED = -100;
|
||||||
|
|
||||||
|
/** Caused by expired authentication. */
|
||||||
|
public static final int ERROR_CODE_AUTHENTICATION_EXPIRED = -102;
|
||||||
|
|
||||||
|
/** Caused by a premium account that is required but the user is not subscribed. */
|
||||||
|
public static final int ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED = -103;
|
||||||
|
|
||||||
|
/** Caused by too many concurrent streams. */
|
||||||
|
public static final int ERROR_CODE_CONCURRENT_STREAM_LIMIT = -104;
|
||||||
|
|
||||||
|
/** Caused by the content being blocked due to parental controls. */
|
||||||
|
public static final int ERROR_CODE_PARENTAL_CONTROL_RESTRICTED = -105;
|
||||||
|
|
||||||
|
/** Caused by the content being blocked due to being regionally unavailable. */
|
||||||
|
public static final int ERROR_CODE_NOT_AVAILABLE_IN_REGION = -106;
|
||||||
|
|
||||||
|
/** Caused by the skip limit that is exhausted. */
|
||||||
|
public static final int ERROR_CODE_SKIP_LIMIT_REACHED = -107;
|
||||||
|
|
||||||
|
/** Caused by playback that needs manual user intervention. */
|
||||||
|
public static final int ERROR_CODE_SETUP_REQUIRED = -108;
|
||||||
|
|
||||||
|
/** Caused by navigation that failed because the playlist was exhausted. */
|
||||||
|
public static final int ERROR_CODE_END_OF_PLAYLIST = -109;
|
||||||
|
|
||||||
|
/** Caused by a request for content that was already playing. */
|
||||||
|
public static final int ERROR_CODE_CONTENT_ALREADY_PLAYING = -110;
|
||||||
|
|
||||||
// Miscellaneous errors (1xxx).
|
// Miscellaneous errors (1xxx).
|
||||||
|
|
||||||
/** Caused by an error whose cause could not be identified. */
|
/** Caused by an error whose cause could not be identified. */
|
||||||
@ -286,6 +344,34 @@ public class PlaybackException extends Exception {
|
|||||||
/** Returns the name of a given {@code errorCode}. */
|
/** Returns the name of a given {@code errorCode}. */
|
||||||
public static String getErrorCodeName(@ErrorCode int errorCode) {
|
public static String getErrorCodeName(@ErrorCode int errorCode) {
|
||||||
switch (errorCode) {
|
switch (errorCode) {
|
||||||
|
case ERROR_CODE_INVALID_STATE:
|
||||||
|
return "ERROR_CODE_INVALID_STATE";
|
||||||
|
case ERROR_CODE_BAD_VALUE:
|
||||||
|
return "ERROR_CODE_BAD_VALUE";
|
||||||
|
case ERROR_CODE_PERMISSION_DENIED:
|
||||||
|
return "ERROR_CODE_PERMISSION_DENIED";
|
||||||
|
case ERROR_CODE_NOT_SUPPORTED:
|
||||||
|
return "ERROR_CODE_NOT_SUPPORTED";
|
||||||
|
case ERROR_CODE_DISCONNECTED:
|
||||||
|
return "ERROR_CODE_DISCONNECTED";
|
||||||
|
case ERROR_CODE_AUTHENTICATION_EXPIRED:
|
||||||
|
return "ERROR_CODE_AUTHENTICATION_EXPIRED";
|
||||||
|
case ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED:
|
||||||
|
return "ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED";
|
||||||
|
case ERROR_CODE_CONCURRENT_STREAM_LIMIT:
|
||||||
|
return "ERROR_CODE_CONCURRENT_STREAM_LIMIT";
|
||||||
|
case ERROR_CODE_PARENTAL_CONTROL_RESTRICTED:
|
||||||
|
return "ERROR_CODE_PARENTAL_CONTROL_RESTRICTED";
|
||||||
|
case ERROR_CODE_NOT_AVAILABLE_IN_REGION:
|
||||||
|
return "ERROR_CODE_NOT_AVAILABLE_IN_REGION";
|
||||||
|
case ERROR_CODE_SKIP_LIMIT_REACHED:
|
||||||
|
return "ERROR_CODE_SKIP_LIMIT_REACHED";
|
||||||
|
case ERROR_CODE_SETUP_REQUIRED:
|
||||||
|
return "ERROR_CODE_SETUP_REQUIRED";
|
||||||
|
case ERROR_CODE_END_OF_PLAYLIST:
|
||||||
|
return "ERROR_CODE_END_OF_PLAYLIST";
|
||||||
|
case ERROR_CODE_CONTENT_ALREADY_PLAYING:
|
||||||
|
return "ERROR_CODE_CONTENT_ALREADY_PLAYING";
|
||||||
case ERROR_CODE_UNSPECIFIED:
|
case ERROR_CODE_UNSPECIFIED:
|
||||||
return "ERROR_CODE_UNSPECIFIED";
|
return "ERROR_CODE_UNSPECIFIED";
|
||||||
case ERROR_CODE_REMOTE_ERROR:
|
case ERROR_CODE_REMOTE_ERROR:
|
||||||
@ -387,6 +473,9 @@ public class PlaybackException extends Exception {
|
|||||||
/** The value of {@link SystemClock#elapsedRealtime()} when this exception was created. */
|
/** The value of {@link SystemClock#elapsedRealtime()} when this exception was created. */
|
||||||
public final long timestampMs;
|
public final long timestampMs;
|
||||||
|
|
||||||
|
/** An extras {@link Bundle}. */
|
||||||
|
@UnstableApi public final Bundle extras;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance.
|
* Creates an instance.
|
||||||
*
|
*
|
||||||
@ -398,7 +487,25 @@ public class PlaybackException extends Exception {
|
|||||||
@UnstableApi
|
@UnstableApi
|
||||||
public PlaybackException(
|
public PlaybackException(
|
||||||
@Nullable String message, @Nullable Throwable cause, @ErrorCode int errorCode) {
|
@Nullable String message, @Nullable Throwable cause, @ErrorCode int errorCode) {
|
||||||
this(message, cause, errorCode, Clock.DEFAULT.elapsedRealtime());
|
this(message, cause, errorCode, Bundle.EMPTY, Clock.DEFAULT.elapsedRealtime());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance.
|
||||||
|
*
|
||||||
|
* @param errorCode A number which identifies the cause of the error. May be one of the {@link
|
||||||
|
* ErrorCode ErrorCodes}.
|
||||||
|
* @param cause See {@link #getCause()}.
|
||||||
|
* @param message See {@link #getMessage()}.
|
||||||
|
* @param extras An optional {@link Bundle}.
|
||||||
|
*/
|
||||||
|
@UnstableApi
|
||||||
|
public PlaybackException(
|
||||||
|
@Nullable String message,
|
||||||
|
@Nullable Throwable cause,
|
||||||
|
@ErrorCode int errorCode,
|
||||||
|
Bundle extras) {
|
||||||
|
this(message, cause, errorCode, extras, Clock.DEFAULT.elapsedRealtime());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a new instance using the fields obtained from the given {@link Bundle}. */
|
/** Creates a new instance using the fields obtained from the given {@link Bundle}. */
|
||||||
@ -409,6 +516,7 @@ public class PlaybackException extends Exception {
|
|||||||
/* cause= */ getCauseFromBundle(bundle),
|
/* cause= */ getCauseFromBundle(bundle),
|
||||||
/* errorCode= */ bundle.getInt(
|
/* errorCode= */ bundle.getInt(
|
||||||
FIELD_INT_ERROR_CODE, /* defaultValue= */ ERROR_CODE_UNSPECIFIED),
|
FIELD_INT_ERROR_CODE, /* defaultValue= */ ERROR_CODE_UNSPECIFIED),
|
||||||
|
/* extras= */ getExtrasFromBundle(bundle),
|
||||||
/* timestampMs= */ bundle.getLong(
|
/* timestampMs= */ bundle.getLong(
|
||||||
FIELD_LONG_TIMESTAMP_MS, /* defaultValue= */ SystemClock.elapsedRealtime()));
|
FIELD_LONG_TIMESTAMP_MS, /* defaultValue= */ SystemClock.elapsedRealtime()));
|
||||||
}
|
}
|
||||||
@ -419,9 +527,11 @@ public class PlaybackException extends Exception {
|
|||||||
@Nullable String message,
|
@Nullable String message,
|
||||||
@Nullable Throwable cause,
|
@Nullable Throwable cause,
|
||||||
@ErrorCode int errorCode,
|
@ErrorCode int errorCode,
|
||||||
|
Bundle extras,
|
||||||
long timestampMs) {
|
long timestampMs) {
|
||||||
super(message, cause);
|
super(message, cause);
|
||||||
this.errorCode = errorCode;
|
this.errorCode = errorCode;
|
||||||
|
this.extras = extras;
|
||||||
this.timestampMs = timestampMs;
|
this.timestampMs = timestampMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -462,6 +572,7 @@ public class PlaybackException extends Exception {
|
|||||||
private static final String FIELD_STRING_MESSAGE = Util.intToStringMaxRadix(2);
|
private static final String FIELD_STRING_MESSAGE = Util.intToStringMaxRadix(2);
|
||||||
private static final String FIELD_STRING_CAUSE_CLASS_NAME = Util.intToStringMaxRadix(3);
|
private static final String FIELD_STRING_CAUSE_CLASS_NAME = Util.intToStringMaxRadix(3);
|
||||||
private static final String FIELD_STRING_CAUSE_MESSAGE = Util.intToStringMaxRadix(4);
|
private static final String FIELD_STRING_CAUSE_MESSAGE = Util.intToStringMaxRadix(4);
|
||||||
|
private static final String FIELD_BUNDLE_EXTRAS = Util.intToStringMaxRadix(5);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines a minimum field ID value for subclasses to use when implementing {@link #toBundle()}
|
* Defines a minimum field ID value for subclasses to use when implementing {@link #toBundle()}
|
||||||
@ -486,6 +597,7 @@ public class PlaybackException extends Exception {
|
|||||||
bundle.putInt(FIELD_INT_ERROR_CODE, errorCode);
|
bundle.putInt(FIELD_INT_ERROR_CODE, errorCode);
|
||||||
bundle.putLong(FIELD_LONG_TIMESTAMP_MS, timestampMs);
|
bundle.putLong(FIELD_LONG_TIMESTAMP_MS, timestampMs);
|
||||||
bundle.putString(FIELD_STRING_MESSAGE, getMessage());
|
bundle.putString(FIELD_STRING_MESSAGE, getMessage());
|
||||||
|
bundle.putBundle(FIELD_BUNDLE_EXTRAS, extras);
|
||||||
@Nullable Throwable cause = getCause();
|
@Nullable Throwable cause = getCause();
|
||||||
if (cause != null) {
|
if (cause != null) {
|
||||||
bundle.putString(FIELD_STRING_CAUSE_CLASS_NAME, cause.getClass().getName());
|
bundle.putString(FIELD_STRING_CAUSE_CLASS_NAME, cause.getClass().getName());
|
||||||
@ -507,6 +619,11 @@ public class PlaybackException extends Exception {
|
|||||||
return new RemoteException(message);
|
return new RemoteException(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Bundle getExtrasFromBundle(Bundle bundle) {
|
||||||
|
Bundle extras = bundle.getBundle(FIELD_BUNDLE_EXTRAS);
|
||||||
|
return extras != null ? extras : Bundle.EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private static Throwable getCauseFromBundle(Bundle bundle) {
|
private static Throwable getCauseFromBundle(Bundle bundle) {
|
||||||
@Nullable String causeClassName = bundle.getString(FIELD_STRING_CAUSE_CLASS_NAME);
|
@Nullable String causeClassName = bundle.getString(FIELD_STRING_CAUSE_CLASS_NAME);
|
||||||
|
@ -30,13 +30,18 @@ public class PlaybackExceptionTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void roundTripViaBundle_yieldsEqualInstance() {
|
public void roundTripViaBundle_yieldsEqualInstance() {
|
||||||
|
Bundle extras = new Bundle();
|
||||||
|
extras.putInt("intKey", 123);
|
||||||
PlaybackException before =
|
PlaybackException before =
|
||||||
new PlaybackException(
|
new PlaybackException(
|
||||||
/* message= */ "test",
|
/* message= */ "test",
|
||||||
/* cause= */ new IOException(/* message= */ "io"),
|
/* cause= */ new IOException(/* message= */ "io"),
|
||||||
PlaybackException.ERROR_CODE_IO_FILE_NOT_FOUND);
|
PlaybackException.ERROR_CODE_IO_FILE_NOT_FOUND,
|
||||||
|
extras);
|
||||||
PlaybackException after = PlaybackException.fromBundle(before.toBundle());
|
PlaybackException after = PlaybackException.fromBundle(before.toBundle());
|
||||||
assertPlaybackExceptionsAreEquivalent(before, after);
|
assertPlaybackExceptionsAreEquivalent(before, after);
|
||||||
|
assertThat(after.extras.size()).isEqualTo(1);
|
||||||
|
assertThat(after.extras.getInt("intKey")).isEqualTo(123);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Backward compatibility tests.
|
// Backward compatibility tests.
|
||||||
@ -51,6 +56,7 @@ public class PlaybackExceptionTest {
|
|||||||
"message",
|
"message",
|
||||||
expectedCause,
|
expectedCause,
|
||||||
PlaybackException.ERROR_CODE_AUDIO_TRACK_INIT_FAILED,
|
PlaybackException.ERROR_CODE_AUDIO_TRACK_INIT_FAILED,
|
||||||
|
Bundle.EMPTY,
|
||||||
/* timestampMs= */ 1000);
|
/* timestampMs= */ 1000);
|
||||||
|
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
@ -71,6 +77,7 @@ public class PlaybackExceptionTest {
|
|||||||
"message",
|
"message",
|
||||||
cause,
|
cause,
|
||||||
PlaybackException.ERROR_CODE_DECODING_FAILED,
|
PlaybackException.ERROR_CODE_DECODING_FAILED,
|
||||||
|
Bundle.EMPTY,
|
||||||
/* timestampMs= */ 2000);
|
/* timestampMs= */ 2000);
|
||||||
|
|
||||||
Bundle bundle = exception.toBundle();
|
Bundle bundle = exception.toBundle();
|
||||||
@ -89,6 +96,7 @@ public class PlaybackExceptionTest {
|
|||||||
"message",
|
"message",
|
||||||
expectedCause,
|
expectedCause,
|
||||||
PlaybackException.ERROR_CODE_AUDIO_TRACK_INIT_FAILED,
|
PlaybackException.ERROR_CODE_AUDIO_TRACK_INIT_FAILED,
|
||||||
|
Bundle.EMPTY,
|
||||||
/* timestampMs= */ 1000);
|
/* timestampMs= */ 1000);
|
||||||
|
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
|
@ -279,7 +279,7 @@ public final class ExoPlaybackException extends PlaybackException {
|
|||||||
@Nullable MediaPeriodId mediaPeriodId,
|
@Nullable MediaPeriodId mediaPeriodId,
|
||||||
long timestampMs,
|
long timestampMs,
|
||||||
boolean isRecoverable) {
|
boolean isRecoverable) {
|
||||||
super(message, cause, errorCode, timestampMs);
|
super(message, cause, errorCode, Bundle.EMPTY, timestampMs);
|
||||||
Assertions.checkArgument(!isRecoverable || type == TYPE_RENDERER);
|
Assertions.checkArgument(!isRecoverable || type == TYPE_RENDERER);
|
||||||
Assertions.checkArgument(cause != null || type == TYPE_REMOTE);
|
Assertions.checkArgument(cause != null || type == TYPE_REMOTE);
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
@ -48,7 +48,7 @@ oneway interface IMediaController {
|
|||||||
void onRenderedFirstFrame(int seq) = 3010;
|
void onRenderedFirstFrame(int seq) = 3010;
|
||||||
void onExtrasChanged(int seq, in Bundle extras) = 3011;
|
void onExtrasChanged(int seq, in Bundle extras) = 3011;
|
||||||
void onSessionActivityChanged(int seq, in PendingIntent pendingIntent) = 3013;
|
void onSessionActivityChanged(int seq, in PendingIntent pendingIntent) = 3013;
|
||||||
void onError(int seq, int errorCode, String errorMessage, in Bundle errorExtras) = 3014;
|
void onError(int seq, in Bundle sessionError) = 3014;
|
||||||
// Next Id for MediaController: 3015
|
// Next Id for MediaController: 3015
|
||||||
|
|
||||||
void onChildrenChanged(
|
void onChildrenChanged(
|
||||||
|
@ -76,7 +76,6 @@ import androidx.media3.common.Timeline.Period;
|
|||||||
import androidx.media3.common.Timeline.Window;
|
import androidx.media3.common.Timeline.Window;
|
||||||
import androidx.media3.common.util.Log;
|
import androidx.media3.common.util.Log;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import androidx.media3.session.MediaControllerImplLegacy.NonFatalErrorInfo;
|
|
||||||
import androidx.media3.session.MediaLibraryService.LibraryParams;
|
import androidx.media3.session.MediaLibraryService.LibraryParams;
|
||||||
import androidx.media3.session.legacy.AudioAttributesCompat;
|
import androidx.media3.session.legacy.AudioAttributesCompat;
|
||||||
import androidx.media3.session.legacy.MediaBrowserCompat;
|
import androidx.media3.session.legacy.MediaBrowserCompat;
|
||||||
@ -154,53 +153,122 @@ import java.util.concurrent.TimeoutException;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final ImmutableSet<Integer> FATAL_LEGACY_ERROR_CODES =
|
|
||||||
ImmutableSet.of(
|
|
||||||
PlaybackStateCompat.ERROR_CODE_UNKNOWN_ERROR,
|
|
||||||
PlaybackStateCompat.ERROR_CODE_AUTHENTICATION_EXPIRED,
|
|
||||||
PlaybackStateCompat.ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED,
|
|
||||||
PlaybackStateCompat.ERROR_CODE_CONCURRENT_STREAM_LIMIT,
|
|
||||||
PlaybackStateCompat.ERROR_CODE_PARENTAL_CONTROL_RESTRICTED,
|
|
||||||
PlaybackStateCompat.ERROR_CODE_NOT_AVAILABLE_IN_REGION,
|
|
||||||
PlaybackStateCompat.ERROR_CODE_SKIP_LIMIT_REACHED);
|
|
||||||
|
|
||||||
/** Converts {@link PlaybackStateCompat} to {@link PlaybackException}. */
|
/** Converts {@link PlaybackStateCompat} to {@link PlaybackException}. */
|
||||||
@Nullable
|
@Nullable
|
||||||
public static PlaybackException convertToPlaybackException(
|
public static PlaybackException convertToPlaybackException(
|
||||||
@Nullable PlaybackStateCompat playbackStateCompat) {
|
@Nullable PlaybackStateCompat playbackStateCompat) {
|
||||||
if (playbackStateCompat == null
|
if (playbackStateCompat == null
|
||||||
|| playbackStateCompat.getState() != PlaybackStateCompat.STATE_ERROR
|
|| playbackStateCompat.getState() != PlaybackStateCompat.STATE_ERROR) {
|
||||||
|| !FATAL_LEGACY_ERROR_CODES.contains(playbackStateCompat.getErrorCode())) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
StringBuilder stringBuilder = new StringBuilder();
|
@Nullable CharSequence errorMessage = playbackStateCompat.getErrorMessage();
|
||||||
if (!TextUtils.isEmpty(playbackStateCompat.getErrorMessage())) {
|
@Nullable Bundle playbackStateCompatExtras = playbackStateCompat.getExtras();
|
||||||
stringBuilder.append(playbackStateCompat.getErrorMessage().toString()).append(", ");
|
|
||||||
}
|
|
||||||
stringBuilder.append("code=").append(playbackStateCompat.getErrorCode());
|
|
||||||
String errorMessage = stringBuilder.toString();
|
|
||||||
return new PlaybackException(
|
return new PlaybackException(
|
||||||
errorMessage, /* cause= */ null, PlaybackException.ERROR_CODE_REMOTE_ERROR);
|
errorMessage != null ? errorMessage.toString() : null,
|
||||||
|
/* cause= */ null,
|
||||||
|
convertToPlaybackExceptionErrorCode(playbackStateCompat.getErrorCode()),
|
||||||
|
playbackStateCompatExtras != null ? playbackStateCompatExtras : Bundle.EMPTY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Converts {@link PlaybackStateCompat} to {@link NonFatalErrorInfo}. */
|
/** Converts {@link PlaybackStateCompat} to {@link SessionError}. */
|
||||||
@Nullable
|
@Nullable
|
||||||
public static NonFatalErrorInfo convertToNonFatalErrorInfo(
|
public static SessionError convertToSessionError(
|
||||||
@Nullable PlaybackStateCompat playbackStateCompat, String errorMessageFallback) {
|
@Nullable PlaybackStateCompat playbackStateCompat) {
|
||||||
if (playbackStateCompat == null
|
if (playbackStateCompat == null
|
||||||
|| playbackStateCompat.getState() != PlaybackStateCompat.STATE_ERROR
|
|| playbackStateCompat.getState() == PlaybackStateCompat.STATE_ERROR
|
||||||
|| FATAL_LEGACY_ERROR_CODES.contains(playbackStateCompat.getErrorCode())) {
|
|| playbackStateCompat.getErrorCode() == PlaybackStateCompat.ERROR_CODE_UNKNOWN_ERROR
|
||||||
|
|| playbackStateCompat.getErrorMessage() == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@Nullable Bundle playbackStateCompatExtras = playbackStateCompat.getExtras();
|
@Nullable Bundle playbackStateCompatExtras = playbackStateCompat.getExtras();
|
||||||
return new NonFatalErrorInfo(
|
return new SessionError(
|
||||||
playbackStateCompat.getErrorCode(),
|
convertToSessionErrorCode(playbackStateCompat.getErrorCode()),
|
||||||
!TextUtils.isEmpty(playbackStateCompat.getErrorMessage())
|
checkNotNull(playbackStateCompat.getErrorMessage()).toString(),
|
||||||
? playbackStateCompat.getErrorMessage().toString()
|
|
||||||
: errorMessageFallback,
|
|
||||||
playbackStateCompatExtras != null ? playbackStateCompatExtras : Bundle.EMPTY);
|
playbackStateCompatExtras != null ? playbackStateCompatExtras : Bundle.EMPTY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static @SessionError.Code int convertToSessionErrorCode(
|
||||||
|
@PlaybackStateCompat.ErrorCode int errorCode) {
|
||||||
|
switch (errorCode) {
|
||||||
|
case PlaybackStateCompat.ERROR_CODE_ACTION_ABORTED:
|
||||||
|
return SessionError.INFO_CANCELLED;
|
||||||
|
case PlaybackStateCompat.ERROR_CODE_APP_ERROR:
|
||||||
|
return SessionError.ERROR_INVALID_STATE;
|
||||||
|
case PlaybackStateCompat.ERROR_CODE_AUTHENTICATION_EXPIRED:
|
||||||
|
return SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED;
|
||||||
|
case PlaybackStateCompat.ERROR_CODE_CONTENT_ALREADY_PLAYING:
|
||||||
|
return SessionError.ERROR_SESSION_CONTENT_ALREADY_PLAYING;
|
||||||
|
case PlaybackStateCompat.ERROR_CODE_CONCURRENT_STREAM_LIMIT:
|
||||||
|
return SessionError.ERROR_SESSION_CONCURRENT_STREAM_LIMIT;
|
||||||
|
case PlaybackStateCompat.ERROR_CODE_END_OF_QUEUE:
|
||||||
|
return SessionError.ERROR_SESSION_END_OF_PLAYLIST;
|
||||||
|
case PlaybackStateCompat.ERROR_CODE_NOT_AVAILABLE_IN_REGION:
|
||||||
|
return SessionError.ERROR_SESSION_NOT_AVAILABLE_IN_REGION;
|
||||||
|
case PlaybackStateCompat.ERROR_CODE_NOT_SUPPORTED:
|
||||||
|
return SessionError.ERROR_NOT_SUPPORTED;
|
||||||
|
case PlaybackStateCompat.ERROR_CODE_PARENTAL_CONTROL_RESTRICTED:
|
||||||
|
return SessionError.ERROR_SESSION_PARENTAL_CONTROL_RESTRICTED;
|
||||||
|
case PlaybackStateCompat.ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED:
|
||||||
|
return SessionError.ERROR_SESSION_PREMIUM_ACCOUNT_REQUIRED;
|
||||||
|
case PlaybackStateCompat.ERROR_CODE_SKIP_LIMIT_REACHED:
|
||||||
|
return SessionError.ERROR_SESSION_SKIP_LIMIT_REACHED;
|
||||||
|
default:
|
||||||
|
return SessionError.ERROR_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @PlaybackException.ErrorCode int convertToPlaybackExceptionErrorCode(
|
||||||
|
@PlaybackStateCompat.ErrorCode int errorCode) {
|
||||||
|
@PlaybackException.ErrorCode
|
||||||
|
int playbackExceptionErrorCode = convertToSessionErrorCode(errorCode);
|
||||||
|
switch (playbackExceptionErrorCode) {
|
||||||
|
case SessionError.ERROR_UNKNOWN:
|
||||||
|
return PlaybackException.ERROR_CODE_UNSPECIFIED;
|
||||||
|
case SessionError.ERROR_IO:
|
||||||
|
return PlaybackException.ERROR_CODE_IO_UNSPECIFIED;
|
||||||
|
default:
|
||||||
|
return playbackExceptionErrorCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Converts {@link SessionError.Code} to {@link PlaybackStateCompat.ErrorCode}. */
|
||||||
|
@PlaybackStateCompat.ErrorCode
|
||||||
|
public static int convertToLegacyErrorCode(@SessionError.Code int errorCode) {
|
||||||
|
switch (errorCode) {
|
||||||
|
case SessionError.INFO_CANCELLED:
|
||||||
|
return PlaybackStateCompat.ERROR_CODE_ACTION_ABORTED;
|
||||||
|
case SessionError.ERROR_INVALID_STATE:
|
||||||
|
return PlaybackStateCompat.ERROR_CODE_APP_ERROR;
|
||||||
|
case SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED:
|
||||||
|
return PlaybackStateCompat.ERROR_CODE_AUTHENTICATION_EXPIRED;
|
||||||
|
case SessionError.ERROR_SESSION_CONTENT_ALREADY_PLAYING:
|
||||||
|
return PlaybackStateCompat.ERROR_CODE_CONTENT_ALREADY_PLAYING;
|
||||||
|
case SessionError.ERROR_SESSION_CONCURRENT_STREAM_LIMIT:
|
||||||
|
return PlaybackStateCompat.ERROR_CODE_CONCURRENT_STREAM_LIMIT;
|
||||||
|
case SessionError.ERROR_SESSION_END_OF_PLAYLIST:
|
||||||
|
return PlaybackStateCompat.ERROR_CODE_END_OF_QUEUE;
|
||||||
|
case SessionError.ERROR_SESSION_NOT_AVAILABLE_IN_REGION:
|
||||||
|
return PlaybackStateCompat.ERROR_CODE_NOT_AVAILABLE_IN_REGION;
|
||||||
|
case SessionError.ERROR_NOT_SUPPORTED:
|
||||||
|
return PlaybackStateCompat.ERROR_CODE_NOT_SUPPORTED;
|
||||||
|
case SessionError.ERROR_SESSION_PARENTAL_CONTROL_RESTRICTED:
|
||||||
|
return PlaybackStateCompat.ERROR_CODE_PARENTAL_CONTROL_RESTRICTED;
|
||||||
|
case SessionError.ERROR_SESSION_PREMIUM_ACCOUNT_REQUIRED:
|
||||||
|
return PlaybackStateCompat.ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED;
|
||||||
|
case SessionError.ERROR_SESSION_SKIP_LIMIT_REACHED:
|
||||||
|
return PlaybackStateCompat.ERROR_CODE_SKIP_LIMIT_REACHED;
|
||||||
|
case SessionError.ERROR_UNKNOWN: // fall through
|
||||||
|
default:
|
||||||
|
return PlaybackStateCompat.ERROR_CODE_UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Converts {@link PlaybackException} to {@link PlaybackStateCompat.ErrorCode}. */
|
||||||
|
@PlaybackStateCompat.ErrorCode
|
||||||
|
public static int convertToLegacyErrorCode(PlaybackException playbackException) {
|
||||||
|
return convertToLegacyErrorCode(playbackException.errorCode);
|
||||||
|
}
|
||||||
|
|
||||||
public static MediaBrowserCompat.MediaItem convertToBrowserItem(
|
public static MediaBrowserCompat.MediaItem convertToBrowserItem(
|
||||||
MediaItem item, @Nullable Bitmap artworkBitmap) {
|
MediaItem item, @Nullable Bitmap artworkBitmap) {
|
||||||
MediaDescriptionCompat description = convertToMediaDescriptionCompat(item, artworkBitmap);
|
MediaDescriptionCompat description = convertToMediaDescriptionCompat(item, artworkBitmap);
|
||||||
|
@ -53,7 +53,7 @@ public final class LibraryResult<V> {
|
|||||||
@Target(TYPE_USE)
|
@Target(TYPE_USE)
|
||||||
@IntDef({
|
@IntDef({
|
||||||
RESULT_SUCCESS,
|
RESULT_SUCCESS,
|
||||||
SessionError.INFO_SKIPPED,
|
SessionError.INFO_CANCELLED,
|
||||||
SessionError.ERROR_UNKNOWN,
|
SessionError.ERROR_UNKNOWN,
|
||||||
SessionError.ERROR_INVALID_STATE,
|
SessionError.ERROR_INVALID_STATE,
|
||||||
SessionError.ERROR_BAD_VALUE,
|
SessionError.ERROR_BAD_VALUE,
|
||||||
@ -82,7 +82,7 @@ public final class LibraryResult<V> {
|
|||||||
public static final int RESULT_SUCCESS = 0;
|
public static final int RESULT_SUCCESS = 0;
|
||||||
|
|
||||||
/** Result code representing that the command is skipped. */
|
/** Result code representing that the command is skipped. */
|
||||||
public static final int RESULT_INFO_SKIPPED = SessionError.INFO_SKIPPED;
|
public static final int RESULT_INFO_SKIPPED = SessionError.INFO_CANCELLED;
|
||||||
|
|
||||||
/** Result code representing that the command is ended with an unknown error. */
|
/** Result code representing that the command is ended with an unknown error. */
|
||||||
public static final int RESULT_ERROR_UNKNOWN = SessionError.ERROR_UNKNOWN;
|
public static final int RESULT_ERROR_UNKNOWN = SessionError.ERROR_UNKNOWN;
|
||||||
|
@ -24,7 +24,7 @@ import static androidx.media3.session.SessionCommand.COMMAND_CODE_LIBRARY_SUBSCR
|
|||||||
import static androidx.media3.session.SessionCommand.COMMAND_CODE_LIBRARY_UNSUBSCRIBE;
|
import static androidx.media3.session.SessionCommand.COMMAND_CODE_LIBRARY_UNSUBSCRIBE;
|
||||||
import static androidx.media3.session.SessionError.ERROR_PERMISSION_DENIED;
|
import static androidx.media3.session.SessionError.ERROR_PERMISSION_DENIED;
|
||||||
import static androidx.media3.session.SessionError.ERROR_SESSION_DISCONNECTED;
|
import static androidx.media3.session.SessionError.ERROR_SESSION_DISCONNECTED;
|
||||||
import static androidx.media3.session.SessionError.INFO_SKIPPED;
|
import static androidx.media3.session.SessionError.INFO_CANCELLED;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@ -189,7 +189,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
IMediaSession iSession = getSessionInterfaceWithSessionCommandIfAble(commandCode);
|
IMediaSession iSession = getSessionInterfaceWithSessionCommandIfAble(commandCode);
|
||||||
if (iSession != null) {
|
if (iSession != null) {
|
||||||
SequencedFuture<LibraryResult<V>> result =
|
SequencedFuture<LibraryResult<V>> result =
|
||||||
sequencedFutureManager.createSequencedFuture(LibraryResult.ofError(INFO_SKIPPED));
|
sequencedFutureManager.createSequencedFuture(LibraryResult.ofError(INFO_CANCELLED));
|
||||||
try {
|
try {
|
||||||
task.run(iSession, result.getSequenceNumber());
|
task.run(iSession, result.getSequenceNumber());
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
|
@ -451,51 +451,24 @@ public class MediaController implements Player {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when an non-fatal error {@linkplain
|
* Called when an non-fatal error {@linkplain
|
||||||
* MediaSession#sendError(MediaSession.ControllerInfo, int, int, Bundle) sent by the session} is
|
* MediaSession#sendError(MediaSession.ControllerInfo, SessionError) sent by the session} is
|
||||||
* received.
|
* received.
|
||||||
*
|
*
|
||||||
* <p>When connected to a legacy or platform session, this callback is called each time the
|
* <p>When connected to a legacy or platform session, this callback is called when {@link
|
||||||
* callback {@link
|
|
||||||
* android.media.session.MediaController.Callback#onPlaybackStateChanged(PlaybackState)} is
|
* android.media.session.MediaController.Callback#onPlaybackStateChanged(PlaybackState)} is
|
||||||
* called in {@linkplain PlaybackState#STATE_ERROR state error} with a non-fatal error code.
|
* called with an error code and an error message while the playback state is different to
|
||||||
|
* {@linkplain PlaybackState#STATE_ERROR state error}.
|
||||||
*
|
*
|
||||||
* <p>Non-fatal legacy error codes:
|
* <p>Fatal playback errors are reported to {@link
|
||||||
*
|
|
||||||
* <ul>
|
|
||||||
* <li>{@code PlaybackStateCompat.ERROR_CODE_ACTION_ABORTED}
|
|
||||||
* <li>{@code PlaybackStateCompat.ERROR_CODE_APP_ERROR}
|
|
||||||
* <li>{@code PlaybackStateCompat.ERROR_CODE_CONTENT_ALREADY_PLAYING}
|
|
||||||
* <li>{@code PlaybackStateCompat.ERROR_CODE_END_OF_QUEUE}
|
|
||||||
* <li>{@code PlaybackStateCompat.ERROR_CODE_NOT_SUPPORTED}
|
|
||||||
* <li>In addition, all other error code values not defined as fatal errors in {@code
|
|
||||||
* android.support.v4.media.session.PlaybackStateCompat} are handled as non-fatal.
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* <p>Fatal legacy error codes of the {@link PlaybackState} in state {@link
|
|
||||||
* PlaybackState#STATE_ERROR} are converted to a player error. See {@link
|
|
||||||
* Player.Listener#onPlayerError(PlaybackException)} and {@link
|
* Player.Listener#onPlayerError(PlaybackException)} and {@link
|
||||||
* Player.Listener#onPlayerErrorChanged(PlaybackException)}.
|
* Player.Listener#onPlayerErrorChanged(PlaybackException)} of listeners {@linkplain
|
||||||
*
|
* #addListener(Player.Listener) registered on the controller}.
|
||||||
* <p>Fatal legacy error codes:
|
|
||||||
*
|
|
||||||
* <ul>
|
|
||||||
* <li>{@code PlaybackStateCompat.ERROR_CODE_UNKNOWN_ERROR}
|
|
||||||
* <li>{@code PlaybackStateCompat.ERROR_CODE_AUTHENTICATION_EXPIRED}
|
|
||||||
* <li>{@code PlaybackStateCompat.ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED}
|
|
||||||
* <li>{@code PlaybackStateCompat.ERROR_CODE_CONCURRENT_STREAM_LIMIT}
|
|
||||||
* <li>{@code PlaybackStateCompat.ERROR_CODE_PARENTAL_CONTROL_RESTRICTED}
|
|
||||||
* <li>{@code PlaybackStateCompat.ERROR_CODE_NOT_AVAILABLE_IN_REGION}
|
|
||||||
* <li>{@code PlaybackStateCompat.ERROR_CODE_SKIP_LIMIT_REACHED}
|
|
||||||
* </ul>
|
|
||||||
*
|
*
|
||||||
* @param controller The {@link MediaController} that received the error.
|
* @param controller The {@link MediaController} that received the error.
|
||||||
* @param errorCode The error code.
|
* @param sessionError The session error.
|
||||||
* @param errorMessage The localized error message.
|
|
||||||
* @param errorExtras A bundle with additional custom error data.
|
|
||||||
*/
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
default void onError(
|
default void onError(MediaController controller, SessionError sessionError) {}
|
||||||
MediaController controller, int errorCode, String errorMessage, Bundle errorExtras) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* package */ interface ConnectionCallback {
|
/* package */ interface ConnectionCallback {
|
||||||
|
@ -2890,13 +2890,12 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
|||||||
listener -> listener.onSessionActivityChanged(getInstance(), sessionActivity));
|
listener -> listener.onSessionActivityChanged(getInstance(), sessionActivity));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onError(int seq, int errorCode, String errorMessage, Bundle errorExtras) {
|
public void onError(int seq, SessionError sessionError) {
|
||||||
if (!isConnected()) {
|
if (!isConnected()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
getInstance()
|
getInstance()
|
||||||
.notifyControllerListener(
|
.notifyControllerListener(listener -> listener.onError(getInstance(), sessionError));
|
||||||
listener -> listener.onError(getInstance(), errorCode, errorMessage, errorExtras));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onRenderedFirstFrame() {
|
public void onRenderedFirstFrame() {
|
||||||
|
@ -193,7 +193,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
controllerInfo.availablePlayerCommands,
|
controllerInfo.availablePlayerCommands,
|
||||||
controllerInfo.customLayout,
|
controllerInfo.customLayout,
|
||||||
controllerInfo.sessionExtras,
|
controllerInfo.sessionExtras,
|
||||||
/* errorInfo= */ null);
|
/* sessionError= */ null);
|
||||||
updateStateMaskedControllerInfo(
|
updateStateMaskedControllerInfo(
|
||||||
maskedControllerInfo,
|
maskedControllerInfo,
|
||||||
/* discontinuityReason= */ null,
|
/* discontinuityReason= */ null,
|
||||||
@ -259,7 +259,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
controllerInfo.availablePlayerCommands,
|
controllerInfo.availablePlayerCommands,
|
||||||
controllerInfo.customLayout,
|
controllerInfo.customLayout,
|
||||||
controllerInfo.sessionExtras,
|
controllerInfo.sessionExtras,
|
||||||
/* errorInfo= */ null);
|
/* sessionError= */ null);
|
||||||
updateStateMaskedControllerInfo(
|
updateStateMaskedControllerInfo(
|
||||||
maskedControllerInfo,
|
maskedControllerInfo,
|
||||||
/* discontinuityReason= */ null,
|
/* discontinuityReason= */ null,
|
||||||
@ -382,7 +382,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
controllerInfo.availablePlayerCommands,
|
controllerInfo.availablePlayerCommands,
|
||||||
controllerInfo.customLayout,
|
controllerInfo.customLayout,
|
||||||
controllerInfo.sessionExtras,
|
controllerInfo.sessionExtras,
|
||||||
/* errorInfo= */ null);
|
/* sessionError= */ null);
|
||||||
updateStateMaskedControllerInfo(
|
updateStateMaskedControllerInfo(
|
||||||
maskedControllerInfo, discontinuityReason, mediaItemTransitionReason);
|
maskedControllerInfo, discontinuityReason, mediaItemTransitionReason);
|
||||||
}
|
}
|
||||||
@ -542,7 +542,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
controllerInfo.availablePlayerCommands,
|
controllerInfo.availablePlayerCommands,
|
||||||
controllerInfo.customLayout,
|
controllerInfo.customLayout,
|
||||||
controllerInfo.sessionExtras,
|
controllerInfo.sessionExtras,
|
||||||
/* errorInfo= */ null);
|
/* sessionError= */ null);
|
||||||
updateStateMaskedControllerInfo(
|
updateStateMaskedControllerInfo(
|
||||||
maskedControllerInfo,
|
maskedControllerInfo,
|
||||||
/* discontinuityReason= */ null,
|
/* discontinuityReason= */ null,
|
||||||
@ -563,7 +563,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
controllerInfo.availablePlayerCommands,
|
controllerInfo.availablePlayerCommands,
|
||||||
controllerInfo.customLayout,
|
controllerInfo.customLayout,
|
||||||
controllerInfo.sessionExtras,
|
controllerInfo.sessionExtras,
|
||||||
/* errorInfo= */ null);
|
/* sessionError= */ null);
|
||||||
updateStateMaskedControllerInfo(
|
updateStateMaskedControllerInfo(
|
||||||
maskedControllerInfo,
|
maskedControllerInfo,
|
||||||
/* discontinuityReason= */ null,
|
/* discontinuityReason= */ null,
|
||||||
@ -657,7 +657,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
controllerInfo.availablePlayerCommands,
|
controllerInfo.availablePlayerCommands,
|
||||||
controllerInfo.customLayout,
|
controllerInfo.customLayout,
|
||||||
controllerInfo.sessionExtras,
|
controllerInfo.sessionExtras,
|
||||||
/* errorInfo= */ null);
|
/* sessionError= */ null);
|
||||||
updateStateMaskedControllerInfo(
|
updateStateMaskedControllerInfo(
|
||||||
maskedControllerInfo,
|
maskedControllerInfo,
|
||||||
/* discontinuityReason= */ null,
|
/* discontinuityReason= */ null,
|
||||||
@ -722,7 +722,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
controllerInfo.availablePlayerCommands,
|
controllerInfo.availablePlayerCommands,
|
||||||
controllerInfo.customLayout,
|
controllerInfo.customLayout,
|
||||||
controllerInfo.sessionExtras,
|
controllerInfo.sessionExtras,
|
||||||
/* errorInfo= */ null);
|
/* sessionError= */ null);
|
||||||
updateStateMaskedControllerInfo(
|
updateStateMaskedControllerInfo(
|
||||||
maskedControllerInfo,
|
maskedControllerInfo,
|
||||||
/* discontinuityReason= */ null,
|
/* discontinuityReason= */ null,
|
||||||
@ -776,7 +776,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
controllerInfo.availablePlayerCommands,
|
controllerInfo.availablePlayerCommands,
|
||||||
controllerInfo.customLayout,
|
controllerInfo.customLayout,
|
||||||
controllerInfo.sessionExtras,
|
controllerInfo.sessionExtras,
|
||||||
/* errorInfo= */ null);
|
/* sessionError= */ null);
|
||||||
updateStateMaskedControllerInfo(
|
updateStateMaskedControllerInfo(
|
||||||
maskedControllerInfo,
|
maskedControllerInfo,
|
||||||
/* discontinuityReason= */ null,
|
/* discontinuityReason= */ null,
|
||||||
@ -844,7 +844,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
controllerInfo.availablePlayerCommands,
|
controllerInfo.availablePlayerCommands,
|
||||||
controllerInfo.customLayout,
|
controllerInfo.customLayout,
|
||||||
controllerInfo.sessionExtras,
|
controllerInfo.sessionExtras,
|
||||||
/* errorInfo= */ null);
|
/* sessionError= */ null);
|
||||||
updateStateMaskedControllerInfo(
|
updateStateMaskedControllerInfo(
|
||||||
maskedControllerInfo,
|
maskedControllerInfo,
|
||||||
/* discontinuityReason= */ null,
|
/* discontinuityReason= */ null,
|
||||||
@ -958,7 +958,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
controllerInfo.availablePlayerCommands,
|
controllerInfo.availablePlayerCommands,
|
||||||
controllerInfo.customLayout,
|
controllerInfo.customLayout,
|
||||||
controllerInfo.sessionExtras,
|
controllerInfo.sessionExtras,
|
||||||
/* errorInfo= */ null);
|
/* sessionError= */ null);
|
||||||
updateStateMaskedControllerInfo(
|
updateStateMaskedControllerInfo(
|
||||||
maskedControllerInfo,
|
maskedControllerInfo,
|
||||||
/* discontinuityReason= */ null,
|
/* discontinuityReason= */ null,
|
||||||
@ -986,7 +986,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
controllerInfo.availablePlayerCommands,
|
controllerInfo.availablePlayerCommands,
|
||||||
controllerInfo.customLayout,
|
controllerInfo.customLayout,
|
||||||
controllerInfo.sessionExtras,
|
controllerInfo.sessionExtras,
|
||||||
/* errorInfo= */ null);
|
/* sessionError= */ null);
|
||||||
updateStateMaskedControllerInfo(
|
updateStateMaskedControllerInfo(
|
||||||
maskedControllerInfo,
|
maskedControllerInfo,
|
||||||
/* discontinuityReason= */ null,
|
/* discontinuityReason= */ null,
|
||||||
@ -1123,7 +1123,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
controllerInfo.availablePlayerCommands,
|
controllerInfo.availablePlayerCommands,
|
||||||
controllerInfo.customLayout,
|
controllerInfo.customLayout,
|
||||||
controllerInfo.sessionExtras,
|
controllerInfo.sessionExtras,
|
||||||
/* errorInfo= */ null);
|
/* sessionError= */ null);
|
||||||
updateStateMaskedControllerInfo(
|
updateStateMaskedControllerInfo(
|
||||||
maskedControllerInfo,
|
maskedControllerInfo,
|
||||||
/* discontinuityReason= */ null,
|
/* discontinuityReason= */ null,
|
||||||
@ -1156,7 +1156,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
controllerInfo.availablePlayerCommands,
|
controllerInfo.availablePlayerCommands,
|
||||||
controllerInfo.customLayout,
|
controllerInfo.customLayout,
|
||||||
controllerInfo.sessionExtras,
|
controllerInfo.sessionExtras,
|
||||||
/* errorInfo= */ null);
|
/* sessionError= */ null);
|
||||||
updateStateMaskedControllerInfo(
|
updateStateMaskedControllerInfo(
|
||||||
maskedControllerInfo,
|
maskedControllerInfo,
|
||||||
/* discontinuityReason= */ null,
|
/* discontinuityReason= */ null,
|
||||||
@ -1188,7 +1188,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
controllerInfo.availablePlayerCommands,
|
controllerInfo.availablePlayerCommands,
|
||||||
controllerInfo.customLayout,
|
controllerInfo.customLayout,
|
||||||
controllerInfo.sessionExtras,
|
controllerInfo.sessionExtras,
|
||||||
/* errorInfo= */ null);
|
/* sessionError= */ null);
|
||||||
updateStateMaskedControllerInfo(
|
updateStateMaskedControllerInfo(
|
||||||
maskedControllerInfo,
|
maskedControllerInfo,
|
||||||
/* discontinuityReason= */ null,
|
/* discontinuityReason= */ null,
|
||||||
@ -1223,7 +1223,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
controllerInfo.availablePlayerCommands,
|
controllerInfo.availablePlayerCommands,
|
||||||
controllerInfo.customLayout,
|
controllerInfo.customLayout,
|
||||||
controllerInfo.sessionExtras,
|
controllerInfo.sessionExtras,
|
||||||
/* errorInfo= */ null);
|
/* sessionError= */ null);
|
||||||
updateStateMaskedControllerInfo(
|
updateStateMaskedControllerInfo(
|
||||||
maskedControllerInfo,
|
maskedControllerInfo,
|
||||||
/* discontinuityReason= */ null,
|
/* discontinuityReason= */ null,
|
||||||
@ -1262,7 +1262,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
controllerInfo.availablePlayerCommands,
|
controllerInfo.availablePlayerCommands,
|
||||||
controllerInfo.customLayout,
|
controllerInfo.customLayout,
|
||||||
controllerInfo.sessionExtras,
|
controllerInfo.sessionExtras,
|
||||||
/* errorInfo= */ null);
|
/* sessionError= */ null);
|
||||||
updateStateMaskedControllerInfo(
|
updateStateMaskedControllerInfo(
|
||||||
maskedControllerInfo,
|
maskedControllerInfo,
|
||||||
/* discontinuityReason= */ null,
|
/* discontinuityReason= */ null,
|
||||||
@ -1555,8 +1555,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
controllerCompat.isSessionReady(),
|
controllerCompat.isSessionReady(),
|
||||||
controllerCompat.getRatingType(),
|
controllerCompat.getRatingType(),
|
||||||
getInstance().getTimeDiffMs(),
|
getInstance().getTimeDiffMs(),
|
||||||
getRoutingControllerId(controllerCompat),
|
getRoutingControllerId(controllerCompat));
|
||||||
context);
|
|
||||||
Pair<@NullableType Integer, @NullableType Integer> reasons =
|
Pair<@NullableType Integer, @NullableType Integer> reasons =
|
||||||
calculateDiscontinuityAndTransitionReason(
|
calculateDiscontinuityAndTransitionReason(
|
||||||
legacyPlayerInfo,
|
legacyPlayerInfo,
|
||||||
@ -1748,15 +1747,10 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
listener.onCustomLayoutChanged(getInstance(), newControllerInfo.customLayout);
|
listener.onCustomLayoutChanged(getInstance(), newControllerInfo.customLayout);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (newControllerInfo.nonFatalErrorInfo != null) {
|
if (newControllerInfo.sessionError != null) {
|
||||||
getInstance()
|
getInstance()
|
||||||
.notifyControllerListener(
|
.notifyControllerListener(
|
||||||
listener ->
|
listener -> listener.onError(getInstance(), newControllerInfo.sessionError));
|
||||||
listener.onError(
|
|
||||||
getInstance(),
|
|
||||||
newControllerInfo.nonFatalErrorInfo.errorCode,
|
|
||||||
newControllerInfo.nonFatalErrorInfo.errorMessage,
|
|
||||||
newControllerInfo.nonFatalErrorInfo.errorExtras));
|
|
||||||
}
|
}
|
||||||
listeners.flushEvents();
|
listeners.flushEvents();
|
||||||
}
|
}
|
||||||
@ -1781,18 +1775,6 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
// Ignore return value of the future because legacy session cannot get result back.
|
// Ignore return value of the future because legacy session cannot get result back.
|
||||||
}
|
}
|
||||||
|
|
||||||
/* package */ static class NonFatalErrorInfo {
|
|
||||||
public final int errorCode;
|
|
||||||
public final String errorMessage;
|
|
||||||
public final Bundle errorExtras;
|
|
||||||
|
|
||||||
public NonFatalErrorInfo(int errorCode, String errorMessage, Bundle errorExtras) {
|
|
||||||
this.errorCode = errorCode;
|
|
||||||
this.errorMessage = errorMessage;
|
|
||||||
this.errorExtras = errorExtras;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ConnectionCallback extends MediaBrowserCompat.ConnectionCallback {
|
private class ConnectionCallback extends MediaBrowserCompat.ConnectionCallback {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -1913,7 +1895,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
controllerInfo.availablePlayerCommands,
|
controllerInfo.availablePlayerCommands,
|
||||||
controllerInfo.customLayout,
|
controllerInfo.customLayout,
|
||||||
extras,
|
extras,
|
||||||
/* errorInfo= */ null);
|
/* sessionError= */ null);
|
||||||
getInstance()
|
getInstance()
|
||||||
.notifyControllerListener(listener -> listener.onExtrasChanged(getInstance(), extras));
|
.notifyControllerListener(listener -> listener.onExtrasChanged(getInstance(), extras));
|
||||||
}
|
}
|
||||||
@ -1976,8 +1958,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
boolean isSessionReady,
|
boolean isSessionReady,
|
||||||
@RatingCompat.Style int ratingType,
|
@RatingCompat.Style int ratingType,
|
||||||
long timeDiffMs,
|
long timeDiffMs,
|
||||||
@Nullable String routingControllerId,
|
@Nullable String routingControllerId) {
|
||||||
Context context) {
|
|
||||||
QueueTimeline currentTimeline;
|
QueueTimeline currentTimeline;
|
||||||
MediaMetadata mediaMetadata;
|
MediaMetadata mediaMetadata;
|
||||||
int currentMediaItemIndex;
|
int currentMediaItemIndex;
|
||||||
@ -2094,10 +2075,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
|
|
||||||
PlaybackException playerError =
|
PlaybackException playerError =
|
||||||
LegacyConversions.convertToPlaybackException(newLegacyPlayerInfo.playbackStateCompat);
|
LegacyConversions.convertToPlaybackException(newLegacyPlayerInfo.playbackStateCompat);
|
||||||
NonFatalErrorInfo nonFatalErrorInfo =
|
SessionError sessionError =
|
||||||
LegacyConversions.convertToNonFatalErrorInfo(
|
LegacyConversions.convertToSessionError(newLegacyPlayerInfo.playbackStateCompat);
|
||||||
newLegacyPlayerInfo.playbackStateCompat,
|
|
||||||
context.getString(R.string.legacy_error_message_fallback));
|
|
||||||
|
|
||||||
long currentPositionMs =
|
long currentPositionMs =
|
||||||
LegacyConversions.convertToCurrentPositionMs(
|
LegacyConversions.convertToCurrentPositionMs(
|
||||||
@ -2166,7 +2145,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
customLayout,
|
customLayout,
|
||||||
newLegacyPlayerInfo.sessionExtras,
|
newLegacyPlayerInfo.sessionExtras,
|
||||||
playerError,
|
playerError,
|
||||||
nonFatalErrorInfo,
|
sessionError,
|
||||||
durationMs,
|
durationMs,
|
||||||
currentPositionMs,
|
currentPositionMs,
|
||||||
bufferedPositionMs,
|
bufferedPositionMs,
|
||||||
@ -2335,7 +2314,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
ImmutableList<CommandButton> customLayout,
|
ImmutableList<CommandButton> customLayout,
|
||||||
Bundle sessionExtras,
|
Bundle sessionExtras,
|
||||||
@Nullable PlaybackException playerError,
|
@Nullable PlaybackException playerError,
|
||||||
@Nullable NonFatalErrorInfo nonFatalErrorInfo,
|
@Nullable SessionError sessionError,
|
||||||
long durationMs,
|
long durationMs,
|
||||||
long currentPositionMs,
|
long currentPositionMs,
|
||||||
long bufferedPositionMs,
|
long bufferedPositionMs,
|
||||||
@ -2410,7 +2389,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
availablePlayerCommands,
|
availablePlayerCommands,
|
||||||
customLayout,
|
customLayout,
|
||||||
sessionExtras,
|
sessionExtras,
|
||||||
/* errorInfo= */ nonFatalErrorInfo);
|
sessionError);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static PositionInfo createPositionInfo(
|
private static PositionInfo createPositionInfo(
|
||||||
@ -2621,7 +2600,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
public final Commands availablePlayerCommands;
|
public final Commands availablePlayerCommands;
|
||||||
public final ImmutableList<CommandButton> customLayout;
|
public final ImmutableList<CommandButton> customLayout;
|
||||||
public final Bundle sessionExtras;
|
public final Bundle sessionExtras;
|
||||||
@Nullable public final NonFatalErrorInfo nonFatalErrorInfo;
|
@Nullable public final SessionError sessionError;
|
||||||
|
|
||||||
public ControllerInfo() {
|
public ControllerInfo() {
|
||||||
playerInfo = PlayerInfo.DEFAULT.copyWithTimeline(QueueTimeline.DEFAULT);
|
playerInfo = PlayerInfo.DEFAULT.copyWithTimeline(QueueTimeline.DEFAULT);
|
||||||
@ -2629,7 +2608,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
availablePlayerCommands = Commands.EMPTY;
|
availablePlayerCommands = Commands.EMPTY;
|
||||||
customLayout = ImmutableList.of();
|
customLayout = ImmutableList.of();
|
||||||
sessionExtras = Bundle.EMPTY;
|
sessionExtras = Bundle.EMPTY;
|
||||||
nonFatalErrorInfo = null;
|
sessionError = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ControllerInfo(
|
public ControllerInfo(
|
||||||
@ -2638,13 +2617,13 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
Commands availablePlayerCommands,
|
Commands availablePlayerCommands,
|
||||||
ImmutableList<CommandButton> customLayout,
|
ImmutableList<CommandButton> customLayout,
|
||||||
@Nullable Bundle sessionExtras,
|
@Nullable Bundle sessionExtras,
|
||||||
@Nullable NonFatalErrorInfo nonFatalErrorInfo) {
|
@Nullable SessionError sessionError) {
|
||||||
this.playerInfo = playerInfo;
|
this.playerInfo = playerInfo;
|
||||||
this.availableSessionCommands = availableSessionCommands;
|
this.availableSessionCommands = availableSessionCommands;
|
||||||
this.availablePlayerCommands = availablePlayerCommands;
|
this.availablePlayerCommands = availablePlayerCommands;
|
||||||
this.customLayout = customLayout;
|
this.customLayout = customLayout;
|
||||||
this.sessionExtras = sessionExtras == null ? Bundle.EMPTY : sessionExtras;
|
this.sessionExtras = sessionExtras == null ? Bundle.EMPTY : sessionExtras;
|
||||||
this.nonFatalErrorInfo = nonFatalErrorInfo;
|
this.sessionError = sessionError;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
|||||||
private static final String TAG = "MediaControllerStub";
|
private static final String TAG = "MediaControllerStub";
|
||||||
|
|
||||||
/** The version of the IMediaController interface. */
|
/** The version of the IMediaController interface. */
|
||||||
public static final int VERSION_INT = 4;
|
public static final int VERSION_INT = 5;
|
||||||
|
|
||||||
private final WeakReference<MediaControllerImplBase> controller;
|
private final WeakReference<MediaControllerImplBase> controller;
|
||||||
|
|
||||||
@ -265,10 +265,15 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(int seq, int errorCode, String errorMessage, Bundle errorExtras)
|
public void onError(int seq, Bundle sessionError) throws RemoteException {
|
||||||
throws RemoteException {
|
SessionError error;
|
||||||
dispatchControllerTaskOnHandler(
|
try {
|
||||||
controller -> controller.onError(seq, errorCode, errorMessage, errorExtras));
|
error = SessionError.fromBundle(sessionError);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
Log.w(TAG, "Ignoring malformed Bundle for SessionError", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dispatchControllerTaskOnHandler(controller -> controller.onError(seq, error));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -20,7 +20,6 @@ import static androidx.media3.common.util.Assertions.checkState;
|
|||||||
import static androidx.media3.session.LibraryResult.RESULT_SUCCESS;
|
import static androidx.media3.session.LibraryResult.RESULT_SUCCESS;
|
||||||
import static androidx.media3.session.MediaConstants.ERROR_CODE_AUTHENTICATION_EXPIRED_COMPAT;
|
import static androidx.media3.session.MediaConstants.ERROR_CODE_AUTHENTICATION_EXPIRED_COMPAT;
|
||||||
import static androidx.media3.session.MediaConstants.EXTRAS_KEY_ERROR_RESOLUTION_ACTION_INTENT_COMPAT;
|
import static androidx.media3.session.MediaConstants.EXTRAS_KEY_ERROR_RESOLUTION_ACTION_INTENT_COMPAT;
|
||||||
import static androidx.media3.session.PlayerWrapper.STATUS_CODE_SUCCESS_COMPAT;
|
|
||||||
import static androidx.media3.session.SessionError.ERROR_INVALID_STATE;
|
import static androidx.media3.session.SessionError.ERROR_INVALID_STATE;
|
||||||
import static androidx.media3.session.SessionError.ERROR_NOT_SUPPORTED;
|
import static androidx.media3.session.SessionError.ERROR_NOT_SUPPORTED;
|
||||||
import static androidx.media3.session.SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED;
|
import static androidx.media3.session.SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED;
|
||||||
@ -372,18 +371,20 @@ import java.util.concurrent.Future;
|
|||||||
|
|
||||||
private void maybeUpdateLegacyErrorState(LibraryResult<?> result) {
|
private void maybeUpdateLegacyErrorState(LibraryResult<?> result) {
|
||||||
PlayerWrapper playerWrapper = getPlayerWrapper();
|
PlayerWrapper playerWrapper = getPlayerWrapper();
|
||||||
if (setLegacyErrorState(result)) {
|
if (setLegacyAuthenticationExpiredErrorState(result)) {
|
||||||
// Sync playback state if legacy error state changed.
|
// Sync playback state if legacy error state changed.
|
||||||
getSessionCompat().setPlaybackState(playerWrapper.createPlaybackStateCompat());
|
getSessionCompat().setPlaybackState(playerWrapper.createPlaybackStateCompat());
|
||||||
} else if (playerWrapper.getLegacyStatusCode() != STATUS_CODE_SUCCESS_COMPAT) {
|
} else if (playerWrapper.getLegacyError() != null && result.resultCode == RESULT_SUCCESS) {
|
||||||
playerWrapper.clearLegacyErrorStatus();
|
playerWrapper.clearLegacyErrorStatus();
|
||||||
getSessionCompat().setPlaybackState(playerWrapper.createPlaybackStateCompat());
|
getSessionCompat().setPlaybackState(playerWrapper.createPlaybackStateCompat());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean setLegacyErrorState(LibraryResult<?> result) {
|
private boolean setLegacyAuthenticationExpiredErrorState(LibraryResult<?> result) {
|
||||||
|
PlayerWrapper playerWrapper = getPlayerWrapper();
|
||||||
|
PlayerWrapper.LegacyError legacyError = playerWrapper.getLegacyError();
|
||||||
if (result.resultCode == ERROR_SESSION_AUTHENTICATION_EXPIRED
|
if (result.resultCode == ERROR_SESSION_AUTHENTICATION_EXPIRED
|
||||||
&& getPlayerWrapper().getLegacyStatusCode() != ERROR_SESSION_AUTHENTICATION_EXPIRED) {
|
&& (legacyError == null || legacyError.code != ERROR_SESSION_AUTHENTICATION_EXPIRED)) {
|
||||||
// Mapping this error to the legacy error state provides backwards compatibility for the
|
// Mapping this error to the legacy error state provides backwards compatibility for the
|
||||||
// Automotive OS sign-in.
|
// Automotive OS sign-in.
|
||||||
Bundle bundle = Bundle.EMPTY;
|
Bundle bundle = Bundle.EMPTY;
|
||||||
@ -396,11 +397,11 @@ import java.util.concurrent.Future;
|
|||||||
EXTRAS_KEY_ERROR_RESOLUTION_ACTION_INTENT_COMPAT)) {
|
EXTRAS_KEY_ERROR_RESOLUTION_ACTION_INTENT_COMPAT)) {
|
||||||
bundle = result.sessionError.extras;
|
bundle = result.sessionError.extras;
|
||||||
}
|
}
|
||||||
getPlayerWrapper()
|
playerWrapper.setLegacyError(
|
||||||
.setLegacyErrorStatus(
|
/* isFatal= */ true,
|
||||||
ERROR_CODE_AUTHENTICATION_EXPIRED_COMPAT,
|
ERROR_CODE_AUTHENTICATION_EXPIRED_COMPAT,
|
||||||
getContext().getString(R.string.authentication_required),
|
getContext().getString(R.string.authentication_required),
|
||||||
bundle);
|
bundle);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -1138,6 +1138,9 @@ public class MediaSession {
|
|||||||
/**
|
/**
|
||||||
* Sends a non-fatal error to the given controller.
|
* Sends a non-fatal error to the given controller.
|
||||||
*
|
*
|
||||||
|
* <p>This will call {@link MediaController.Listener#onError(MediaController, SessionError)} of
|
||||||
|
* the given connected controller.
|
||||||
|
*
|
||||||
* <p>Use {@linkplain MediaSession#getMediaNotificationControllerInfo()} to set the error of the
|
* <p>Use {@linkplain MediaSession#getMediaNotificationControllerInfo()} to set the error of the
|
||||||
* {@linkplain android.media.session.PlaybackState playback state} of the legacy platform session.
|
* {@linkplain android.media.session.PlaybackState playback state} of the legacy platform session.
|
||||||
*
|
*
|
||||||
@ -1146,33 +1149,28 @@ public class MediaSession {
|
|||||||
* ControllerInfo#LEGACY_CONTROLLER_VERSION}, an {@link IllegalArgumentException} is thrown.
|
* ControllerInfo#LEGACY_CONTROLLER_VERSION}, an {@link IllegalArgumentException} is thrown.
|
||||||
*
|
*
|
||||||
* @param controllerInfo The controller to send the error to.
|
* @param controllerInfo The controller to send the error to.
|
||||||
* @param errorCode The error code.
|
* @param sessionError The session error.
|
||||||
* @param errorMessageResId A {@code R.string} resource ID.
|
|
||||||
* @param errorExtras A error extras bundle to send additional data.
|
|
||||||
* @exception IllegalArgumentException thrown if an error is attempted to be sent to a legacy
|
* @exception IllegalArgumentException thrown if an error is attempted to be sent to a legacy
|
||||||
* controller.
|
* controller.
|
||||||
*/
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public final void sendError(
|
public final void sendError(ControllerInfo controllerInfo, SessionError sessionError) {
|
||||||
ControllerInfo controllerInfo, int errorCode, int errorMessageResId, Bundle errorExtras) {
|
|
||||||
checkArgument(
|
checkArgument(
|
||||||
controllerInfo.getControllerVersion() != ControllerInfo.LEGACY_CONTROLLER_VERSION);
|
controllerInfo.getControllerVersion() != ControllerInfo.LEGACY_CONTROLLER_VERSION);
|
||||||
impl.sendError(controllerInfo, errorCode, errorMessageResId, errorExtras);
|
impl.sendError(controllerInfo, sessionError);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a non-fatal error to all connected Media3 controllers.
|
* Sends a non-fatal error to all connected Media3 controllers.
|
||||||
*
|
*
|
||||||
* <p>See {@link #sendError(ControllerInfo, int, int, Bundle)} for sending an error to a specific
|
* <p>See {@link #sendError(ControllerInfo, SessionError)} for sending an error to a specific
|
||||||
* controller only.
|
* controller only.
|
||||||
*
|
*
|
||||||
* @param errorCode The error code.
|
* @param sessionError The session error.
|
||||||
* @param errorMessageResourceId A {@code R.string} resource ID of a localized error message.
|
|
||||||
* @param errorExtras An error extras bundle to send additional data.
|
|
||||||
*/
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public final void sendError(int errorCode, int errorMessageResourceId, Bundle errorExtras) {
|
public final void sendError(SessionError sessionError) {
|
||||||
impl.sendError(errorCode, errorMessageResourceId, errorExtras);
|
impl.sendError(sessionError);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* package */ final MediaSessionCompat getSessionCompat() {
|
/* package */ final MediaSessionCompat getSessionCompat() {
|
||||||
@ -2004,8 +2002,7 @@ public class MediaSession {
|
|||||||
|
|
||||||
default void onRenderedFirstFrame(int seq) throws RemoteException {}
|
default void onRenderedFirstFrame(int seq) throws RemoteException {}
|
||||||
|
|
||||||
default void onError(int seq, int errorCode, String errorMessage, Bundle errorExtras)
|
default void onError(int seq, SessionError sessionError) throws RemoteException {}
|
||||||
throws RemoteException {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,7 +33,7 @@ import static androidx.media3.common.util.Util.postOrRun;
|
|||||||
import static androidx.media3.session.MediaSessionStub.UNKNOWN_SEQUENCE_NUMBER;
|
import static androidx.media3.session.MediaSessionStub.UNKNOWN_SEQUENCE_NUMBER;
|
||||||
import static androidx.media3.session.SessionError.ERROR_SESSION_DISCONNECTED;
|
import static androidx.media3.session.SessionError.ERROR_SESSION_DISCONNECTED;
|
||||||
import static androidx.media3.session.SessionError.ERROR_UNKNOWN;
|
import static androidx.media3.session.SessionError.ERROR_UNKNOWN;
|
||||||
import static androidx.media3.session.SessionError.INFO_SKIPPED;
|
import static androidx.media3.session.SessionError.INFO_CANCELLED;
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
@ -113,7 +113,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
|
|
||||||
public static final String TAG = "MediaSessionImpl";
|
public static final String TAG = "MediaSessionImpl";
|
||||||
|
|
||||||
private static final SessionResult RESULT_WHEN_CLOSED = new SessionResult(INFO_SKIPPED);
|
private static final SessionResult RESULT_WHEN_CLOSED = new SessionResult(INFO_CANCELLED);
|
||||||
|
|
||||||
private final Object lock = new Object();
|
private final Object lock = new Object();
|
||||||
|
|
||||||
@ -625,31 +625,25 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
controller, (cb, seq) -> cb.sendCustomCommand(seq, command, args));
|
controller, (cb, seq) -> cb.sendCustomCommand(seq, command, args));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendError(
|
public void sendError(ControllerInfo controllerInfo, SessionError sessionError) {
|
||||||
ControllerInfo controllerInfo,
|
|
||||||
int errorCode,
|
|
||||||
int errorMessageResourceId,
|
|
||||||
Bundle errorExtras) {
|
|
||||||
if (controllerInfo.getInterfaceVersion() < 4) {
|
if (controllerInfo.getInterfaceVersion() < 4) {
|
||||||
// IMediaController.onError introduced with interface version 4.
|
// IMediaController.onError introduced with interface version 4.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String errorMessage = context.getString(errorMessageResourceId);
|
|
||||||
dispatchRemoteControllerTaskWithoutReturn(
|
dispatchRemoteControllerTaskWithoutReturn(
|
||||||
controllerInfo,
|
controllerInfo, (callback, seq) -> callback.onError(seq, sessionError));
|
||||||
(callback, seq) -> callback.onError(seq, errorCode, errorMessage, errorExtras));
|
|
||||||
if (isMediaNotificationController(controllerInfo)) {
|
if (isMediaNotificationController(controllerInfo)) {
|
||||||
dispatchRemoteControllerTaskToLegacyStub(
|
dispatchRemoteControllerTaskToLegacyStub(
|
||||||
(callback, seq) -> callback.onError(seq, errorCode, errorMessage, errorExtras));
|
(callback, seq) -> callback.onError(seq, sessionError));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendError(int errorCode, int errorMessageResourceId, Bundle errorExtras) {
|
public void sendError(SessionError sessionError) {
|
||||||
// Send error messages only to Media3 controllers.
|
// Send error messages only to Media3 controllers.
|
||||||
ImmutableList<ControllerInfo> connectedControllers =
|
ImmutableList<ControllerInfo> connectedControllers =
|
||||||
sessionStub.getConnectedControllersManager().getConnectedControllers();
|
sessionStub.getConnectedControllersManager().getConnectedControllers();
|
||||||
for (int i = 0; i < connectedControllers.size(); i++) {
|
for (int i = 0; i < connectedControllers.size(); i++) {
|
||||||
sendError(connectedControllers.get(i), errorCode, errorMessageResourceId, errorExtras);
|
sendError(connectedControllers.get(i), sessionError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1118,9 +1118,13 @@ import org.checkerframework.checker.initialization.qual.Initialized;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(int seq, int errorCode, String errorMessage, Bundle errorExtras) {
|
public void onError(int seq, SessionError sessionError) {
|
||||||
PlayerWrapper playerWrapper = sessionImpl.getPlayerWrapper();
|
PlayerWrapper playerWrapper = sessionImpl.getPlayerWrapper();
|
||||||
playerWrapper.setLegacyErrorStatus(errorCode, errorMessage, errorExtras);
|
playerWrapper.setLegacyError(
|
||||||
|
/* isFatal= */ false,
|
||||||
|
LegacyConversions.convertToLegacyErrorCode(sessionError.code),
|
||||||
|
sessionError.message,
|
||||||
|
sessionError.extras);
|
||||||
sessionCompat.setPlaybackState(playerWrapper.createPlaybackStateCompat());
|
sessionCompat.setPlaybackState(playerWrapper.createPlaybackStateCompat());
|
||||||
playerWrapper.clearLegacyErrorStatus();
|
playerWrapper.clearLegacyErrorStatus();
|
||||||
sessionCompat.setPlaybackState(playerWrapper.createPlaybackStateCompat());
|
sessionCompat.setPlaybackState(playerWrapper.createPlaybackStateCompat());
|
||||||
|
@ -57,7 +57,7 @@ import static androidx.media3.session.SessionError.ERROR_NOT_SUPPORTED;
|
|||||||
import static androidx.media3.session.SessionError.ERROR_PERMISSION_DENIED;
|
import static androidx.media3.session.SessionError.ERROR_PERMISSION_DENIED;
|
||||||
import static androidx.media3.session.SessionError.ERROR_SESSION_DISCONNECTED;
|
import static androidx.media3.session.SessionError.ERROR_SESSION_DISCONNECTED;
|
||||||
import static androidx.media3.session.SessionError.ERROR_UNKNOWN;
|
import static androidx.media3.session.SessionError.ERROR_UNKNOWN;
|
||||||
import static androidx.media3.session.SessionError.INFO_SKIPPED;
|
import static androidx.media3.session.SessionError.INFO_CANCELLED;
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
@ -278,7 +278,7 @@ import java.util.concurrent.ExecutionException;
|
|||||||
result = checkNotNull(future.get(), "LibraryResult must not be null");
|
result = checkNotNull(future.get(), "LibraryResult must not be null");
|
||||||
} catch (CancellationException e) {
|
} catch (CancellationException e) {
|
||||||
Log.w(TAG, "Library operation cancelled", e);
|
Log.w(TAG, "Library operation cancelled", e);
|
||||||
result = LibraryResult.ofError(INFO_SKIPPED);
|
result = LibraryResult.ofError(INFO_CANCELLED);
|
||||||
} catch (ExecutionException | InterruptedException e) {
|
} catch (ExecutionException | InterruptedException e) {
|
||||||
Log.w(TAG, "Library operation failed", e);
|
Log.w(TAG, "Library operation failed", e);
|
||||||
result = LibraryResult.ofError(ERROR_UNKNOWN);
|
result = LibraryResult.ofError(ERROR_UNKNOWN);
|
||||||
@ -2141,9 +2141,8 @@ import java.util.concurrent.ExecutionException;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(int sequenceNumber, int errorCode, String errorMessage, Bundle errorExtras)
|
public void onError(int sequenceNumber, SessionError sessionError) throws RemoteException {
|
||||||
throws RemoteException {
|
iController.onError(sequenceNumber, sessionError.toBundle());
|
||||||
iController.onError(sequenceNumber, errorCode, errorMessage, errorExtras);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -50,7 +50,6 @@ import androidx.media3.common.VideoSize;
|
|||||||
import androidx.media3.common.text.CueGroup;
|
import androidx.media3.common.text.CueGroup;
|
||||||
import androidx.media3.common.util.Log;
|
import androidx.media3.common.util.Log;
|
||||||
import androidx.media3.common.util.Size;
|
import androidx.media3.common.util.Size;
|
||||||
import androidx.media3.common.util.Util;
|
|
||||||
import androidx.media3.session.legacy.MediaSessionCompat;
|
import androidx.media3.session.legacy.MediaSessionCompat;
|
||||||
import androidx.media3.session.legacy.PlaybackStateCompat;
|
import androidx.media3.session.legacy.PlaybackStateCompat;
|
||||||
import androidx.media3.session.legacy.VolumeProviderCompat;
|
import androidx.media3.session.legacy.VolumeProviderCompat;
|
||||||
@ -64,13 +63,28 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
/* package */ final class PlayerWrapper extends ForwardingPlayer {
|
/* package */ final class PlayerWrapper extends ForwardingPlayer {
|
||||||
|
|
||||||
/* package */ static final int STATUS_CODE_SUCCESS_COMPAT = -1;
|
/** Describes a legacy error. */
|
||||||
|
public static final class LegacyError {
|
||||||
|
public final boolean isFatal;
|
||||||
|
@PlaybackStateCompat.ErrorCode public final int code;
|
||||||
|
@Nullable public final String message;
|
||||||
|
public final Bundle extras;
|
||||||
|
|
||||||
|
/** Creates an instance. */
|
||||||
|
private LegacyError(
|
||||||
|
boolean isFatal,
|
||||||
|
@PlaybackStateCompat.ErrorCode int code,
|
||||||
|
@Nullable String message,
|
||||||
|
@Nullable Bundle extras) {
|
||||||
|
this.isFatal = isFatal;
|
||||||
|
this.code = code;
|
||||||
|
this.message = message;
|
||||||
|
this.extras = extras != null ? extras : Bundle.EMPTY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final boolean playIfSuppressed;
|
private final boolean playIfSuppressed;
|
||||||
|
@Nullable private LegacyError legacyError;
|
||||||
private int legacyStatusCode;
|
|
||||||
@Nullable private String legacyErrorMessage;
|
|
||||||
@Nullable private Bundle legacyErrorExtras;
|
|
||||||
@Nullable private Bundle legacyExtras;
|
@Nullable private Bundle legacyExtras;
|
||||||
private ImmutableList<CommandButton> customLayout;
|
private ImmutableList<CommandButton> customLayout;
|
||||||
private SessionCommands availableSessionCommands;
|
private SessionCommands availableSessionCommands;
|
||||||
@ -89,7 +103,6 @@ import java.util.List;
|
|||||||
this.availableSessionCommands = availableSessionCommands;
|
this.availableSessionCommands = availableSessionCommands;
|
||||||
this.availablePlayerCommands = availablePlayerCommands;
|
this.availablePlayerCommands = availablePlayerCommands;
|
||||||
this.legacyExtras = legacyExtras;
|
this.legacyExtras = legacyExtras;
|
||||||
legacyStatusCode = STATUS_CODE_SUCCESS_COMPAT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAvailableCommands(
|
public void setAvailableCommands(
|
||||||
@ -128,36 +141,37 @@ import java.util.List;
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the legacy error code.
|
* Sets the legacy error that will be used when the next {@linkplain #createPlaybackStateCompat()
|
||||||
|
* legacy playback state is created}.
|
||||||
*
|
*
|
||||||
* <p>This sets the legacy {@link PlaybackStateCompat} to {@link PlaybackStateCompat#STATE_ERROR}
|
* <p>This sets the legacy {@link PlaybackStateCompat} to {@link PlaybackStateCompat#STATE_ERROR}
|
||||||
* and calls {@link PlaybackStateCompat.Builder#setErrorMessage(int, CharSequence)} and {@link
|
* if the error is fatal, calls {@link PlaybackStateCompat.Builder#setErrorMessage(int,
|
||||||
* PlaybackStateCompat.Builder#setExtras(Bundle)} with the given arguments.
|
* CharSequence)} and includes the entries of the extras in the {@link Bundle} set with {@link
|
||||||
|
* PlaybackStateCompat.Builder#setExtras(Bundle)}.
|
||||||
*
|
*
|
||||||
* <p>Use {@link #clearLegacyErrorStatus()} to clear the error state and to resume to the actual
|
* <p>Use {@link #clearLegacyErrorStatus()} to clear the error.
|
||||||
* playback state reflecting the player.
|
|
||||||
*
|
*
|
||||||
|
* @param isFatal Whether the legacy error is fatal.
|
||||||
* @param errorCode The legacy error code.
|
* @param errorCode The legacy error code.
|
||||||
* @param errorMessage The legacy error message.
|
* @param errorMessage The legacy error message.
|
||||||
* @param extras The extras.
|
* @param extras The extras.
|
||||||
*/
|
*/
|
||||||
public void setLegacyErrorStatus(int errorCode, String errorMessage, Bundle extras) {
|
public void setLegacyError(boolean isFatal, int errorCode, String errorMessage, Bundle extras) {
|
||||||
checkState(errorCode != STATUS_CODE_SUCCESS_COMPAT);
|
legacyError = new LegacyError(isFatal, errorCode, errorMessage, extras);
|
||||||
legacyStatusCode = errorCode;
|
|
||||||
legacyErrorMessage = errorMessage;
|
|
||||||
legacyErrorExtras = extras;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the legacy status code. */
|
/** Returns the legacy error or null if not set. */
|
||||||
public int getLegacyStatusCode() {
|
@Nullable
|
||||||
return legacyStatusCode;
|
public LegacyError getLegacyError() {
|
||||||
|
return legacyError;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Clears the legacy error status. */
|
/**
|
||||||
|
* Clears the legacy error to resolve the error when {@linkplain #createPlaybackStateCompat()
|
||||||
|
* creating} the next legacy playback state.
|
||||||
|
*/
|
||||||
public void clearLegacyErrorStatus() {
|
public void clearLegacyErrorStatus() {
|
||||||
legacyStatusCode = STATUS_CODE_SUCCESS_COMPAT;
|
legacyError = null;
|
||||||
legacyErrorMessage = null;
|
|
||||||
legacyErrorExtras = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -1016,24 +1030,23 @@ import java.util.List;
|
|||||||
}
|
}
|
||||||
|
|
||||||
public PlaybackStateCompat createPlaybackStateCompat() {
|
public PlaybackStateCompat createPlaybackStateCompat() {
|
||||||
if (legacyStatusCode != STATUS_CODE_SUCCESS_COMPAT) {
|
LegacyError legacyError = this.legacyError;
|
||||||
Bundle extras;
|
if (legacyError != null && legacyError.isFatal) {
|
||||||
|
Bundle extras = new Bundle(legacyError.extras);
|
||||||
if (legacyExtras != null) {
|
if (legacyExtras != null) {
|
||||||
extras = new Bundle(checkNotNull(legacyErrorExtras));
|
|
||||||
extras.putAll(legacyExtras);
|
extras.putAll(legacyExtras);
|
||||||
} else {
|
|
||||||
extras = checkNotNull(legacyErrorExtras);
|
|
||||||
}
|
}
|
||||||
return new PlaybackStateCompat.Builder()
|
return new PlaybackStateCompat.Builder()
|
||||||
.setState(
|
.setState(
|
||||||
PlaybackStateCompat.STATE_ERROR,
|
PlaybackStateCompat.STATE_ERROR,
|
||||||
/* position= */ PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN,
|
/* position= */ PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN,
|
||||||
/* playbackSpeed= */ 0,
|
/* playbackSpeed= */ .0f,
|
||||||
/* updateTime= */ SystemClock.elapsedRealtime())
|
/* updateTime= */ SystemClock.elapsedRealtime())
|
||||||
.setActions(0)
|
.setActions(0)
|
||||||
.setBufferedPosition(0)
|
.setBufferedPosition(0)
|
||||||
.setErrorMessage(legacyStatusCode, checkNotNull(legacyErrorMessage))
|
|
||||||
.setExtras(extras)
|
.setExtras(extras)
|
||||||
|
.setErrorMessage(legacyError.code, checkNotNull(legacyError.message))
|
||||||
|
.setExtras(legacyError.extras)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
@Nullable PlaybackException playerError = getPlayerError();
|
@Nullable PlaybackException playerError = getPlayerError();
|
||||||
@ -1051,7 +1064,10 @@ import java.util.List;
|
|||||||
: MediaSessionCompat.QueueItem.UNKNOWN_ID;
|
: MediaSessionCompat.QueueItem.UNKNOWN_ID;
|
||||||
float playbackSpeed = getPlaybackParameters().speed;
|
float playbackSpeed = getPlaybackParameters().speed;
|
||||||
float sessionPlaybackSpeed = isPlaying() ? playbackSpeed : 0f;
|
float sessionPlaybackSpeed = isPlaying() ? playbackSpeed : 0f;
|
||||||
Bundle extras = legacyExtras != null ? new Bundle(legacyExtras) : new Bundle();
|
Bundle extras = legacyError != null ? new Bundle(legacyError.extras) : new Bundle();
|
||||||
|
if (legacyExtras != null && !legacyExtras.isEmpty()) {
|
||||||
|
extras.putAll(legacyExtras);
|
||||||
|
}
|
||||||
extras.putFloat(EXTRAS_KEY_PLAYBACK_SPEED_COMPAT, playbackSpeed);
|
extras.putFloat(EXTRAS_KEY_PLAYBACK_SPEED_COMPAT, playbackSpeed);
|
||||||
@Nullable MediaItem currentMediaItem = getCurrentMediaItemWithCommandCheck();
|
@Nullable MediaItem currentMediaItem = getCurrentMediaItemWithCommandCheck();
|
||||||
if (currentMediaItem != null && !MediaItem.DEFAULT_MEDIA_ID.equals(currentMediaItem.mediaId)) {
|
if (currentMediaItem != null && !MediaItem.DEFAULT_MEDIA_ID.equals(currentMediaItem.mediaId)) {
|
||||||
@ -1092,7 +1108,9 @@ import java.util.List;
|
|||||||
}
|
}
|
||||||
if (playerError != null) {
|
if (playerError != null) {
|
||||||
builder.setErrorMessage(
|
builder.setErrorMessage(
|
||||||
PlaybackStateCompat.ERROR_CODE_UNKNOWN_ERROR, Util.castNonNull(playerError.getMessage()));
|
LegacyConversions.convertToLegacyErrorCode(playerError), playerError.getMessage());
|
||||||
|
} else if (legacyError != null) {
|
||||||
|
builder.setErrorMessage(legacyError.code, legacyError.message);
|
||||||
}
|
}
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
@ -46,26 +46,32 @@ public final class SessionError {
|
|||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
@Target(TYPE_USE)
|
@Target(TYPE_USE)
|
||||||
@IntDef({
|
@IntDef({
|
||||||
INFO_SKIPPED,
|
|
||||||
ERROR_UNKNOWN,
|
ERROR_UNKNOWN,
|
||||||
ERROR_INVALID_STATE,
|
ERROR_INVALID_STATE,
|
||||||
ERROR_BAD_VALUE,
|
ERROR_BAD_VALUE,
|
||||||
ERROR_PERMISSION_DENIED,
|
ERROR_PERMISSION_DENIED,
|
||||||
ERROR_IO,
|
ERROR_IO,
|
||||||
ERROR_SESSION_DISCONNECTED,
|
|
||||||
ERROR_NOT_SUPPORTED,
|
ERROR_NOT_SUPPORTED,
|
||||||
|
ERROR_SESSION_DISCONNECTED,
|
||||||
ERROR_SESSION_AUTHENTICATION_EXPIRED,
|
ERROR_SESSION_AUTHENTICATION_EXPIRED,
|
||||||
ERROR_SESSION_PREMIUM_ACCOUNT_REQUIRED,
|
ERROR_SESSION_PREMIUM_ACCOUNT_REQUIRED,
|
||||||
ERROR_SESSION_CONCURRENT_STREAM_LIMIT,
|
ERROR_SESSION_CONCURRENT_STREAM_LIMIT,
|
||||||
|
ERROR_SESSION_CONTENT_ALREADY_PLAYING,
|
||||||
|
ERROR_SESSION_END_OF_PLAYLIST,
|
||||||
ERROR_SESSION_PARENTAL_CONTROL_RESTRICTED,
|
ERROR_SESSION_PARENTAL_CONTROL_RESTRICTED,
|
||||||
ERROR_SESSION_NOT_AVAILABLE_IN_REGION,
|
ERROR_SESSION_NOT_AVAILABLE_IN_REGION,
|
||||||
ERROR_SESSION_SKIP_LIMIT_REACHED,
|
ERROR_SESSION_SKIP_LIMIT_REACHED,
|
||||||
ERROR_SESSION_SETUP_REQUIRED
|
ERROR_SESSION_SETUP_REQUIRED,
|
||||||
|
INFO_CANCELLED
|
||||||
})
|
})
|
||||||
public @interface Code {}
|
public @interface Code {}
|
||||||
|
|
||||||
/** Info code representing that the command is skipped. */
|
// Info codes (> 0).
|
||||||
public static final int INFO_SKIPPED = 1;
|
|
||||||
|
/** Info code representing that the command was cancelled. */
|
||||||
|
public static final int INFO_CANCELLED = 1;
|
||||||
|
|
||||||
|
// Error codes (< 0).
|
||||||
|
|
||||||
/** Error code representing that the command is ended with an unknown error. */
|
/** Error code representing that the command is ended with an unknown error. */
|
||||||
public static final int ERROR_UNKNOWN = -1;
|
public static final int ERROR_UNKNOWN = -1;
|
||||||
@ -74,49 +80,106 @@ public final class SessionError {
|
|||||||
* Error code representing that the command cannot be completed because the current state is not
|
* Error code representing that the command cannot be completed because the current state is not
|
||||||
* valid for the command.
|
* valid for the command.
|
||||||
*/
|
*/
|
||||||
public static final int ERROR_INVALID_STATE = -2;
|
public static final int ERROR_INVALID_STATE = PlaybackException.ERROR_CODE_INVALID_STATE;
|
||||||
|
|
||||||
/** Error code representing that an argument is illegal. */
|
/** Error code representing that an argument is illegal. */
|
||||||
public static final int ERROR_BAD_VALUE = -3;
|
public static final int ERROR_BAD_VALUE = PlaybackException.ERROR_CODE_BAD_VALUE;
|
||||||
|
|
||||||
/** Error code representing that the command is not allowed. */
|
/** Error code representing that the command is not allowed. */
|
||||||
public static final int ERROR_PERMISSION_DENIED = -4;
|
public static final int ERROR_PERMISSION_DENIED = PlaybackException.ERROR_CODE_PERMISSION_DENIED;
|
||||||
|
|
||||||
/** Error code representing that a file or network related error happened. */
|
/** Error code representing that a file or network related error happened. */
|
||||||
public static final int ERROR_IO = -5;
|
public static final int ERROR_IO = -5;
|
||||||
|
|
||||||
/** Error code representing that the command is not supported. */
|
/** Error code representing that the command is not supported. */
|
||||||
public static final int ERROR_NOT_SUPPORTED = -6;
|
public static final int ERROR_NOT_SUPPORTED = PlaybackException.ERROR_CODE_NOT_SUPPORTED;
|
||||||
|
|
||||||
/** Error code representing that the session and controller were disconnected. */
|
/** Error code representing that the session and controller were disconnected. */
|
||||||
public static final int ERROR_SESSION_DISCONNECTED = -100;
|
public static final int ERROR_SESSION_DISCONNECTED = PlaybackException.ERROR_CODE_DISCONNECTED;
|
||||||
|
|
||||||
/** Error code representing that the authentication has expired. */
|
/** Error code representing that the authentication has expired. */
|
||||||
public static final int ERROR_SESSION_AUTHENTICATION_EXPIRED = -102;
|
public static final int ERROR_SESSION_AUTHENTICATION_EXPIRED =
|
||||||
|
PlaybackException.ERROR_CODE_AUTHENTICATION_EXPIRED;
|
||||||
|
|
||||||
/** Error code representing that a premium account is required. */
|
/** Error code representing that a premium account is required. */
|
||||||
public static final int ERROR_SESSION_PREMIUM_ACCOUNT_REQUIRED = -103;
|
public static final int ERROR_SESSION_PREMIUM_ACCOUNT_REQUIRED =
|
||||||
|
PlaybackException.ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED;
|
||||||
|
|
||||||
/** Error code representing that too many concurrent streams are detected. */
|
/** Error code representing that too many concurrent streams are detected. */
|
||||||
public static final int ERROR_SESSION_CONCURRENT_STREAM_LIMIT = -104;
|
public static final int ERROR_SESSION_CONCURRENT_STREAM_LIMIT =
|
||||||
|
PlaybackException.ERROR_CODE_CONCURRENT_STREAM_LIMIT;
|
||||||
|
|
||||||
/** Error code representing that the content is blocked due to parental controls. */
|
/** Error code representing that the content is blocked due to parental controls. */
|
||||||
public static final int ERROR_SESSION_PARENTAL_CONTROL_RESTRICTED = -105;
|
public static final int ERROR_SESSION_PARENTAL_CONTROL_RESTRICTED =
|
||||||
|
PlaybackException.ERROR_CODE_PARENTAL_CONTROL_RESTRICTED;
|
||||||
|
|
||||||
/** Error code representing that the content is blocked due to being regionally unavailable. */
|
/** Error code representing that the content is blocked due to being regionally unavailable. */
|
||||||
public static final int ERROR_SESSION_NOT_AVAILABLE_IN_REGION = -106;
|
public static final int ERROR_SESSION_NOT_AVAILABLE_IN_REGION =
|
||||||
|
PlaybackException.ERROR_CODE_NOT_AVAILABLE_IN_REGION;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Error code representing that the application cannot skip any more because the skip limit is
|
* Error code representing that the application cannot skip any more because the skip limit is
|
||||||
* reached.
|
* reached.
|
||||||
*/
|
*/
|
||||||
public static final int ERROR_SESSION_SKIP_LIMIT_REACHED = -107;
|
public static final int ERROR_SESSION_SKIP_LIMIT_REACHED =
|
||||||
|
PlaybackException.ERROR_CODE_SKIP_LIMIT_REACHED;
|
||||||
|
|
||||||
/** Error code representing that the session needs user's manual intervention. */
|
/** Error code representing that the session needs user's manual intervention. */
|
||||||
public static final int ERROR_SESSION_SETUP_REQUIRED = -108;
|
public static final int ERROR_SESSION_SETUP_REQUIRED =
|
||||||
|
PlaybackException.ERROR_CODE_SETUP_REQUIRED;
|
||||||
|
|
||||||
|
/** Error code representing that navigation failed because the the playlist was exhausted. */
|
||||||
|
public static final int ERROR_SESSION_END_OF_PLAYLIST =
|
||||||
|
PlaybackException.ERROR_CODE_END_OF_PLAYLIST;
|
||||||
|
|
||||||
|
/** Error code representing that the requested content is already playing. */
|
||||||
|
public static final int ERROR_SESSION_CONTENT_ALREADY_PLAYING =
|
||||||
|
PlaybackException.ERROR_CODE_CONTENT_ALREADY_PLAYING;
|
||||||
|
|
||||||
/** Default error message. Only used by deprecated methods and for backwards compatibility. */
|
/** Default error message. Only used by deprecated methods and for backwards compatibility. */
|
||||||
public static final String DEFAULT_ERROR_MESSAGE = "no error message provided";
|
/* package */ static final String DEFAULT_ERROR_MESSAGE = "no error message provided";
|
||||||
|
|
||||||
|
/** Returns the name of a given error code. */
|
||||||
|
public static String getErrorCodeName(@Code int errorCode) {
|
||||||
|
switch (errorCode) {
|
||||||
|
case ERROR_UNKNOWN:
|
||||||
|
return "ERROR_UNKNOWN";
|
||||||
|
case ERROR_INVALID_STATE:
|
||||||
|
return "ERROR_INVALID_STATE";
|
||||||
|
case ERROR_BAD_VALUE:
|
||||||
|
return "ERROR_BAD_VALUE";
|
||||||
|
case ERROR_PERMISSION_DENIED:
|
||||||
|
return "ERROR_PERMISSION_DENIED";
|
||||||
|
case ERROR_IO:
|
||||||
|
return "ERROR_IO";
|
||||||
|
case ERROR_NOT_SUPPORTED:
|
||||||
|
return "ERROR_NOT_SUPPORTED";
|
||||||
|
case ERROR_SESSION_DISCONNECTED:
|
||||||
|
return "ERROR_SESSION_DISCONNECTED";
|
||||||
|
case ERROR_SESSION_AUTHENTICATION_EXPIRED:
|
||||||
|
return "ERROR_SESSION_AUTHENTICATION_EXPIRED";
|
||||||
|
case ERROR_SESSION_PREMIUM_ACCOUNT_REQUIRED:
|
||||||
|
return "ERROR_SESSION_PREMIUM_ACCOUNT_REQUIRED";
|
||||||
|
case ERROR_SESSION_CONCURRENT_STREAM_LIMIT:
|
||||||
|
return "ERROR_SESSION_CONCURRENT_STREAM_LIMIT";
|
||||||
|
case ERROR_SESSION_CONTENT_ALREADY_PLAYING:
|
||||||
|
return "ERROR_SESSION_CONTENT_ALREADY_PLAYING";
|
||||||
|
case ERROR_SESSION_END_OF_PLAYLIST:
|
||||||
|
return "ERROR_SESSION_END_OF_PLAYLIST";
|
||||||
|
case ERROR_SESSION_PARENTAL_CONTROL_RESTRICTED:
|
||||||
|
return "ERROR_SESSION_PARENTAL_CONTROL_RESTRICTED";
|
||||||
|
case ERROR_SESSION_NOT_AVAILABLE_IN_REGION:
|
||||||
|
return "ERROR_SESSION_NOT_AVAILABLE_IN_REGION";
|
||||||
|
case ERROR_SESSION_SKIP_LIMIT_REACHED:
|
||||||
|
return "ERROR_SESSION_SKIP_LIMIT_REACHED";
|
||||||
|
case ERROR_SESSION_SETUP_REQUIRED:
|
||||||
|
return "ERROR_SESSION_SETUP_REQUIRED";
|
||||||
|
case INFO_CANCELLED:
|
||||||
|
return "INFO_CANCELLED";
|
||||||
|
default:
|
||||||
|
return "invalid error code";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public @SessionError.Code int code;
|
public @SessionError.Code int code;
|
||||||
public String message;
|
public String message;
|
||||||
@ -142,7 +205,7 @@ public final class SessionError {
|
|||||||
* @throws IllegalArgumentException if the result code is not an error result code.
|
* @throws IllegalArgumentException if the result code is not an error result code.
|
||||||
*/
|
*/
|
||||||
public SessionError(@SessionError.Code int code, String message, Bundle extras) {
|
public SessionError(@SessionError.Code int code, String message, Bundle extras) {
|
||||||
Assertions.checkArgument(code < 0 || code == INFO_SKIPPED);
|
Assertions.checkArgument(code < 0 || code == INFO_CANCELLED);
|
||||||
this.code = code;
|
this.code = code;
|
||||||
this.message = message;
|
this.message = message;
|
||||||
this.extras = extras;
|
this.extras = extras;
|
||||||
|
@ -57,7 +57,7 @@ public final class SessionResult {
|
|||||||
@Target(TYPE_USE)
|
@Target(TYPE_USE)
|
||||||
@IntDef({
|
@IntDef({
|
||||||
RESULT_SUCCESS,
|
RESULT_SUCCESS,
|
||||||
SessionError.INFO_SKIPPED,
|
SessionError.INFO_CANCELLED,
|
||||||
SessionError.ERROR_UNKNOWN,
|
SessionError.ERROR_UNKNOWN,
|
||||||
SessionError.ERROR_INVALID_STATE,
|
SessionError.ERROR_INVALID_STATE,
|
||||||
SessionError.ERROR_BAD_VALUE,
|
SessionError.ERROR_BAD_VALUE,
|
||||||
@ -86,7 +86,7 @@ public final class SessionResult {
|
|||||||
public static final int RESULT_SUCCESS = 0;
|
public static final int RESULT_SUCCESS = 0;
|
||||||
|
|
||||||
/** Result code representing that the command is skipped. */
|
/** Result code representing that the command is skipped. */
|
||||||
public static final int RESULT_INFO_SKIPPED = SessionError.INFO_SKIPPED;
|
public static final int RESULT_INFO_SKIPPED = SessionError.INFO_CANCELLED;
|
||||||
|
|
||||||
/** Result code representing that the command is ended with an unknown error. */
|
/** Result code representing that the command is ended with an unknown error. */
|
||||||
public static final int RESULT_ERROR_UNKNOWN = SessionError.ERROR_UNKNOWN;
|
public static final int RESULT_ERROR_UNKNOWN = SessionError.ERROR_UNKNOWN;
|
||||||
|
@ -443,6 +443,7 @@ public final class PlaybackStateCompat implements Parcelable {
|
|||||||
*/
|
*/
|
||||||
public static final int SHUFFLE_MODE_GROUP = 2;
|
public static final int SHUFFLE_MODE_GROUP = 2;
|
||||||
|
|
||||||
|
/** Supported error codes. */
|
||||||
@IntDef({
|
@IntDef({
|
||||||
ERROR_CODE_UNKNOWN_ERROR,
|
ERROR_CODE_UNKNOWN_ERROR,
|
||||||
ERROR_CODE_APP_ERROR,
|
ERROR_CODE_APP_ERROR,
|
||||||
@ -458,7 +459,7 @@ public final class PlaybackStateCompat implements Parcelable {
|
|||||||
ERROR_CODE_END_OF_QUEUE
|
ERROR_CODE_END_OF_QUEUE
|
||||||
})
|
})
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
private @interface ErrorCode {}
|
public @interface ErrorCode {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the default error code and indicates that none of the other error codes applies. The
|
* This is the default error code and indicates that none of the other error codes applies. The
|
||||||
|
@ -34,7 +34,7 @@ interface IRemoteMediaSession {
|
|||||||
void setCustomLayout(String sessionId, in List<Bundle> layout);
|
void setCustomLayout(String sessionId, in List<Bundle> layout);
|
||||||
void setSessionExtras(String sessionId, in Bundle extras);
|
void setSessionExtras(String sessionId, in Bundle extras);
|
||||||
void setSessionExtrasForController(String sessionId, in String controllerKey, in Bundle extras);
|
void setSessionExtrasForController(String sessionId, in String controllerKey, in Bundle extras);
|
||||||
void sendError(String sessionId, String controllerKey, int errorCode, int errorMessageResId, in Bundle errorExtras);
|
void sendError(String sessionId, String controllerKey, in Bundle SessionError);
|
||||||
void setSessionActivity(String sessionId, in PendingIntent sessionActivity);
|
void setSessionActivity(String sessionId, in PendingIntent sessionActivity);
|
||||||
|
|
||||||
// Player Methods
|
// Player Methods
|
||||||
|
@ -42,6 +42,5 @@ interface IRemoteMediaSessionCompat {
|
|||||||
void sendSessionEvent(String sessionTag, String event, in Bundle extras);
|
void sendSessionEvent(String sessionTag, String event, in Bundle extras);
|
||||||
void setCaptioningEnabled(String sessionTag, boolean enabled);
|
void setCaptioningEnabled(String sessionTag, boolean enabled);
|
||||||
void setSessionExtras(String sessionTag, in Bundle extras);
|
void setSessionExtras(String sessionTag, in Bundle extras);
|
||||||
void sendError(String sessionTag, int errorCode, int errorMessageIntRes, in Bundle errorExtras);
|
|
||||||
int getCallbackMethodCount(String sessionTag, String methodName);
|
int getCallbackMethodCount(String sessionTag, String methodName);
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,8 @@ public class MediaBrowserConstants {
|
|||||||
public static final String PARENT_ID_AUTH_EXPIRED_ERROR = "parent_auth_expired_error";
|
public static final String PARENT_ID_AUTH_EXPIRED_ERROR = "parent_auth_expired_error";
|
||||||
public static final String PARENT_ID_AUTH_EXPIRED_ERROR_DEPRECATED =
|
public static final String PARENT_ID_AUTH_EXPIRED_ERROR_DEPRECATED =
|
||||||
"parent_auth_expired_error_deprecated";
|
"parent_auth_expired_error_deprecated";
|
||||||
|
public static final String PARENT_ID_AUTH_EXPIRED_ERROR_NON_FATAL =
|
||||||
|
"parent_auth_expired_error_non_fatal";
|
||||||
public static final String PARENT_ID_AUTH_EXPIRED_ERROR_KEY_ERROR_RESOLUTION_ACTION_LABEL =
|
public static final String PARENT_ID_AUTH_EXPIRED_ERROR_KEY_ERROR_RESOLUTION_ACTION_LABEL =
|
||||||
"parent_auth_expired_error_label";
|
"parent_auth_expired_error_label";
|
||||||
|
|
||||||
|
@ -25,6 +25,10 @@ public class MediaBrowserServiceCompatConstants {
|
|||||||
public static final String TEST_GET_CHILDREN = "getChildren_correctMetadataExtras";
|
public static final String TEST_GET_CHILDREN = "getChildren_correctMetadataExtras";
|
||||||
public static final String TEST_GET_CHILDREN_WITH_NULL_LIST =
|
public static final String TEST_GET_CHILDREN_WITH_NULL_LIST =
|
||||||
"onChildrenChanged_withNullChildrenListInLegacyService_convertedToSessionError";
|
"onChildrenChanged_withNullChildrenListInLegacyService_convertedToSessionError";
|
||||||
|
public static final String TEST_GET_CHILDREN_FATAL_AUTHENTICATION_ERROR =
|
||||||
|
"getLibraryRoot_fatalAuthenticationError_receivesPlaybackException";
|
||||||
|
public static final String TEST_GET_CHILDREN_NON_FATAL_AUTHENTICATION_ERROR =
|
||||||
|
"getLibraryRoot_nonFatalAuthenticationError_receivesPlaybackException";
|
||||||
|
|
||||||
private MediaBrowserServiceCompatConstants() {}
|
private MediaBrowserServiceCompatConstants() {}
|
||||||
}
|
}
|
||||||
|
@ -395,13 +395,13 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
|
|||||||
assertGetChildrenAuthenticationRequired(PARENT_ID_AUTH_EXPIRED_ERROR_DEPRECATED);
|
assertGetChildrenAuthenticationRequired(PARENT_ID_AUTH_EXPIRED_ERROR_DEPRECATED);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void assertGetChildrenAuthenticationRequired(String testParentId) throws Exception {
|
public void assertGetChildrenAuthenticationRequired(String authExpiredParentId) throws Exception {
|
||||||
connectAndWait(/* rootHints= */ Bundle.EMPTY);
|
connectAndWait(/* rootHints= */ Bundle.EMPTY);
|
||||||
CountDownLatch errorLatch = new CountDownLatch(1);
|
CountDownLatch errorLatch = new CountDownLatch(1);
|
||||||
AtomicReference<String> parentIdRefOnError = new AtomicReference<>();
|
AtomicReference<String> parentIdRefOnError = new AtomicReference<>();
|
||||||
|
|
||||||
browserCompat.subscribe(
|
browserCompat.subscribe(
|
||||||
testParentId,
|
authExpiredParentId,
|
||||||
new SubscriptionCallback() {
|
new SubscriptionCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onError(String parentId) {
|
public void onError(String parentId) {
|
||||||
@ -411,7 +411,7 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
|
|||||||
});
|
});
|
||||||
|
|
||||||
assertThat(errorLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
assertThat(errorLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||||
assertThat(parentIdRefOnError.get()).isEqualTo(testParentId);
|
assertThat(parentIdRefOnError.get()).isEqualTo(authExpiredParentId);
|
||||||
assertThat(firstPlaybackStateCompatReported.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
assertThat(firstPlaybackStateCompatReported.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||||
assertThat(lastReportedPlaybackStateCompat.getState())
|
assertThat(lastReportedPlaybackStateCompat.getState())
|
||||||
.isEqualTo(PlaybackStateCompat.STATE_ERROR);
|
.isEqualTo(PlaybackStateCompat.STATE_ERROR);
|
||||||
|
@ -259,8 +259,8 @@ public class MediaBrowserListenerTest extends MediaControllerListenerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getChildren_errorLibraryResult() throws Exception {
|
public void getChildren_authExpiredErrorLibraryResult() throws Exception {
|
||||||
String parentId = MediaBrowserConstants.PARENT_ID_ERROR;
|
String parentId = MediaBrowserConstants.PARENT_ID_AUTH_EXPIRED_ERROR;
|
||||||
MediaBrowser browser = createBrowser();
|
MediaBrowser browser = createBrowser();
|
||||||
|
|
||||||
LibraryResult<ImmutableList<MediaItem>> result =
|
LibraryResult<ImmutableList<MediaItem>> result =
|
||||||
@ -272,7 +272,11 @@ public class MediaBrowserListenerTest extends MediaControllerListenerTest {
|
|||||||
assertThat(result.resultCode).isLessThan(0);
|
assertThat(result.resultCode).isLessThan(0);
|
||||||
assertThat(result.sessionError.code).isLessThan(0);
|
assertThat(result.sessionError.code).isLessThan(0);
|
||||||
assertThat(result.sessionError.message).isEqualTo("error message");
|
assertThat(result.sessionError.message).isEqualTo("error message");
|
||||||
assertThat(result.sessionError.extras.getString("key")).isEqualTo("value");
|
assertThat(
|
||||||
|
result.sessionError.extras.getString(
|
||||||
|
MediaConstants.EXTRAS_KEY_ERROR_RESOLUTION_ACTION_LABEL_COMPAT))
|
||||||
|
.isEqualTo(
|
||||||
|
MediaBrowserConstants.PARENT_ID_AUTH_EXPIRED_ERROR_KEY_ERROR_RESOLUTION_ACTION_LABEL);
|
||||||
assertThat(result.value).isNull();
|
assertThat(result.value).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +27,8 @@ import static androidx.media3.test.session.common.MediaBrowserConstants.ROOT_ID;
|
|||||||
import static androidx.media3.test.session.common.MediaBrowserConstants.ROOT_ID_SUPPORTS_BROWSABLE_CHILDREN_ONLY;
|
import static androidx.media3.test.session.common.MediaBrowserConstants.ROOT_ID_SUPPORTS_BROWSABLE_CHILDREN_ONLY;
|
||||||
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_CONNECT_REJECTED;
|
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_CONNECT_REJECTED;
|
||||||
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_GET_CHILDREN;
|
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_GET_CHILDREN;
|
||||||
|
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_GET_CHILDREN_FATAL_AUTHENTICATION_ERROR;
|
||||||
|
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_GET_CHILDREN_NON_FATAL_AUTHENTICATION_ERROR;
|
||||||
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_GET_CHILDREN_WITH_NULL_LIST;
|
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_GET_CHILDREN_WITH_NULL_LIST;
|
||||||
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_GET_LIBRARY_ROOT;
|
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_GET_LIBRARY_ROOT;
|
||||||
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_ON_CHILDREN_CHANGED_SUBSCRIBE_AND_UNSUBSCRIBE;
|
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_ON_CHILDREN_CHANGED_SUBSCRIBE_AND_UNSUBSCRIBE;
|
||||||
@ -40,12 +42,16 @@ import android.os.Bundle;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media.MediaBrowserServiceCompat;
|
import androidx.media.MediaBrowserServiceCompat;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
|
import androidx.media3.common.PlaybackException;
|
||||||
|
import androidx.media3.common.Player;
|
||||||
import androidx.media3.session.MediaLibraryService.LibraryParams;
|
import androidx.media3.session.MediaLibraryService.LibraryParams;
|
||||||
import androidx.media3.test.session.common.HandlerThreadTestRule;
|
import androidx.media3.test.session.common.HandlerThreadTestRule;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import androidx.test.filters.LargeTest;
|
import androidx.test.filters.LargeTest;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
@ -264,4 +270,70 @@ public class MediaBrowserListenerWithMediaBrowserServiceCompatTest {
|
|||||||
assertThat(status).isEqualTo(expectedStatus);
|
assertThat(status).isEqualTo(expectedStatus);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getChildren_fatalAuthenticationErrorOfLegacySessionApp_receivesPlaybackException()
|
||||||
|
throws Exception {
|
||||||
|
remoteService.setProxyForTest(TEST_GET_CHILDREN_FATAL_AUTHENTICATION_ERROR);
|
||||||
|
MediaBrowser browser = createBrowser(/* listener= */ null);
|
||||||
|
List<PlaybackException> playbackExceptions = new ArrayList<>();
|
||||||
|
CountDownLatch playbackErrorLatch = new CountDownLatch(1);
|
||||||
|
browser.addListener(
|
||||||
|
new Player.Listener() {
|
||||||
|
@Override
|
||||||
|
public void onPlayerError(PlaybackException error) {
|
||||||
|
playbackExceptions.add(error);
|
||||||
|
playbackErrorLatch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
LibraryResult<ImmutableList<MediaItem>> libraryResult =
|
||||||
|
threadTestRule
|
||||||
|
.getHandler()
|
||||||
|
.postAndSync(
|
||||||
|
() ->
|
||||||
|
browser.getChildren(
|
||||||
|
PARENT_ID, /* page= */ 4, /* pageSize= */ 10, /* params= */ null))
|
||||||
|
.get(TIMEOUT_MS, MILLISECONDS);
|
||||||
|
|
||||||
|
assertThat(playbackErrorLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||||
|
assertThat(libraryResult.sessionError.code).isEqualTo(SessionError.ERROR_UNKNOWN);
|
||||||
|
assertThat(playbackExceptions).hasSize(1);
|
||||||
|
assertThat(playbackExceptions.get(0).errorCode)
|
||||||
|
.isEqualTo(PlaybackException.ERROR_CODE_AUTHENTICATION_EXPIRED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getChildren_nonFatalAuthenticationErrorOfLegacySessionApp_receivesSessionError()
|
||||||
|
throws Exception {
|
||||||
|
remoteService.setProxyForTest(TEST_GET_CHILDREN_NON_FATAL_AUTHENTICATION_ERROR);
|
||||||
|
List<SessionError> sessionErrors = new ArrayList<>();
|
||||||
|
CountDownLatch sessionErrorLatch = new CountDownLatch(1);
|
||||||
|
MediaBrowser browser =
|
||||||
|
createBrowser(
|
||||||
|
/* listener= */ new MediaBrowser.Listener() {
|
||||||
|
@Override
|
||||||
|
public void onError(MediaController controller, SessionError sessionError) {
|
||||||
|
sessionErrors.add(sessionError);
|
||||||
|
sessionErrorLatch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
LibraryResult<ImmutableList<MediaItem>> libraryResult =
|
||||||
|
threadTestRule
|
||||||
|
.getHandler()
|
||||||
|
.postAndSync(
|
||||||
|
() ->
|
||||||
|
browser.getChildren(
|
||||||
|
PARENT_ID, /* page= */ 4, /* pageSize= */ 10, /* params= */ null))
|
||||||
|
.get(TIMEOUT_MS, MILLISECONDS);
|
||||||
|
|
||||||
|
assertThat(sessionErrorLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||||
|
assertThat(libraryResult.sessionError).isNull();
|
||||||
|
assertThat(libraryResult.value).hasSize(1);
|
||||||
|
assertThat(libraryResult.value.get(0).mediaId).isEqualTo("mediaId");
|
||||||
|
assertThat(sessionErrors).hasSize(1);
|
||||||
|
assertThat(sessionErrors.get(0).code)
|
||||||
|
.isEqualTo(PlaybackException.ERROR_CODE_AUTHENTICATION_EXPIRED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -184,7 +184,7 @@ public class MediaControllerCompatCallbackWithMediaSessionTest {
|
|||||||
new PlaybackException(
|
new PlaybackException(
|
||||||
/* message= */ "testremote",
|
/* message= */ "testremote",
|
||||||
/* cause= */ null,
|
/* cause= */ null,
|
||||||
PlaybackException.ERROR_CODE_REMOTE_ERROR);
|
PlaybackException.ERROR_CODE_AUTHENTICATION_EXPIRED);
|
||||||
Bundle playerConfig =
|
Bundle playerConfig =
|
||||||
new RemoteMediaSession.MockPlayerConfigBuilder().setPlayerError(testPlayerError).build();
|
new RemoteMediaSession.MockPlayerConfigBuilder().setPlayerError(testPlayerError).build();
|
||||||
session.setPlayer(playerConfig);
|
session.setPlayer(playerConfig);
|
||||||
@ -201,7 +201,11 @@ public class MediaControllerCompatCallbackWithMediaSessionTest {
|
|||||||
handler);
|
handler);
|
||||||
|
|
||||||
assertThat(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
|
assertThat(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
|
||||||
assertPlaybackStateCompatErrorEquals(controller.getPlaybackState(), testPlayerError);
|
PlaybackStateCompat state = controller.getPlaybackState();
|
||||||
|
assertThat(state.getState()).isEqualTo(PlaybackStateCompat.STATE_ERROR);
|
||||||
|
assertThat(state.getErrorCode())
|
||||||
|
.isEqualTo(PlaybackStateCompat.ERROR_CODE_AUTHENTICATION_EXPIRED);
|
||||||
|
assertThat(state.getErrorMessage().toString()).isEqualTo(testPlayerError.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -210,7 +214,7 @@ public class MediaControllerCompatCallbackWithMediaSessionTest {
|
|||||||
new PlaybackException(
|
new PlaybackException(
|
||||||
/* message= */ "player error",
|
/* message= */ "player error",
|
||||||
/* cause= */ null,
|
/* cause= */ null,
|
||||||
PlaybackException.ERROR_CODE_UNSPECIFIED);
|
PlaybackException.ERROR_CODE_AUTHENTICATION_EXPIRED);
|
||||||
|
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
AtomicReference<PlaybackStateCompat> playbackStateCompatRef = new AtomicReference<>();
|
AtomicReference<PlaybackStateCompat> playbackStateCompatRef = new AtomicReference<>();
|
||||||
@ -227,7 +231,133 @@ public class MediaControllerCompatCallbackWithMediaSessionTest {
|
|||||||
session.getMockPlayer().notifyPlayerError(testPlayerError);
|
session.getMockPlayer().notifyPlayerError(testPlayerError);
|
||||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||||
PlaybackStateCompat state = playbackStateCompatRef.get();
|
PlaybackStateCompat state = playbackStateCompatRef.get();
|
||||||
assertPlaybackStateCompatErrorEquals(state, testPlayerError);
|
assertThat(state.getState()).isEqualTo(PlaybackStateCompat.STATE_ERROR);
|
||||||
|
assertThat(state.getErrorCode())
|
||||||
|
.isEqualTo(PlaybackStateCompat.ERROR_CODE_AUTHENTICATION_EXPIRED);
|
||||||
|
assertThat(state.getErrorMessage().toString()).isEqualTo(testPlayerError.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sendError_toAllControllers_onPlaybackStateChangedToErrorStateAndWithCorrectErrorData()
|
||||||
|
throws Exception {
|
||||||
|
CountDownLatch latch = new CountDownLatch(2);
|
||||||
|
List<PlaybackStateCompat> playbackStates = new ArrayList<>();
|
||||||
|
MediaControllerCompat.Callback callback =
|
||||||
|
new MediaControllerCompat.Callback() {
|
||||||
|
@Override
|
||||||
|
public void onPlaybackStateChanged(PlaybackStateCompat state) {
|
||||||
|
playbackStates.add(state);
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
controllerCompat.registerCallback(callback, handler);
|
||||||
|
Bundle sessionExtras = new Bundle();
|
||||||
|
sessionExtras.putString("initialKey", "initialValue");
|
||||||
|
session.setSessionExtras(sessionExtras);
|
||||||
|
PlaybackStateCompat initialPlaybackStateCompat = controllerCompat.getPlaybackState();
|
||||||
|
Bundle errorBundle = new Bundle();
|
||||||
|
errorBundle.putInt("errorKey", 99);
|
||||||
|
|
||||||
|
session.sendError(
|
||||||
|
/* controllerKey= */ null,
|
||||||
|
new SessionError(
|
||||||
|
/* code= */ SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED,
|
||||||
|
/* message= */ ApplicationProvider.getApplicationContext()
|
||||||
|
.getString(R.string.authentication_required),
|
||||||
|
errorBundle));
|
||||||
|
|
||||||
|
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||||
|
assertThat(playbackStates).hasSize(3);
|
||||||
|
|
||||||
|
// Skip the playback state from the first setSessionExtras() call.
|
||||||
|
PlaybackStateCompat errorPlaybackStateCompat = playbackStates.get(1);
|
||||||
|
assertThat(errorPlaybackStateCompat.getState()).isNotEqualTo(PlaybackStateCompat.STATE_ERROR);
|
||||||
|
assertThat(errorPlaybackStateCompat.getState())
|
||||||
|
.isEqualTo(initialPlaybackStateCompat.getState());
|
||||||
|
assertThat(errorPlaybackStateCompat.getErrorCode())
|
||||||
|
.isEqualTo(PlaybackStateCompat.ERROR_CODE_AUTHENTICATION_EXPIRED);
|
||||||
|
assertThat(errorPlaybackStateCompat.getErrorMessage().toString())
|
||||||
|
.isEqualTo(context.getString(R.string.authentication_required));
|
||||||
|
assertThat(errorPlaybackStateCompat.getExtras()).hasSize(3);
|
||||||
|
assertThat(errorPlaybackStateCompat.getExtras()).integer("errorKey").isEqualTo(99);
|
||||||
|
assertThat(errorPlaybackStateCompat.getExtras()).string("initialKey").isEqualTo("initialValue");
|
||||||
|
assertThat(errorPlaybackStateCompat.getExtras()).containsKey("EXO_SPEED");
|
||||||
|
|
||||||
|
PlaybackStateCompat resolvedPlaybackStateCompat = playbackStates.get(2);
|
||||||
|
assertThat(resolvedPlaybackStateCompat.getState())
|
||||||
|
.isEqualTo(initialPlaybackStateCompat.getState());
|
||||||
|
assertThat(resolvedPlaybackStateCompat.getErrorCode())
|
||||||
|
.isEqualTo(initialPlaybackStateCompat.getErrorCode());
|
||||||
|
assertThat(resolvedPlaybackStateCompat.getErrorMessage()).isNull();
|
||||||
|
assertThat(resolvedPlaybackStateCompat.getActions())
|
||||||
|
.isEqualTo(initialPlaybackStateCompat.getActions());
|
||||||
|
assertThat(
|
||||||
|
TestUtils.equals(
|
||||||
|
resolvedPlaybackStateCompat.getExtras(), initialPlaybackStateCompat.getExtras()))
|
||||||
|
.isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void
|
||||||
|
sendError_toMediaNotificationController_onPlaybackStateChangedToErrorStateAndWithCorrectErrorData()
|
||||||
|
throws Exception {
|
||||||
|
CountDownLatch latch = new CountDownLatch(2);
|
||||||
|
List<PlaybackStateCompat> playbackStates = new ArrayList<>();
|
||||||
|
MediaControllerCompat.Callback callback =
|
||||||
|
new MediaControllerCompat.Callback() {
|
||||||
|
@Override
|
||||||
|
public void onPlaybackStateChanged(PlaybackStateCompat state) {
|
||||||
|
playbackStates.add(state);
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
controllerCompat.registerCallback(callback, handler);
|
||||||
|
Bundle sessionExtras = new Bundle();
|
||||||
|
sessionExtras.putString("initialKey", "initialValue");
|
||||||
|
session.setSessionExtras(/* controllerKey= */ NOTIFICATION_CONTROLLER_KEY, sessionExtras);
|
||||||
|
PlaybackStateCompat initialPlaybackStateCompat = controllerCompat.getPlaybackState();
|
||||||
|
Bundle errorBundle = new Bundle();
|
||||||
|
errorBundle.putInt("errorKey", 99);
|
||||||
|
|
||||||
|
session.sendError(
|
||||||
|
/* controllerKey= */ NOTIFICATION_CONTROLLER_KEY,
|
||||||
|
new SessionError(
|
||||||
|
SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED,
|
||||||
|
/* message= */ ApplicationProvider.getApplicationContext()
|
||||||
|
.getString(R.string.authentication_required),
|
||||||
|
errorBundle));
|
||||||
|
|
||||||
|
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||||
|
assertThat(playbackStates).hasSize(3);
|
||||||
|
|
||||||
|
// Skip the playback state from the first setSessionExtras() call.
|
||||||
|
PlaybackStateCompat errorPlaybackStateCompat = playbackStates.get(1);
|
||||||
|
assertThat(errorPlaybackStateCompat.getState())
|
||||||
|
.isEqualTo(initialPlaybackStateCompat.getState());
|
||||||
|
assertThat(errorPlaybackStateCompat.getState()).isNotEqualTo(PlaybackStateCompat.STATE_ERROR);
|
||||||
|
assertThat(errorPlaybackStateCompat.getErrorCode())
|
||||||
|
.isEqualTo(PlaybackStateCompat.ERROR_CODE_AUTHENTICATION_EXPIRED);
|
||||||
|
assertThat(errorPlaybackStateCompat.getErrorMessage().toString())
|
||||||
|
.isEqualTo(context.getString(R.string.authentication_required));
|
||||||
|
assertThat(errorPlaybackStateCompat.getActions())
|
||||||
|
.isEqualTo(initialPlaybackStateCompat.getActions());
|
||||||
|
assertThat(errorPlaybackStateCompat.getExtras()).hasSize(3);
|
||||||
|
assertThat(errorPlaybackStateCompat.getExtras()).string("initialKey").isEqualTo("initialValue");
|
||||||
|
assertThat(errorPlaybackStateCompat.getExtras()).integer("errorKey").isEqualTo(99);
|
||||||
|
assertThat(errorPlaybackStateCompat.getExtras()).containsKey("EXO_SPEED");
|
||||||
|
|
||||||
|
PlaybackStateCompat resolvedPlaybackStateCompat = playbackStates.get(2);
|
||||||
|
assertThat(resolvedPlaybackStateCompat.getState())
|
||||||
|
.isEqualTo(initialPlaybackStateCompat.getState());
|
||||||
|
assertThat(resolvedPlaybackStateCompat.getErrorCode())
|
||||||
|
.isEqualTo(initialPlaybackStateCompat.getErrorCode());
|
||||||
|
assertThat(resolvedPlaybackStateCompat.getErrorMessage()).isNull();
|
||||||
|
assertThat(resolvedPlaybackStateCompat.getActions())
|
||||||
|
.isEqualTo(initialPlaybackStateCompat.getActions());
|
||||||
|
assertThat(
|
||||||
|
TestUtils.equals(
|
||||||
|
resolvedPlaybackStateCompat.getExtras(), initialPlaybackStateCompat.getExtras()))
|
||||||
|
.isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -1096,118 +1226,6 @@ public class MediaControllerCompatCallbackWithMediaSessionTest {
|
|||||||
assertThat(playbackStateExtrasFromController.get()).string("key-0").isEqualTo("value-0");
|
assertThat(playbackStateExtrasFromController.get()).string("key-0").isEqualTo("value-0");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void sendError_toAllControllers_onPlaybackStateChangedToErrorStateAndWithCorrectErrorData()
|
|
||||||
throws Exception {
|
|
||||||
CountDownLatch latch = new CountDownLatch(3);
|
|
||||||
List<PlaybackStateCompat> playbackStates = new ArrayList<>();
|
|
||||||
MediaControllerCompat.Callback callback =
|
|
||||||
new MediaControllerCompat.Callback() {
|
|
||||||
@Override
|
|
||||||
public void onPlaybackStateChanged(PlaybackStateCompat state) {
|
|
||||||
playbackStates.add(state);
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
controllerCompat.registerCallback(callback, handler);
|
|
||||||
Bundle sessionExtras = new Bundle();
|
|
||||||
sessionExtras.putString("initialKey", "initialValue");
|
|
||||||
session.setSessionExtras(sessionExtras);
|
|
||||||
PlaybackStateCompat initialPlaybackStateCompat = controllerCompat.getPlaybackState();
|
|
||||||
Bundle errorBundle = new Bundle();
|
|
||||||
errorBundle.putInt("errorKey", 99);
|
|
||||||
|
|
||||||
session.sendError(
|
|
||||||
/* controllerKey= */ null,
|
|
||||||
/* errorCode= */ 1,
|
|
||||||
R.string.authentication_required,
|
|
||||||
errorBundle);
|
|
||||||
|
|
||||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
|
||||||
assertThat(playbackStates).hasSize(3);
|
|
||||||
|
|
||||||
// Skip the playback state from the first setSessionExtras() call.
|
|
||||||
PlaybackStateCompat errorPlaybackStateCompat = playbackStates.get(1);
|
|
||||||
assertThat(errorPlaybackStateCompat.getState()).isEqualTo(PlaybackStateCompat.STATE_ERROR);
|
|
||||||
assertThat(errorPlaybackStateCompat.getErrorCode()).isEqualTo(1);
|
|
||||||
assertThat(errorPlaybackStateCompat.getErrorMessage().toString())
|
|
||||||
.isEqualTo(context.getString(R.string.authentication_required));
|
|
||||||
assertThat(errorPlaybackStateCompat.getExtras()).hasSize(2);
|
|
||||||
assertThat(errorPlaybackStateCompat.getExtras()).integer("errorKey").isEqualTo(99);
|
|
||||||
assertThat(errorPlaybackStateCompat.getExtras()).string("initialKey").isEqualTo("initialValue");
|
|
||||||
|
|
||||||
PlaybackStateCompat resolvedPlaybackStateCompat = playbackStates.get(2);
|
|
||||||
assertThat(resolvedPlaybackStateCompat.getState())
|
|
||||||
.isEqualTo(initialPlaybackStateCompat.getState());
|
|
||||||
assertThat(resolvedPlaybackStateCompat.getErrorCode())
|
|
||||||
.isEqualTo(initialPlaybackStateCompat.getErrorCode());
|
|
||||||
assertThat(resolvedPlaybackStateCompat.getErrorMessage()).isNull();
|
|
||||||
assertThat(resolvedPlaybackStateCompat.getActions())
|
|
||||||
.isEqualTo(initialPlaybackStateCompat.getActions());
|
|
||||||
assertThat(resolvedPlaybackStateCompat.getExtras())
|
|
||||||
.hasSize(initialPlaybackStateCompat.getExtras().size());
|
|
||||||
assertThat(resolvedPlaybackStateCompat.getExtras())
|
|
||||||
.string("initialKey")
|
|
||||||
.isEqualTo(initialPlaybackStateCompat.getExtras().getString("initialKey"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void
|
|
||||||
sendError_toMediaNotificationControllers_onPlaybackStateChangedToErrorStateAndWithCorrectErrorData()
|
|
||||||
throws Exception {
|
|
||||||
CountDownLatch latch = new CountDownLatch(3);
|
|
||||||
List<PlaybackStateCompat> playbackStates = new ArrayList<>();
|
|
||||||
MediaControllerCompat.Callback callback =
|
|
||||||
new MediaControllerCompat.Callback() {
|
|
||||||
@Override
|
|
||||||
public void onPlaybackStateChanged(PlaybackStateCompat state) {
|
|
||||||
playbackStates.add(state);
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
controllerCompat.registerCallback(callback, handler);
|
|
||||||
Bundle sessionExtras = new Bundle();
|
|
||||||
sessionExtras.putString("initialKey", "initialValue");
|
|
||||||
session.setSessionExtras(/* controllerKey= */ NOTIFICATION_CONTROLLER_KEY, sessionExtras);
|
|
||||||
PlaybackStateCompat initialPlaybackStateCompat = controllerCompat.getPlaybackState();
|
|
||||||
|
|
||||||
Bundle errorBundle = new Bundle();
|
|
||||||
errorBundle.putInt("errorKey", 99);
|
|
||||||
session.sendError(
|
|
||||||
/* controllerKey= */ NOTIFICATION_CONTROLLER_KEY,
|
|
||||||
/* errorCode= */ 1,
|
|
||||||
R.string.authentication_required,
|
|
||||||
errorBundle);
|
|
||||||
|
|
||||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
|
||||||
assertThat(playbackStates).hasSize(3);
|
|
||||||
|
|
||||||
// Skip the playback state from the first setSessionExtras() call.
|
|
||||||
PlaybackStateCompat errorPlaybackStateCompat = playbackStates.get(1);
|
|
||||||
assertThat(errorPlaybackStateCompat.getState()).isEqualTo(PlaybackStateCompat.STATE_ERROR);
|
|
||||||
assertThat(errorPlaybackStateCompat.getErrorCode()).isEqualTo(1);
|
|
||||||
assertThat(errorPlaybackStateCompat.getErrorMessage().toString())
|
|
||||||
.isEqualTo(context.getString(R.string.authentication_required));
|
|
||||||
assertThat(errorPlaybackStateCompat.getActions()).isEqualTo(0);
|
|
||||||
assertThat(errorPlaybackStateCompat.getExtras()).hasSize(2);
|
|
||||||
assertThat(errorPlaybackStateCompat.getExtras()).string("initialKey").isEqualTo("initialValue");
|
|
||||||
assertThat(errorPlaybackStateCompat.getExtras()).integer("errorKey").isEqualTo(99);
|
|
||||||
|
|
||||||
PlaybackStateCompat resolvedPlaybackStateCompat = playbackStates.get(2);
|
|
||||||
assertThat(resolvedPlaybackStateCompat.getState())
|
|
||||||
.isEqualTo(initialPlaybackStateCompat.getState());
|
|
||||||
assertThat(resolvedPlaybackStateCompat.getErrorCode())
|
|
||||||
.isEqualTo(initialPlaybackStateCompat.getErrorCode());
|
|
||||||
assertThat(resolvedPlaybackStateCompat.getErrorMessage()).isNull();
|
|
||||||
assertThat(resolvedPlaybackStateCompat.getActions())
|
|
||||||
.isEqualTo(initialPlaybackStateCompat.getActions());
|
|
||||||
assertThat(resolvedPlaybackStateCompat.getExtras())
|
|
||||||
.hasSize(initialPlaybackStateCompat.getExtras().size());
|
|
||||||
assertThat(resolvedPlaybackStateCompat.getExtras())
|
|
||||||
.string("initialKey")
|
|
||||||
.isEqualTo(initialPlaybackStateCompat.getExtras().getString("initialKey"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void setSessionActivity_changedWhenReceivedWithSetter() throws Exception {
|
public void setSessionActivity_changedWhenReceivedWithSetter() throws Exception {
|
||||||
Intent intent = new Intent(context, SurfaceActivity.class);
|
Intent intent = new Intent(context, SurfaceActivity.class);
|
||||||
@ -1609,11 +1627,4 @@ public class MediaControllerCompatCallbackWithMediaSessionTest {
|
|||||||
assertThat(targetVolumeNotified.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
assertThat(targetVolumeNotified.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||||
assertThat(controllerCompat.getPlaybackInfo().getCurrentVolume()).isEqualTo(targetVolume);
|
assertThat(controllerCompat.getPlaybackInfo().getCurrentVolume()).isEqualTo(targetVolume);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void assertPlaybackStateCompatErrorEquals(
|
|
||||||
PlaybackStateCompat state, PlaybackException playerError) {
|
|
||||||
assertThat(state.getState()).isEqualTo(PlaybackStateCompat.STATE_ERROR);
|
|
||||||
assertThat(state.getErrorCode()).isEqualTo(PlaybackStateCompat.ERROR_CODE_UNKNOWN_ERROR);
|
|
||||||
assertThat(state.getErrorMessage().toString()).isEqualTo(playerError.getMessage());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -2638,9 +2638,7 @@ public class MediaControllerListenerTest {
|
|||||||
public void onError_sendErrorToAllAndToSingleController_correctErrorDataReported()
|
public void onError_sendErrorToAllAndToSingleController_correctErrorDataReported()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
CountDownLatch errorLatch = new CountDownLatch(/* count= */ 3);
|
CountDownLatch errorLatch = new CountDownLatch(/* count= */ 3);
|
||||||
List<Integer> errorCodes1 = new ArrayList<>();
|
List<SessionError> sessionErrors1 = new ArrayList<>();
|
||||||
List<String> errorMessages1 = new ArrayList<>();
|
|
||||||
List<Bundle> errorExtras1 = new ArrayList<>();
|
|
||||||
Bundle connectionHints1 = new Bundle();
|
Bundle connectionHints1 = new Bundle();
|
||||||
connectionHints1.putString(KEY_CONTROLLER, "ctrl-1");
|
connectionHints1.putString(KEY_CONTROLLER, "ctrl-1");
|
||||||
controllerTestRule.createController(
|
controllerTestRule.createController(
|
||||||
@ -2648,17 +2646,12 @@ public class MediaControllerListenerTest {
|
|||||||
connectionHints1,
|
connectionHints1,
|
||||||
new MediaController.Listener() {
|
new MediaController.Listener() {
|
||||||
@Override
|
@Override
|
||||||
public void onError(
|
public void onError(MediaController controller, SessionError sessionError) {
|
||||||
MediaController controller, int errorCode, String errorMessage, Bundle extras) {
|
sessionErrors1.add(sessionError);
|
||||||
errorCodes1.add(errorCode);
|
|
||||||
errorMessages1.add(errorMessage);
|
|
||||||
errorExtras1.add(extras);
|
|
||||||
errorLatch.countDown();
|
errorLatch.countDown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
List<Integer> errorCodes2 = new ArrayList<>();
|
List<SessionError> sessionErrors2 = new ArrayList<>();
|
||||||
List<String> errorMessages2 = new ArrayList<>();
|
|
||||||
List<Bundle> errorExtras2 = new ArrayList<>();
|
|
||||||
Bundle connectionHints2 = new Bundle();
|
Bundle connectionHints2 = new Bundle();
|
||||||
connectionHints2.putString(KEY_CONTROLLER, "ctrl-2");
|
connectionHints2.putString(KEY_CONTROLLER, "ctrl-2");
|
||||||
controllerTestRule.createController(
|
controllerTestRule.createController(
|
||||||
@ -2666,44 +2659,38 @@ public class MediaControllerListenerTest {
|
|||||||
connectionHints2,
|
connectionHints2,
|
||||||
new MediaController.Listener() {
|
new MediaController.Listener() {
|
||||||
@Override
|
@Override
|
||||||
public void onError(
|
public void onError(MediaController controller, SessionError sessionError) {
|
||||||
MediaController controller, int errorCode, String errorMessage, Bundle extras) {
|
sessionErrors2.add(sessionError);
|
||||||
errorCodes2.add(errorCode);
|
|
||||||
errorMessages2.add(errorMessage);
|
|
||||||
errorExtras2.add(extras);
|
|
||||||
errorLatch.countDown();
|
errorLatch.countDown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Bundle errorExtra1 = new Bundle();
|
Bundle errorExtra1 = new Bundle();
|
||||||
errorExtra1.putInt("intKey", 1);
|
errorExtra1.putInt("intKey", 1);
|
||||||
|
SessionError error1 =
|
||||||
|
new SessionError(
|
||||||
|
/* code= */ SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED,
|
||||||
|
ApplicationProvider.getApplicationContext().getString(R.string.authentication_required),
|
||||||
|
errorExtra1);
|
||||||
Bundle errorExtra2 = new Bundle();
|
Bundle errorExtra2 = new Bundle();
|
||||||
errorExtra2.putInt("intKey", 2);
|
errorExtra2.putInt("intKey", 2);
|
||||||
|
SessionError error2 =
|
||||||
|
new SessionError(
|
||||||
|
SessionError.ERROR_SESSION_CONCURRENT_STREAM_LIMIT,
|
||||||
|
ApplicationProvider.getApplicationContext()
|
||||||
|
.getString(R.string.default_notification_channel_name),
|
||||||
|
errorExtra2);
|
||||||
|
|
||||||
remoteSession.sendError(
|
remoteSession.sendError(/* controllerKey= */ null, error1);
|
||||||
/* controllerKey= */ null,
|
remoteSession.sendError(/* controllerKey= */ "ctrl-2", error2);
|
||||||
/* errorCode= */ 1,
|
|
||||||
R.string.authentication_required,
|
|
||||||
errorExtra1);
|
|
||||||
remoteSession.sendError(
|
|
||||||
/* controllerKey= */ "ctrl-2",
|
|
||||||
/* errorCode= */ 2,
|
|
||||||
R.string.default_notification_channel_name,
|
|
||||||
errorExtra2);
|
|
||||||
|
|
||||||
assertThat(errorLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
assertThat(errorLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||||
assertThat(errorCodes1).containsExactly(1);
|
assertThat(sessionErrors1).containsExactly(error1);
|
||||||
assertThat(errorMessages1).containsExactly(context.getString(R.string.authentication_required));
|
assertThat(sessionErrors1.get(0).extras.getInt("intKey")).isEqualTo(1);
|
||||||
assertThat(TestUtils.equals(errorExtras1.get(0), errorExtra1)).isTrue();
|
assertThat(sessionErrors2).containsExactly(error1, error2).inOrder();
|
||||||
assertThat(errorExtras1).hasSize(1);
|
assertThat(sessionErrors2.get(0).extras.getInt("intKey")).isEqualTo(1);
|
||||||
assertThat(errorCodes2).containsExactly(1, 2).inOrder();
|
assertThat(sessionErrors2.get(0).extras.size()).isEqualTo(1);
|
||||||
assertThat(errorMessages2)
|
assertThat(sessionErrors2.get(1).extras.getInt("intKey")).isEqualTo(2);
|
||||||
.containsExactly(
|
assertThat(sessionErrors2.get(1).extras.size()).isEqualTo(1);
|
||||||
context.getString(R.string.authentication_required),
|
|
||||||
context.getString(R.string.default_notification_channel_name))
|
|
||||||
.inOrder();
|
|
||||||
assertThat(TestUtils.equals(errorExtras2.get(0), errorExtra1)).isTrue();
|
|
||||||
assertThat(TestUtils.equals(errorExtras2.get(1), errorExtra2)).isTrue();
|
|
||||||
assertThat(errorExtras2).hasSize(2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -190,6 +190,82 @@ public class MediaControllerListenerWithMediaSessionCompatTest {
|
|||||||
.inOrder();
|
.inOrder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setPlaybackState_fatalError_callsOnPlayerErrorWithCodeMessageAndExtras()
|
||||||
|
throws Exception {
|
||||||
|
MediaController controller =
|
||||||
|
controllerTestRule.createController(session.getSessionToken(), /* listener= */ null);
|
||||||
|
CountDownLatch fatalErrorLatch = new CountDownLatch(/* count= */ 1);
|
||||||
|
List<PlaybackException> fatalErrorExceptions = new ArrayList<>();
|
||||||
|
Bundle fatalErrorExtras = new Bundle();
|
||||||
|
fatalErrorExtras.putString("key-2", "value-2");
|
||||||
|
controller.addListener(
|
||||||
|
new Player.Listener() {
|
||||||
|
@Override
|
||||||
|
public void onPlayerError(PlaybackException error) {
|
||||||
|
fatalErrorExceptions.add(error);
|
||||||
|
fatalErrorLatch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
session.setPlaybackState(
|
||||||
|
new PlaybackStateCompat.Builder()
|
||||||
|
.setState(
|
||||||
|
PlaybackStateCompat.STATE_ERROR, /* position= */ 0L, /* playbackSpeed= */ 1.0f)
|
||||||
|
.setErrorMessage(
|
||||||
|
PlaybackStateCompat.ERROR_CODE_AUTHENTICATION_EXPIRED,
|
||||||
|
ApplicationProvider.getApplicationContext()
|
||||||
|
.getString(R.string.authentication_required))
|
||||||
|
.setExtras(fatalErrorExtras)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
assertThat(fatalErrorLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||||
|
assertThat(fatalErrorExceptions).hasSize(1);
|
||||||
|
assertThat(fatalErrorExceptions.get(0))
|
||||||
|
.hasMessageThat()
|
||||||
|
.isEqualTo(context.getString(R.string.authentication_required));
|
||||||
|
assertThat(fatalErrorExceptions.get(0).errorCode)
|
||||||
|
.isEqualTo(PlaybackException.ERROR_CODE_AUTHENTICATION_EXPIRED);
|
||||||
|
assertThat(TestUtils.equals(fatalErrorExceptions.get(0).extras, fatalErrorExtras)).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setPlaybackState_nonFatalError_callsOnErrorWithCodeMessageAndExtras()
|
||||||
|
throws Exception {
|
||||||
|
CountDownLatch nonFatalErrorLatch = new CountDownLatch(/* count= */ 1);
|
||||||
|
List<SessionError> sessionErrors = new ArrayList<>();
|
||||||
|
Bundle nonFatalErrorExtra = new Bundle();
|
||||||
|
nonFatalErrorExtra.putString("key-1", "value-1");
|
||||||
|
controllerTestRule.createController(
|
||||||
|
session.getSessionToken(),
|
||||||
|
new MediaController.Listener() {
|
||||||
|
@Override
|
||||||
|
public void onError(MediaController controller, SessionError sessionError) {
|
||||||
|
sessionErrors.add(sessionError);
|
||||||
|
nonFatalErrorLatch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
session.setPlaybackState(
|
||||||
|
new PlaybackStateCompat.Builder()
|
||||||
|
.setState(
|
||||||
|
PlaybackStateCompat.STATE_PLAYING,
|
||||||
|
PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN,
|
||||||
|
/* playbackSpeed= */ .0f)
|
||||||
|
.setErrorMessage(
|
||||||
|
PlaybackStateCompat.ERROR_CODE_APP_ERROR,
|
||||||
|
ApplicationProvider.getApplicationContext()
|
||||||
|
.getString(R.string.default_notification_channel_name))
|
||||||
|
.setExtras(nonFatalErrorExtra)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
assertThat(nonFatalErrorLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||||
|
assertThat(sessionErrors).hasSize(1);
|
||||||
|
assertThat(sessionErrors.get(0).message)
|
||||||
|
.isEqualTo(context.getString(R.string.default_notification_channel_name));
|
||||||
|
assertThat(TestUtils.equals(sessionErrors.get(0).extras, nonFatalErrorExtra)).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void setSessionExtras_onExtrasChangedCalled() throws Exception {
|
public void setSessionExtras_onExtrasChangedCalled() throws Exception {
|
||||||
Bundle sessionExtras = new Bundle();
|
Bundle sessionExtras = new Bundle();
|
||||||
@ -230,72 +306,6 @@ public class MediaControllerListenerWithMediaSessionCompatTest {
|
|||||||
.isTrue();
|
.isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void sendError_fatalAndNonFatalErrorCodes_callsCorrectCallbackWithErrorData()
|
|
||||||
throws Exception {
|
|
||||||
CountDownLatch nonFatalErrorLatch = new CountDownLatch(/* count= */ 1);
|
|
||||||
List<Integer> nonFatalErrorCodes = new ArrayList<>();
|
|
||||||
List<String> nonFatalErrorMessages = new ArrayList<>();
|
|
||||||
List<Bundle> nonFatalErrorExtras = new ArrayList<>();
|
|
||||||
Bundle nonFatalErrorExtra = new Bundle();
|
|
||||||
nonFatalErrorExtra.putString("key-1", "value-1");
|
|
||||||
MediaController controller =
|
|
||||||
controllerTestRule.createController(
|
|
||||||
session.getSessionToken(),
|
|
||||||
new MediaController.Listener() {
|
|
||||||
@Override
|
|
||||||
public void onError(
|
|
||||||
MediaController controller,
|
|
||||||
int errorCode,
|
|
||||||
String errorMessage,
|
|
||||||
Bundle errorExtra) {
|
|
||||||
nonFatalErrorCodes.add(errorCode);
|
|
||||||
nonFatalErrorMessages.add(errorMessage);
|
|
||||||
nonFatalErrorExtras.add(errorExtra);
|
|
||||||
nonFatalErrorLatch.countDown();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
CountDownLatch fatalErrorLatch = new CountDownLatch(/* count= */ 1);
|
|
||||||
List<PlaybackException> fatalErrorExceptions = new ArrayList<>();
|
|
||||||
Bundle fatalErrorExtra = new Bundle();
|
|
||||||
fatalErrorExtra.putString("key-2", "value-2");
|
|
||||||
controller.addListener(
|
|
||||||
new Player.Listener() {
|
|
||||||
@Override
|
|
||||||
public void onPlayerError(PlaybackException error) {
|
|
||||||
fatalErrorExceptions.add(error);
|
|
||||||
fatalErrorLatch.countDown();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Send fatal errors code.
|
|
||||||
session.sendError(
|
|
||||||
/* errorCode= */ PlaybackStateCompat.ERROR_CODE_AUTHENTICATION_EXPIRED,
|
|
||||||
R.string.authentication_required,
|
|
||||||
fatalErrorExtra);
|
|
||||||
assertThat(fatalErrorLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
|
||||||
// Send non-fatal error code.
|
|
||||||
session.sendError(
|
|
||||||
/* errorCode= */ PlaybackStateCompat.ERROR_CODE_APP_ERROR,
|
|
||||||
R.string.default_notification_channel_name,
|
|
||||||
nonFatalErrorExtra);
|
|
||||||
|
|
||||||
assertThat(nonFatalErrorLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
|
||||||
assertThat(nonFatalErrorCodes).containsExactly(PlaybackStateCompat.ERROR_CODE_APP_ERROR);
|
|
||||||
assertThat(nonFatalErrorMessages)
|
|
||||||
.containsExactly(context.getString(R.string.default_notification_channel_name));
|
|
||||||
assertThat(TestUtils.equals(nonFatalErrorExtras.get(0), nonFatalErrorExtra)).isTrue();
|
|
||||||
assertThat(fatalErrorExceptions).hasSize(1);
|
|
||||||
assertThat(fatalErrorExceptions.get(0))
|
|
||||||
.hasMessageThat()
|
|
||||||
.isEqualTo(
|
|
||||||
context.getString(R.string.authentication_required)
|
|
||||||
+ ", code="
|
|
||||||
+ PlaybackStateCompat.ERROR_CODE_AUTHENTICATION_EXPIRED);
|
|
||||||
assertThat(fatalErrorExceptions.get(0).errorCode)
|
|
||||||
.isEqualTo(PlaybackException.ERROR_CODE_REMOTE_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void onPlaylistMetadataChanged() throws Exception {
|
public void onPlaylistMetadataChanged() throws Exception {
|
||||||
MediaController controller = controllerTestRule.createController(session.getSessionToken());
|
MediaController controller = controllerTestRule.createController(session.getSessionToken());
|
||||||
|
@ -24,7 +24,6 @@ import static android.support.v4.media.MediaMetadataCompat.METADATA_KEY_DISPLAY_
|
|||||||
import static android.support.v4.media.MediaMetadataCompat.METADATA_KEY_DURATION;
|
import static android.support.v4.media.MediaMetadataCompat.METADATA_KEY_DURATION;
|
||||||
import static android.support.v4.media.MediaMetadataCompat.METADATA_KEY_MEDIA_ID;
|
import static android.support.v4.media.MediaMetadataCompat.METADATA_KEY_MEDIA_ID;
|
||||||
import static android.support.v4.media.MediaMetadataCompat.METADATA_KEY_MEDIA_URI;
|
import static android.support.v4.media.MediaMetadataCompat.METADATA_KEY_MEDIA_URI;
|
||||||
import static androidx.media3.common.PlaybackException.ERROR_CODE_REMOTE_ERROR;
|
|
||||||
import static androidx.media3.common.Player.MEDIA_ITEM_TRANSITION_REASON_AUTO;
|
import static androidx.media3.common.Player.MEDIA_ITEM_TRANSITION_REASON_AUTO;
|
||||||
import static androidx.media3.common.Player.STATE_BUFFERING;
|
import static androidx.media3.common.Player.STATE_BUFFERING;
|
||||||
import static androidx.media3.common.Player.STATE_READY;
|
import static androidx.media3.common.Player.STATE_READY;
|
||||||
@ -64,7 +63,6 @@ import androidx.media.VolumeProviderCompat;
|
|||||||
import androidx.media3.common.DeviceInfo;
|
import androidx.media3.common.DeviceInfo;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
import androidx.media3.common.MediaMetadata;
|
import androidx.media3.common.MediaMetadata;
|
||||||
import androidx.media3.common.PlaybackException;
|
|
||||||
import androidx.media3.common.PlaybackParameters;
|
import androidx.media3.common.PlaybackParameters;
|
||||||
import androidx.media3.common.Player;
|
import androidx.media3.common.Player;
|
||||||
import androidx.media3.common.Player.DiscontinuityReason;
|
import androidx.media3.common.Player.DiscontinuityReason;
|
||||||
@ -1505,39 +1503,6 @@ public class MediaControllerWithMediaSessionCompatTest {
|
|||||||
assertThat(playbackParametersFromGetterRef.get().speed).isEqualTo(testSpeed);
|
assertThat(playbackParametersFromGetterRef.get().speed).isEqualTo(testSpeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setPlaybackState_withError_notifiesOnPlayerErrorChanged() throws Exception {
|
|
||||||
String testErrorMessage = "testErrorMessage";
|
|
||||||
int testErrorCode = PlaybackStateCompat.ERROR_CODE_UNKNOWN_ERROR; // 0
|
|
||||||
String testConvertedErrorMessage = "testErrorMessage, code=0";
|
|
||||||
MediaController controller = controllerTestRule.createController(session.getSessionToken());
|
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
|
||||||
AtomicReference<PlaybackException> errorFromParamRef = new AtomicReference<>();
|
|
||||||
AtomicReference<PlaybackException> errorFromGetterRef = new AtomicReference<>();
|
|
||||||
Player.Listener listener =
|
|
||||||
new Player.Listener() {
|
|
||||||
@Override
|
|
||||||
public void onPlayerErrorChanged(@Nullable PlaybackException error) {
|
|
||||||
errorFromParamRef.set(error);
|
|
||||||
errorFromGetterRef.set(controller.getPlayerError());
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
controller.addListener(listener);
|
|
||||||
|
|
||||||
session.setPlaybackState(
|
|
||||||
new PlaybackStateCompat.Builder()
|
|
||||||
.setState(PlaybackStateCompat.STATE_ERROR, /* position= */ 0, /* playbackSpeed= */ 1.0f)
|
|
||||||
.setErrorMessage(testErrorCode, testErrorMessage)
|
|
||||||
.build());
|
|
||||||
|
|
||||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
|
||||||
assertThat(errorFromParamRef.get().errorCode).isEqualTo(ERROR_CODE_REMOTE_ERROR);
|
|
||||||
assertThat(errorFromParamRef.get().getMessage()).isEqualTo(testConvertedErrorMessage);
|
|
||||||
assertThat(errorFromGetterRef.get().errorCode).isEqualTo(ERROR_CODE_REMOTE_ERROR);
|
|
||||||
assertThat(errorFromGetterRef.get().getMessage()).isEqualTo(testConvertedErrorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void setPlaybackState_withActions_updatesAndNotifiesAvailableCommands() throws Exception {
|
public void setPlaybackState_withActions_updatesAndNotifiesAvailableCommands() throws Exception {
|
||||||
MediaController controller = controllerTestRule.createController(session.getSessionToken());
|
MediaController controller = controllerTestRule.createController(session.getSessionToken());
|
||||||
|
@ -15,20 +15,19 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.session;
|
package androidx.media3.session;
|
||||||
|
|
||||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
|
||||||
import static androidx.media3.test.session.common.CommonConstants.ACTION_MEDIA_SESSION_COMPAT;
|
import static androidx.media3.test.session.common.CommonConstants.ACTION_MEDIA_SESSION_COMPAT;
|
||||||
import static androidx.media3.test.session.common.CommonConstants.KEY_METADATA_COMPAT;
|
import static androidx.media3.test.session.common.CommonConstants.KEY_METADATA_COMPAT;
|
||||||
import static androidx.media3.test.session.common.CommonConstants.KEY_PLAYBACK_STATE_COMPAT;
|
import static androidx.media3.test.session.common.CommonConstants.KEY_PLAYBACK_STATE_COMPAT;
|
||||||
import static androidx.media3.test.session.common.CommonConstants.KEY_QUEUE;
|
import static androidx.media3.test.session.common.CommonConstants.KEY_QUEUE;
|
||||||
import static androidx.media3.test.session.common.CommonConstants.KEY_SESSION_COMPAT_TOKEN;
|
import static androidx.media3.test.session.common.CommonConstants.KEY_SESSION_COMPAT_TOKEN;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.SystemClock;
|
|
||||||
import android.support.v4.media.MediaMetadataCompat;
|
import android.support.v4.media.MediaMetadataCompat;
|
||||||
import android.support.v4.media.session.MediaSessionCompat;
|
import android.support.v4.media.session.MediaSessionCompat;
|
||||||
import android.support.v4.media.session.MediaSessionCompat.QueueItem;
|
import android.support.v4.media.session.MediaSessionCompat.QueueItem;
|
||||||
@ -120,6 +119,7 @@ public class MediaSessionCompatProviderService extends Service {
|
|||||||
session.setPlaybackToLocal(stream);
|
session.setPlaybackToLocal(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("RestrictedApi")
|
||||||
@Override
|
@Override
|
||||||
public void setPlaybackToRemote(
|
public void setPlaybackToRemote(
|
||||||
String sessionTag,
|
String sessionTag,
|
||||||
@ -234,24 +234,6 @@ public class MediaSessionCompatProviderService extends Service {
|
|||||||
session.setExtras(extras);
|
session.setExtras(extras);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendError(
|
|
||||||
String sessionTag, int errorCode, int errorMessageResId, Bundle errorExtras) {
|
|
||||||
MediaSessionCompat session = sessionMap.get(sessionTag);
|
|
||||||
session.setPlaybackState(
|
|
||||||
new PlaybackStateCompat.Builder()
|
|
||||||
.setState(
|
|
||||||
PlaybackStateCompat.STATE_ERROR,
|
|
||||||
/* position= */ PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN,
|
|
||||||
/* playbackSpeed= */ 0,
|
|
||||||
/* updateTime= */ SystemClock.elapsedRealtime())
|
|
||||||
.setActions(0)
|
|
||||||
.setBufferedPosition(0)
|
|
||||||
.setErrorMessage(errorCode, checkNotNull(getString(errorMessageResId)))
|
|
||||||
.setExtras(checkNotNull(errorExtras))
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getCallbackMethodCount(String sessionTag, String methodName) {
|
public int getCallbackMethodCount(String sessionTag, String methodName) {
|
||||||
CallCountingCallback callCountingCallback = callbackMap.get(sessionTag);
|
CallCountingCallback callCountingCallback = callbackMap.get(sessionTag);
|
||||||
|
@ -583,19 +583,15 @@ public class MediaSessionProviderService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendError(
|
public void sendError(String sessionId, String controllerKey, Bundle sessionError)
|
||||||
String sessionId,
|
|
||||||
String controllerKey,
|
|
||||||
int errorCode,
|
|
||||||
int errorMessageResId,
|
|
||||||
Bundle errorExtras)
|
|
||||||
throws RemoteException {
|
throws RemoteException {
|
||||||
runOnHandler(
|
runOnHandler(
|
||||||
() -> {
|
() -> {
|
||||||
MediaSession mediaSession = checkNotNull(sessionMap.get(sessionId));
|
MediaSession mediaSession = checkNotNull(sessionMap.get(sessionId));
|
||||||
|
SessionError error = SessionError.fromBundle(sessionError);
|
||||||
if (TextUtils.isEmpty(controllerKey)) {
|
if (TextUtils.isEmpty(controllerKey)) {
|
||||||
// Broadcast to all connected Media3 controller.
|
// Broadcast to all connected Media3 controller.
|
||||||
mediaSession.sendError(errorCode, errorMessageResId, errorExtras);
|
mediaSession.sendError(error);
|
||||||
} else {
|
} else {
|
||||||
// Send to controller with the given controller key in connection hints.
|
// Send to controller with the given controller key in connection hints.
|
||||||
for (ControllerInfo controllerInfo : mediaSession.getConnectedControllers()) {
|
for (ControllerInfo controllerInfo : mediaSession.getConnectedControllers()) {
|
||||||
@ -603,7 +599,7 @@ public class MediaSessionProviderService extends Service {
|
|||||||
.getConnectionHints()
|
.getConnectionHints()
|
||||||
.getString(KEY_CONTROLLER, /* defaultValue= */ "")
|
.getString(KEY_CONTROLLER, /* defaultValue= */ "")
|
||||||
.equals(controllerKey)) {
|
.equals(controllerKey)) {
|
||||||
mediaSession.sendError(controllerInfo, errorCode, errorMessageResId, errorExtras);
|
mediaSession.sendError(controllerInfo, error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,11 +25,14 @@ import static androidx.media3.test.session.common.MediaBrowserConstants.ROOT_ID;
|
|||||||
import static androidx.media3.test.session.common.MediaBrowserConstants.ROOT_ID_SUPPORTS_BROWSABLE_CHILDREN_ONLY;
|
import static androidx.media3.test.session.common.MediaBrowserConstants.ROOT_ID_SUPPORTS_BROWSABLE_CHILDREN_ONLY;
|
||||||
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_CONNECT_REJECTED;
|
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_CONNECT_REJECTED;
|
||||||
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_GET_CHILDREN;
|
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_GET_CHILDREN;
|
||||||
|
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_GET_CHILDREN_FATAL_AUTHENTICATION_ERROR;
|
||||||
|
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_GET_CHILDREN_NON_FATAL_AUTHENTICATION_ERROR;
|
||||||
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_GET_CHILDREN_WITH_NULL_LIST;
|
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_GET_CHILDREN_WITH_NULL_LIST;
|
||||||
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_GET_LIBRARY_ROOT;
|
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_GET_LIBRARY_ROOT;
|
||||||
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_ON_CHILDREN_CHANGED_SUBSCRIBE_AND_UNSUBSCRIBE;
|
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_ON_CHILDREN_CHANGED_SUBSCRIBE_AND_UNSUBSCRIBE;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
@ -38,6 +41,7 @@ import android.support.v4.media.MediaBrowserCompat.MediaItem;
|
|||||||
import android.support.v4.media.MediaDescriptionCompat;
|
import android.support.v4.media.MediaDescriptionCompat;
|
||||||
import android.support.v4.media.session.MediaSessionCompat;
|
import android.support.v4.media.session.MediaSessionCompat;
|
||||||
import android.support.v4.media.session.MediaSessionCompat.Callback;
|
import android.support.v4.media.session.MediaSessionCompat.Callback;
|
||||||
|
import android.support.v4.media.session.PlaybackStateCompat;
|
||||||
import androidx.annotation.GuardedBy;
|
import androidx.annotation.GuardedBy;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media.MediaBrowserServiceCompat;
|
import androidx.media.MediaBrowserServiceCompat;
|
||||||
@ -81,7 +85,7 @@ public class MockMediaBrowserServiceCompat extends MediaBrowserServiceCompat {
|
|||||||
sessionCompat.setActive(true);
|
sessionCompat.setActive(true);
|
||||||
setSessionToken(sessionCompat.getSessionToken());
|
setSessionToken(sessionCompat.getSessionToken());
|
||||||
|
|
||||||
testBinder = new RemoteMediaBrowserServiceCompatStub();
|
testBinder = new RemoteMediaBrowserServiceCompatStub(sessionCompat);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -236,6 +240,13 @@ public class MockMediaBrowserServiceCompat extends MediaBrowserServiceCompat {
|
|||||||
|
|
||||||
private static class RemoteMediaBrowserServiceCompatStub
|
private static class RemoteMediaBrowserServiceCompatStub
|
||||||
extends IRemoteMediaBrowserServiceCompat.Stub {
|
extends IRemoteMediaBrowserServiceCompat.Stub {
|
||||||
|
|
||||||
|
private final MediaSessionCompat session;
|
||||||
|
|
||||||
|
public RemoteMediaBrowserServiceCompatStub(MediaSessionCompat sessionCompat) {
|
||||||
|
session = sessionCompat;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setProxyForTest(String testName) throws RemoteException {
|
public void setProxyForTest(String testName) throws RemoteException {
|
||||||
switch (testName) {
|
switch (testName) {
|
||||||
@ -254,6 +265,12 @@ public class MockMediaBrowserServiceCompat extends MediaBrowserServiceCompat {
|
|||||||
case TEST_GET_CHILDREN_WITH_NULL_LIST:
|
case TEST_GET_CHILDREN_WITH_NULL_LIST:
|
||||||
setProxyForTestOnChildrenChanged_withNullChildrenListInLegacyService_convertedToSessionError();
|
setProxyForTestOnChildrenChanged_withNullChildrenListInLegacyService_convertedToSessionError();
|
||||||
break;
|
break;
|
||||||
|
case TEST_GET_CHILDREN_FATAL_AUTHENTICATION_ERROR:
|
||||||
|
getChildren_authenticationError_receivesPlaybackException(session, /* isFatal= */ true);
|
||||||
|
break;
|
||||||
|
case TEST_GET_CHILDREN_NON_FATAL_AUTHENTICATION_ERROR:
|
||||||
|
getChildren_authenticationError_receivesPlaybackException(session, /* isFatal= */ false);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unknown testName: " + testName);
|
throw new IllegalArgumentException("Unknown testName: " + testName);
|
||||||
}
|
}
|
||||||
@ -319,6 +336,44 @@ public class MockMediaBrowserServiceCompat extends MediaBrowserServiceCompat {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void getChildren_authenticationError_receivesPlaybackException(
|
||||||
|
MediaSessionCompat session, boolean isFatal) {
|
||||||
|
setMediaBrowserServiceProxy(
|
||||||
|
new MockMediaBrowserServiceCompat.Proxy() {
|
||||||
|
@Override
|
||||||
|
public void onLoadChildren(String parentId, Result<List<MediaItem>> result) {
|
||||||
|
onLoadChildren(parentId, result, new Bundle());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoadChildren(
|
||||||
|
String parentId, Result<List<MediaItem>> result, Bundle bundle) {
|
||||||
|
result.sendResult(
|
||||||
|
isFatal
|
||||||
|
? null
|
||||||
|
: ImmutableList.of(
|
||||||
|
new MediaItem(
|
||||||
|
new MediaDescriptionCompat.Builder()
|
||||||
|
.setMediaUri(Uri.parse("http://www.example.com"))
|
||||||
|
.setMediaId("mediaId")
|
||||||
|
.build(),
|
||||||
|
MediaItem.FLAG_PLAYABLE)));
|
||||||
|
session.setPlaybackState(
|
||||||
|
new PlaybackStateCompat.Builder()
|
||||||
|
.setState(
|
||||||
|
isFatal
|
||||||
|
? PlaybackStateCompat.STATE_ERROR
|
||||||
|
: PlaybackStateCompat.STATE_PLAYING,
|
||||||
|
isFatal ? PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN : 123L,
|
||||||
|
/* playbackSpeed= */ isFatal ? 0f : 1.0f)
|
||||||
|
.setErrorMessage(
|
||||||
|
PlaybackStateCompat.ERROR_CODE_AUTHENTICATION_EXPIRED,
|
||||||
|
"authentication expired")
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void setProxyForTestGetLibraryRoot_correctExtraKeyAndValue() {
|
private void setProxyForTestGetLibraryRoot_correctExtraKeyAndValue() {
|
||||||
setMediaBrowserServiceProxy(
|
setMediaBrowserServiceProxy(
|
||||||
new MockMediaBrowserServiceCompat.Proxy() {
|
new MockMediaBrowserServiceCompat.Proxy() {
|
||||||
|
@ -40,6 +40,7 @@ import static androidx.media3.test.session.common.MediaBrowserConstants.PARENT_I
|
|||||||
import static androidx.media3.test.session.common.MediaBrowserConstants.PARENT_ID_AUTH_EXPIRED_ERROR;
|
import static androidx.media3.test.session.common.MediaBrowserConstants.PARENT_ID_AUTH_EXPIRED_ERROR;
|
||||||
import static androidx.media3.test.session.common.MediaBrowserConstants.PARENT_ID_AUTH_EXPIRED_ERROR_DEPRECATED;
|
import static androidx.media3.test.session.common.MediaBrowserConstants.PARENT_ID_AUTH_EXPIRED_ERROR_DEPRECATED;
|
||||||
import static androidx.media3.test.session.common.MediaBrowserConstants.PARENT_ID_AUTH_EXPIRED_ERROR_KEY_ERROR_RESOLUTION_ACTION_LABEL;
|
import static androidx.media3.test.session.common.MediaBrowserConstants.PARENT_ID_AUTH_EXPIRED_ERROR_KEY_ERROR_RESOLUTION_ACTION_LABEL;
|
||||||
|
import static androidx.media3.test.session.common.MediaBrowserConstants.PARENT_ID_AUTH_EXPIRED_ERROR_NON_FATAL;
|
||||||
import static androidx.media3.test.session.common.MediaBrowserConstants.PARENT_ID_ERROR;
|
import static androidx.media3.test.session.common.MediaBrowserConstants.PARENT_ID_ERROR;
|
||||||
import static androidx.media3.test.session.common.MediaBrowserConstants.PARENT_ID_LONG_LIST;
|
import static androidx.media3.test.session.common.MediaBrowserConstants.PARENT_ID_LONG_LIST;
|
||||||
import static androidx.media3.test.session.common.MediaBrowserConstants.PARENT_ID_NO_CHILDREN;
|
import static androidx.media3.test.session.common.MediaBrowserConstants.PARENT_ID_NO_CHILDREN;
|
||||||
@ -365,13 +366,32 @@ public class MockMediaLibraryService extends MediaLibraryService {
|
|||||||
PARENT_ID_AUTH_EXPIRED_ERROR_KEY_ERROR_RESOLUTION_ACTION_LABEL);
|
PARENT_ID_AUTH_EXPIRED_ERROR_KEY_ERROR_RESOLUTION_ACTION_LABEL);
|
||||||
return Objects.equals(parentId, PARENT_ID_AUTH_EXPIRED_ERROR)
|
return Objects.equals(parentId, PARENT_ID_AUTH_EXPIRED_ERROR)
|
||||||
? Futures.immediateFuture(
|
? Futures.immediateFuture(
|
||||||
|
// error with SessionError
|
||||||
LibraryResult.ofError(
|
LibraryResult.ofError(
|
||||||
new SessionError(ERROR_SESSION_AUTHENTICATION_EXPIRED, "error message", bundle),
|
new SessionError(ERROR_SESSION_AUTHENTICATION_EXPIRED, "error message", bundle),
|
||||||
new LibraryParams.Builder().build()))
|
new LibraryParams.Builder().build()))
|
||||||
: Futures.immediateFuture(
|
: Futures.immediateFuture(
|
||||||
|
// deprecated error before SessionError was introduced
|
||||||
LibraryResult.ofError(
|
LibraryResult.ofError(
|
||||||
ERROR_SESSION_AUTHENTICATION_EXPIRED,
|
ERROR_SESSION_AUTHENTICATION_EXPIRED,
|
||||||
new LibraryParams.Builder().setExtras(bundle).build()));
|
new LibraryParams.Builder().setExtras(bundle).build()));
|
||||||
|
} else if (Objects.equals(parentId, PARENT_ID_AUTH_EXPIRED_ERROR_NON_FATAL)) {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
Intent signInIntent = new Intent("action");
|
||||||
|
int flags = Util.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0;
|
||||||
|
bundle.putParcelable(
|
||||||
|
EXTRAS_KEY_ERROR_RESOLUTION_ACTION_INTENT_COMPAT,
|
||||||
|
PendingIntent.getActivity(
|
||||||
|
getApplicationContext(), /* requestCode= */ 0, signInIntent, flags));
|
||||||
|
bundle.putString(
|
||||||
|
EXTRAS_KEY_ERROR_RESOLUTION_ACTION_LABEL_COMPAT,
|
||||||
|
PARENT_ID_AUTH_EXPIRED_ERROR_KEY_ERROR_RESOLUTION_ACTION_LABEL);
|
||||||
|
session.sendError(
|
||||||
|
new SessionError(ERROR_SESSION_AUTHENTICATION_EXPIRED, "error message", bundle));
|
||||||
|
return Futures.immediateFuture(
|
||||||
|
LibraryResult.ofError(
|
||||||
|
new SessionError(ERROR_SESSION_AUTHENTICATION_EXPIRED, "error message"),
|
||||||
|
new LibraryParams.Builder().build()));
|
||||||
}
|
}
|
||||||
return Futures.immediateFuture(LibraryResult.ofError(ERROR_BAD_VALUE, params));
|
return Futures.immediateFuture(LibraryResult.ofError(ERROR_BAD_VALUE, params));
|
||||||
}
|
}
|
||||||
|
@ -213,11 +213,9 @@ public class RemoteMediaSession {
|
|||||||
binder.setSessionActivity(sessionId, sessionActivity);
|
binder.setSessionActivity(sessionId, sessionActivity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendError(
|
public void sendError(@Nullable String controllerKey, SessionError sessionError)
|
||||||
@Nullable String controllerKey, int errorCode, int errorMessageResId, Bundle errorExtras)
|
|
||||||
throws RemoteException {
|
throws RemoteException {
|
||||||
binder.sendError(
|
binder.sendError(sessionId, nullToEmpty(controllerKey), sessionError.toBundle());
|
||||||
sessionId, nullToEmpty(controllerKey), errorCode, errorMessageResId, errorExtras);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -180,11 +180,6 @@ public class RemoteMediaSessionCompat {
|
|||||||
binder.setSessionExtras(sessionTag, extras);
|
binder.setSessionExtras(sessionTag, extras);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendError(int errorCode, int errorMessageResInt, Bundle errorExtras)
|
|
||||||
throws RemoteException {
|
|
||||||
binder.sendError(sessionTag, errorCode, errorMessageResInt, errorExtras);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Non-public methods
|
// Non-public methods
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -68,9 +68,8 @@ public final class TestMediaBrowserListener implements MediaBrowser.Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(
|
public void onError(MediaController controller, SessionError sessionError) {
|
||||||
MediaController controller, int errorCode, String errorMessage, Bundle errorExtras) {
|
delegate.onError(controller, sessionError);
|
||||||
delegate.onError(controller, errorCode, errorMessage, errorExtras);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
Loading…
x
Reference in New Issue
Block a user