diff options
| author | 2018-01-27 08:05:20 +0000 | |
|---|---|---|
| committer | 2018-01-27 08:05:20 +0000 | |
| commit | 2a28269a79692d9505deaa714dfa84f9cc37f5ea (patch) | |
| tree | 93250fec2944e1dbd5819641e7c808861b89e29d | |
| parent | 98c8ad92e5eb831602fd6e396530b75eeda7fcc6 (diff) | |
| parent | 6a13e1b483434d1e5d7d529bcc989f3f3fe574bc (diff) | |
Merge changes from topics "sessionplayer2", "mediasession2_audio_api", "session2_playbacklistener", "session2_timing"
* changes:
MediaSession2: Initial commit of SessionPlayer2
MediaSession2 API set for audio focus handling
MediaSession2: Add/remove playback listeners
MediaSession2: Fix timing issue
19 files changed, 531 insertions, 364 deletions
diff --git a/media/java/android/media/IMediaSession2.aidl b/media/java/android/media/IMediaSession2.aidl index f79711a389a7..3783e5fd94e4 100644 --- a/media/java/android/media/IMediaSession2.aidl +++ b/media/java/android/media/IMediaSession2.aidl @@ -24,9 +24,7 @@ import android.os.Bundle; * * @hide */ -// TODO(jaewan): Make this oneway interface. -// Malicious app can fake session binder and holds commands from controller. -interface IMediaSession2 { +oneway interface IMediaSession2 { // TODO(jaewan): add onCommand() to send private command // TODO(jaewan): Due to the nature of oneway calls, APIs can be called in out of order // Add id for individual calls to address this. @@ -35,37 +33,18 @@ interface IMediaSession2 { // not to expose other methods to the controller whose connection wasn't accepted. // But this would be enough for now because it's the same as existing // MediaBrowser and MediaBrowserService. - oneway void connect(String callingPackage, IMediaSession2Callback callback); - oneway void release(IMediaSession2Callback caller); + void connect(String callingPackage, IMediaSession2Callback callback); + void release(IMediaSession2Callback caller); ////////////////////////////////////////////////////////////////////////////////////////////// // send command ////////////////////////////////////////////////////////////////////////////////////////////// - oneway void sendCommand(IMediaSession2Callback caller, in Bundle command, in Bundle args); - oneway void sendTransportControlCommand(IMediaSession2Callback caller, + void sendCommand(IMediaSession2Callback caller, in Bundle command, in Bundle args); + void sendTransportControlCommand(IMediaSession2Callback caller, int commandCode, long arg); - Bundle getPlaybackState(); - ////////////////////////////////////////////////////////////////////////////////////////////// // Get library service specific ////////////////////////////////////////////////////////////////////////////////////////////// - oneway void getBrowserRoot(IMediaSession2Callback callback, in Bundle rootHints); - - ////////////////////////////////////////////////////////////////////////////////////////////// - // Callbacks -- remove them - ////////////////////////////////////////////////////////////////////////////////////////////// - /** - * @param callbackBinder binder to be used to notify changes. - * @param callbackFlag one of {@link MediaController2#FLAG_CALLBACK_PLAYBACK} or - * {@link MediaController2#FLAG_CALLBACK_SESSION_ACTIVENESS} - * @param requestCode If >= 0, this code will be called back by the callback after the callback - * is registered. - */ - // TODO(jaewan): Due to the nature of the binder, calls can be called out of order. - // Need a way to ensure calling of unregisterCallback unregisters later - // registerCallback. - oneway void registerCallback(IMediaSession2Callback callbackBinder, - int callbackFlag, int requestCode); - oneway void unregisterCallback(IMediaSession2Callback callbackBinder, int callbackFlag); + void getBrowserRoot(IMediaSession2Callback callback, in Bundle rootHints); } diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java index e2d5c5de7c42..a8b2411a9013 100644 --- a/media/java/android/media/MediaController2.java +++ b/media/java/android/media/MediaController2.java @@ -133,9 +133,9 @@ public class MediaController2 implements AutoCloseable { @NonNull List<MediaItem2> list, @NonNull PlaylistParams param) { } /** - * Called when the playback state is changed. + * Called when the playback state is changed, or connection success. * - * @param state + * @param state latest playback state */ public void onPlaybackStateChanged(@NonNull PlaybackState2 state) { } @@ -254,12 +254,13 @@ public class MediaController2 implements AutoCloseable { @NonNull @CallbackExecutor Executor executor, @NonNull ControllerCallback callback) { super(); + mProvider = createProvider(context, token, executor, callback); // This also connects to the token. // Explicit connect() isn't added on purpose because retrying connect() is impossible with // session whose session binder is only valid while it's active. // prevent a controller from reusable after the // session is released and recreated. - mProvider = createProvider(context, token, executor, callback); + mProvider.initialize(); } MediaController2Provider createProvider(@NonNull Context context, @@ -545,11 +546,15 @@ public class MediaController2 implements AutoCloseable { } /** - * Get the latest {@link PlaybackState2} from the session. + * Get the lastly cached {@link PlaybackState2} from + * {@link ControllerCallback#onPlaybackStateChanged(PlaybackState2)}. + * <p> + * It may return {@code null} before the first callback or session has sent {@code null} + * playback state. * - * @return a playback state + * @return a playback state. Can be {@code null} */ - public PlaybackState2 getPlaybackState() { + public @Nullable PlaybackState2 getPlaybackState() { return mProvider.getPlaybackState_impl(); } diff --git a/media/java/android/media/MediaItem2.java b/media/java/android/media/MediaItem2.java index 6a96faa6905e..f9711aa3b13d 100644 --- a/media/java/android/media/MediaItem2.java +++ b/media/java/android/media/MediaItem2.java @@ -36,9 +36,9 @@ import java.lang.annotation.RetentionPolicy; * @hide */ public class MediaItem2 { - // TODO(jaewan): Keep DataSourceDesc. private final int mFlags; private MediaMetadata2 mMetadata; + private DataSourceDesc mDataSourceDesc; /** @hide */ @Retention(RetentionPolicy.SOURCE) @@ -64,8 +64,10 @@ public class MediaItem2 { * @param metadata metadata with the media id. * @param flags The flags for this item. */ - public MediaItem2(@NonNull MediaMetadata2 metadata, @Flags int flags) { + public MediaItem2(@Nullable MediaMetadata2 metadata, + @Nullable DataSourceDesc data, @Flags int flags) { mFlags = flags; + mDataSourceDesc = data; setMetadata(metadata); } @@ -139,4 +141,8 @@ public class MediaItem2 { public @Nullable String getMediaId() { return mMetadata.getMediaId(); } + + public @Nullable DataSourceDesc getDataSourceDesc() { + return mDataSourceDesc; + } } diff --git a/media/java/android/media/MediaPlayerInterface.java b/media/java/android/media/MediaPlayerInterface.java index 51bcd9b8a56a..f09fa909fab7 100644 --- a/media/java/android/media/MediaPlayerInterface.java +++ b/media/java/android/media/MediaPlayerInterface.java @@ -16,6 +16,8 @@ package android.media; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.media.MediaSession2.PlaylistParams; import java.util.List; @@ -48,9 +50,23 @@ public interface MediaPlayerInterface { void rewind(); PlaybackState2 getPlaybackState(); + + /** + * Sets the {@link AudioAttributes} to be used during the playback of the media. + * + * @param attributes non-null <code>AudioAttributes</code>. + */ + void setAudioAttributes(@NonNull AudioAttributes attributes); + + /** + * Returns AudioAttributes that media player has. + */ + @Nullable AudioAttributes getAudioAttributes(); - void setPlaylist(List<MediaItem2> item, PlaylistParams param); + void setPlaylist(List<MediaItem2> list, PlaylistParams param); + List<MediaItem2> getPlaylist(); + void setCurrentPlaylistItem(int index); void setPlaylistParams(PlaylistParams params); PlaylistParams getPlaylistParams(); diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java index 365c356e5e1d..245ba3b5f65d 100644 --- a/media/java/android/media/MediaSession2.java +++ b/media/java/android/media/MediaSession2.java @@ -83,6 +83,7 @@ public class MediaSession2 implements AutoCloseable { // Note: Do not define IntDef because subclass can add more command code on top of these. // TODO(jaewan): Shouldn't we pull out? + // TODO(jaewan): Should we also protect getPlaybackState()? public static final int COMMAND_CODE_CUSTOM = 0; public static final int COMMAND_CODE_PLAYBACK_START = 1; public static final int COMMAND_CODE_PLAYBACK_PAUSE = 2; @@ -199,7 +200,8 @@ public class MediaSession2 implements AutoCloseable { @Override public int hashCode() { final int prime = 31; - return ((mCustomCommand != null) ? mCustomCommand.hashCode() : 0) * prime + mCommandCode; + return ((mCustomCommand != null) + ? mCustomCommand.hashCode() : 0) * prime + mCommandCode; } } @@ -999,14 +1001,13 @@ public class MediaSession2 implements AutoCloseable { * framework had to add heuristics to figure out if an app is * @hide */ - MediaSession2(Context context, MediaPlayerInterface player, String id, VolumeProvider volumeProvider, int ratingType, PendingIntent sessionActivity, Executor callbackExecutor, SessionCallback callback) { super(); mProvider = createProvider(context, player, id, volumeProvider, ratingType, sessionActivity, - callbackExecutor, callback - ); + callbackExecutor, callback); + mProvider.initialize(); } MediaSession2Provider createProvider(Context context, MediaPlayerInterface player, String id, @@ -1079,15 +1080,6 @@ public class MediaSession2 implements AutoCloseable { } /** - * Sets the {@link AudioAttributes} to be used during the playback of the video. - * - * @param attributes non-null <code>AudioAttributes</code>. - */ - public void setAudioAttributes(@NonNull AudioAttributes attributes) { - mProvider.setAudioAttributes_impl(attributes); - } - - /** * Sets which type of audio focus will be requested during the playback, or configures playback * to not request audio focus. Valid values for focus requests are * {@link AudioManager#AUDIOFOCUS_GAIN}, {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT}, @@ -1270,6 +1262,10 @@ public class MediaSession2 implements AutoCloseable { mProvider.setPlaylist_impl(playlist, param); } + public List<MediaItem2> getPlaylist() { + return mProvider.getPlaylist_impl(); + } + /** * Sets the {@link PlaylistParams} for the current play list. Repeat/shuffle mode and metadata * for the list can be set by calling this method. @@ -1288,4 +1284,38 @@ public class MediaSession2 implements AutoCloseable { public PlaylistParams getPlaylistParams() { return mProvider.getPlaylistParams_impl(); } + + /* + * Add a {@link PlaybackListener} to listen changes in the underlying + * {@link MediaPlayerInterface}. Listener will be called immediately to tell the current value. + * <p> + * Added listeners will be also called when the underlying player is changed. + * + * @param executor the call listener + * @param listener the listener that will be run + * @throws IllegalArgumentException when either the listener or handler is {@code null}. + */ + public void addPlaybackListener(@NonNull @CallbackExecutor Executor executor, + @NonNull PlaybackListener listener) { + mProvider.addPlaybackListener_impl(executor, listener); + } + + /** + * Remove previously added {@link PlaybackListener}. + * + * @param listener the listener to be removed + * @throws IllegalArgumentException if the listener is {@code null}. + */ + public void removePlaybackListener(@NonNull PlaybackListener listener) { + mProvider.removePlaybackListener_impl(listener); + } + + /** + * Return the {@link PlaybackState2} from the player. + * + * @return playback state + */ + public PlaybackState2 getPlaybackState() { + return mProvider.getPlaybackState_impl(); + } } diff --git a/media/java/android/media/PlaybackState2.java b/media/java/android/media/PlaybackState2.java index 7688fbc9bb41..da776ebefa38 100644 --- a/media/java/android/media/PlaybackState2.java +++ b/media/java/android/media/PlaybackState2.java @@ -108,6 +108,9 @@ public final class PlaybackState2 { private final long mActiveItemId; private final CharSequence mErrorMessage; + // TODO(jaewan): Better error handling? + // E.g. media item at #2 has issue, but continue playing #3 + // login error. fire intent xxx to login public PlaybackState2(int state, long position, long updateTime, float speed, long bufferedPosition, long activeItemId, CharSequence error) { mState = state; diff --git a/media/java/android/media/SessionPlayer2.java b/media/java/android/media/SessionPlayer2.java new file mode 100644 index 000000000000..8e9ed23dab59 --- /dev/null +++ b/media/java/android/media/SessionPlayer2.java @@ -0,0 +1,142 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +import android.annotation.SystemApi; +import android.content.Context; +import android.media.MediaSession2.PlaylistParams; +import android.media.update.ApiLoader; +import android.media.update.SessionPlayer2Provider; + +import java.util.List; +import java.util.concurrent.Executor; + +/** + * Implementation of the {@link MediaPlayerInterface} which is backed by the {@link MediaPlayer2} + * @hide + */ +public class SessionPlayer2 implements MediaPlayerInterface { + private final SessionPlayer2Provider mProvider; + + public SessionPlayer2(Context context) { + mProvider = ApiLoader.getProvider(context).createSessionPlayer2(context, this); + } + + @Override + public void play() { + mProvider.play_impl(); + } + + @Override + public void prepare() { + mProvider.prepare_impl(); + } + + @Override + public void pause() { + mProvider.pause_impl(); + } + + @Override + public void stop() { + mProvider.stop_impl(); + } + + @Override + public void skipToPrevious() { + mProvider.skipToPrevious_impl(); + } + + @Override + public void skipToNext() { + mProvider.skipToNext_impl(); + } + + @Override + public void seekTo(long pos) { + mProvider.seekTo_impl(pos); + } + + @Override + public void fastForward() { + mProvider.fastForward_impl(); + } + + @Override + public void rewind() { + mProvider.rewind_impl(); + } + + @Override + public PlaybackState2 getPlaybackState() { + return mProvider.getPlaybackState_impl(); + } + + @Override + public void setAudioAttributes(AudioAttributes attributes) { + mProvider.setAudioAttributes_impl(attributes); + } + + @Override + public AudioAttributes getAudioAttributes() { + return mProvider.getAudioAttributes_impl(); + } + + @Override + public void setPlaylist(List<MediaItem2> list, PlaylistParams param) { + mProvider.setPlaylist_impl(list, param); + } + + @Override + public List<MediaItem2> getPlaylist() { + return mProvider.getPlaylist_impl(); + } + + @Override + public void setCurrentPlaylistItem(int index) { + mProvider.setCurrentPlaylistItem_impl(index); + } + + @Override + public void setPlaylistParams(PlaylistParams params) { + mProvider.setPlaylistParams_impl(params); + } + + @Override + public PlaylistParams getPlaylistParams() { + return mProvider.getPlaylistParams_impl(); + } + + @Override + public void addPlaybackListener(Executor executor, PlaybackListener listener) { + mProvider.addPlaybackListener_impl(executor, listener); + } + + @Override + public void removePlaybackListener(PlaybackListener listener) { + mProvider.removePlaybackListener_impl(listener); + } + + public MediaPlayer2 getPlayer() { + return mProvider.getPlayer_impl(); + } + + @SystemApi + public SessionPlayer2Provider getProvider() { + return mProvider; + } +} diff --git a/media/java/android/media/SessionToken2.java b/media/java/android/media/SessionToken2.java index 0abb85229ffc..7591eb3a25be 100644 --- a/media/java/android/media/SessionToken2.java +++ b/media/java/android/media/SessionToken2.java @@ -19,10 +19,13 @@ package android.media; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.content.Context; import android.media.session.MediaSessionManager; +import android.media.update.ApiLoader; +import android.media.update.SessionToken2Provider; import android.os.Bundle; -import android.os.IBinder; -import android.text.TextUtils; +import android.os.IInterface; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -37,7 +40,6 @@ import java.lang.annotation.RetentionPolicy; * It can be also obtained by {@link MediaSessionManager}. * @hide */ -// TODO(jaewan): Move Token to updatable! public final class SessionToken2 { @Retention(RetentionPolicy.SOURCE) @IntDef(value = {TYPE_SESSION, TYPE_SESSION_SERVICE, TYPE_LIBRARY_SERVICE}) @@ -48,91 +50,81 @@ public final class SessionToken2 { public static final int TYPE_SESSION_SERVICE = 1; public static final int TYPE_LIBRARY_SERVICE = 2; - private static final String KEY_TYPE = "android.media.token.type"; - private static final String KEY_PACKAGE_NAME = "android.media.token.package_name"; - private static final String KEY_SERVICE_NAME = "android.media.token.service_name"; - private static final String KEY_ID = "android.media.token.id"; - private static final String KEY_SESSION_BINDER = "android.media.token.session_binder"; + private final SessionToken2Provider mProvider; - private final @TokenType int mType; - private final String mPackageName; - private final String mServiceName; - private final String mId; - private final IMediaSession2 mSessionBinder; + /** + * Constructor for the token. You can only create token for session service or library service + * to use by {@link MediaController2} or {@link MediaBrowser2}. + * + * @param context context + * @param type type + * @param packageName package name + * @param serviceName name of service. Can be {@code null} if it's not an service. + */ + public SessionToken2(@NonNull Context context, @TokenType int type, @NonNull String packageName, + @NonNull String serviceName) { + this(context, -1, type, packageName, serviceName, null, null); + } /** * Constructor for the token. * - * @hide + * @param context context + * @param uid uid * @param type type * @param packageName package name - * @param id id * @param serviceName name of service. Can be {@code null} if it's not an service. - * @param sessionBinder binder for this session. Can be {@code null} if it's service. - * @hide + * @param id id. Can be {@code null} if serviceName is specified. + * @param sessionBinderInterface sessionBinder. Required for the session. */ - // TODO(jaewan): UID is also needed. - // TODO(jaewan): Unhide - public SessionToken2(@TokenType int type, @NonNull String packageName, @NonNull String id, - @Nullable String serviceName, @Nullable IMediaSession2 sessionBinder) { - // TODO(jaewan): Add sanity check. - mType = type; - mPackageName = packageName; - mId = id; - mServiceName = serviceName; - mSessionBinder = sessionBinder; + @SystemApi + public SessionToken2(@NonNull Context context, int uid, @TokenType int type, + @NonNull String packageName, @Nullable String serviceName, @Nullable String id, + @Nullable IInterface sessionBinderInterface) { + mProvider = ApiLoader.getProvider(context) + .createSessionToken2(context, this, uid, type, packageName, + serviceName, id, sessionBinderInterface); } + @Override public int hashCode() { - final int prime = 31; - return mType - + prime * (mPackageName.hashCode() - + prime * (mId.hashCode() - + prime * ((mServiceName != null ? mServiceName.hashCode() : 0) - + prime * (mSessionBinder != null ? mSessionBinder.asBinder().hashCode() : 0)))); + return mProvider.hashCode_impl(); } @Override public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - SessionToken2 other = (SessionToken2) obj; - if (!mPackageName.equals(other.getPackageName()) - || !mServiceName.equals(other.getServiceName()) - || !mId.equals(other.getId()) - || mType != other.getType()) { - return false; - } - if (mSessionBinder == other.getSessionBinder()) { - return true; - } else if (mSessionBinder == null || other.getSessionBinder() == null) { - return false; - } - return mSessionBinder.asBinder().equals(other.getSessionBinder().asBinder()); + return mProvider.equals_impl(obj); } @Override public String toString() { - return "SessionToken {pkg=" + mPackageName + " id=" + mId + " type=" + mType - + " service=" + mServiceName + " binder=" + mSessionBinder + "}"; + return mProvider.toString_impl(); + } + + @SystemApi + public SessionToken2Provider getProvider() { + return mProvider; + } + + /** + * @return uid of the session + */ + public int getUid() { + return mProvider.getUid_impl(); } /** * @return package name */ public String getPackageName() { - return mPackageName; + return mProvider.getPackageName_impl(); } /** * @return id */ public String getId() { - return mId; + return mProvider.getId_imp(); } /** @@ -141,82 +133,23 @@ public final class SessionToken2 { * @see #TYPE_SESSION_SERVICE */ public @TokenType int getType() { - return mType; - } - - /** - * @return session binder. - * @hide - */ - public @Nullable IMediaSession2 getSessionBinder() { - return mSessionBinder; - } - - /** - * @return service name if it's session service. - * @hide - */ - public @Nullable String getServiceName() { - return mServiceName; + return mProvider.getType_impl(); } /** * Create a token from the bundle, exported by {@link #toBundle()}. - * * @param bundle * @return */ - public static SessionToken2 fromBundle(@NonNull Bundle bundle) { - if (bundle == null) { - return null; - } - final @TokenType int type = bundle.getInt(KEY_TYPE, -1); - final String packageName = bundle.getString(KEY_PACKAGE_NAME); - final String serviceName = bundle.getString(KEY_SERVICE_NAME); - final String id = bundle.getString(KEY_ID); - final IBinder sessionBinder = bundle.getBinder(KEY_SESSION_BINDER); - - // Sanity check. - switch (type) { - case TYPE_SESSION: - if (!(sessionBinder instanceof IMediaSession2)) { - throw new IllegalArgumentException("Session needs sessionBinder"); - } - break; - case TYPE_SESSION_SERVICE: - if (TextUtils.isEmpty(serviceName)) { - throw new IllegalArgumentException("Session service needs service name"); - } - if (sessionBinder != null && !(sessionBinder instanceof IMediaSession2)) { - throw new IllegalArgumentException("Invalid session binder"); - } - break; - default: - throw new IllegalArgumentException("Invalid type"); - } - if (TextUtils.isEmpty(packageName) || id == null) { - throw new IllegalArgumentException("Package name nor ID cannot be null."); - } - // TODO(jaewan): Revisit here when we add connection callback to the session for individual - // controller's permission check. With it, sessionBinder should be available - // if and only if for session, not session service. - return new SessionToken2(type, packageName, id, serviceName, - sessionBinder != null ? IMediaSession2.Stub.asInterface(sessionBinder) : null); + public static SessionToken2 fromBundle(@NonNull Context context, @NonNull Bundle bundle) { + return ApiLoader.getProvider(context).SessionToken2_fromBundle(context, bundle); } /** * Create a {@link Bundle} from this token to share it across processes. - * * @return Bundle */ public Bundle toBundle() { - Bundle bundle = new Bundle(); - bundle.putString(KEY_PACKAGE_NAME, mPackageName); - bundle.putString(KEY_SERVICE_NAME, mServiceName); - bundle.putString(KEY_ID, mId); - bundle.putInt(KEY_TYPE, mType); - bundle.putBinder(KEY_SESSION_BINDER, - mSessionBinder != null ? mSessionBinder.asBinder() : null); - return bundle; + return mProvider.toBundle_impl(); } } diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl index b8463ddba02e..8135106e51c9 100644 --- a/media/java/android/media/session/ISessionManager.aidl +++ b/media/java/android/media/session/ISessionManager.aidl @@ -52,6 +52,7 @@ interface ISessionManager { void setOnMediaKeyListener(in IOnMediaKeyListener listener); // MediaSession2 - Bundle createSessionToken(String callingPackage, String id, IMediaSession2 binder); + boolean onSessionCreated(in Bundle sessionToken); + void onSessionDestroyed(in Bundle sessionToken); List<Bundle> getSessionTokens(boolean activeSessionOnly, boolean sessionServiceOnly); } diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java index 81b4603ebf93..1023a10e8dd3 100644 --- a/media/java/android/media/session/MediaSessionManager.java +++ b/media/java/android/media/session/MediaSessionManager.java @@ -337,19 +337,34 @@ public final class MediaSessionManager { /** * Called when a {@link MediaSession2} is created. - * * @hide */ // TODO(jaewan): System API - public SessionToken2 createSessionToken(@NonNull String callingPackage, @NonNull String id, - @NonNull IMediaSession2 binder) { + public boolean onSessionCreated(@NonNull SessionToken2 token) { + if (token == null) { + return false; + } + try { + return mService.onSessionCreated(token.toBundle()); + } catch (RemoteException e) { + Log.wtf(TAG, "Cannot communicate with the service.", e); + } + return false; + } + + /** Called when a {@link MediaSession2} is destroyed. + * @hide + */ + // TODO(jaewan): System API + public void onSessionDestroyed(@NonNull SessionToken2 token) { + if (token == null) { + return; + } try { - Bundle bundle = mService.createSessionToken(callingPackage, id, binder); - return SessionToken2.fromBundle(bundle); + mService.onSessionDestroyed(token.toBundle()); } catch (RemoteException e) { Log.wtf(TAG, "Cannot communicate with the service.", e); } - return null; } /** @@ -367,7 +382,7 @@ public final class MediaSessionManager { try { List<Bundle> bundles = mService.getSessionTokens( /* activeSessionOnly */ true, /* sessionServiceOnly */ false); - return toTokenList(bundles); + return toTokenList(mContext, bundles); } catch (RemoteException e) { Log.wtf(TAG, "Cannot communicate with the service.", e); return Collections.emptyList(); @@ -387,7 +402,7 @@ public final class MediaSessionManager { try { List<Bundle> bundles = mService.getSessionTokens( /* activeSessionOnly */ false, /* sessionServiceOnly */ true); - return toTokenList(bundles); + return toTokenList(mContext, bundles); } catch (RemoteException e) { Log.wtf(TAG, "Cannot communicate with the service.", e); return Collections.emptyList(); @@ -410,18 +425,18 @@ public final class MediaSessionManager { try { List<Bundle> bundles = mService.getSessionTokens( /* activeSessionOnly */ false, /* sessionServiceOnly */ false); - return toTokenList(bundles); + return toTokenList(mContext, bundles); } catch (RemoteException e) { Log.wtf(TAG, "Cannot communicate with the service.", e); return Collections.emptyList(); } } - private static List<SessionToken2> toTokenList(List<Bundle> bundles) { + private static List<SessionToken2> toTokenList(Context context, List<Bundle> bundles) { List<SessionToken2> tokens = new ArrayList<>(); if (bundles != null) { for (int i = 0; i < bundles.size(); i++) { - SessionToken2 token = SessionToken2.fromBundle(bundles.get(i)); + SessionToken2 token = SessionToken2.fromBundle(context, bundles.get(i)); if (token != null) { tokens.add(token); } diff --git a/media/java/android/media/update/MediaController2Provider.java b/media/java/android/media/update/MediaController2Provider.java index cf35358c7841..b72fda94c688 100644 --- a/media/java/android/media/update/MediaController2Provider.java +++ b/media/java/android/media/update/MediaController2Provider.java @@ -35,6 +35,8 @@ import java.util.List; * @hide */ public interface MediaController2Provider extends TransportControlProvider { + void initialize(); + void close_impl(); SessionToken2 getSessionToken_impl(); boolean isConnected_impl(); diff --git a/media/java/android/media/update/MediaSession2Provider.java b/media/java/android/media/update/MediaSession2Provider.java index 801bdeb68eef..abe3cae789c9 100644 --- a/media/java/android/media/update/MediaSession2Provider.java +++ b/media/java/android/media/update/MediaSession2Provider.java @@ -19,6 +19,7 @@ package android.media.update; import android.media.AudioAttributes; import android.media.MediaItem2; import android.media.MediaPlayerInterface; +import android.media.MediaPlayerInterface.PlaybackListener; import android.media.MediaSession2.Command; import android.media.MediaSession2.CommandButton; import android.media.MediaSession2.CommandGroup; @@ -30,11 +31,14 @@ import android.os.Bundle; import android.os.ResultReceiver; import java.util.List; +import java.util.concurrent.Executor; /** * @hide */ public interface MediaSession2Provider extends TransportControlProvider { + void initialize(); + void close_impl(); void setPlayer_impl(MediaPlayerInterface player); void setPlayer_impl(MediaPlayerInterface player, VolumeProvider volumeProvider); @@ -42,7 +46,6 @@ public interface MediaSession2Provider extends TransportControlProvider { SessionToken2 getToken_impl(); List<ControllerInfo> getConnectedControllers_impl(); void setCustomLayout_impl(ControllerInfo controller, List<CommandButton> layout); - void setAudioAttributes_impl(AudioAttributes attributes); void setAudioFocusRequest_impl(int focusGain); void setAllowedCommands_impl(ControllerInfo controller, CommandGroup commands); @@ -51,9 +54,13 @@ public interface MediaSession2Provider extends TransportControlProvider { ResultReceiver receiver); void sendCustomCommand_impl(Command command, Bundle args); void setPlaylist_impl(List<MediaItem2> playlist, PlaylistParams param); + List<MediaItem2> getPlaylist_impl(); void setPlaylistParams_impl(PlaylistParams params); PlaylistParams getPlaylistParams_impl(); + void addPlaybackListener_impl(Executor executor, PlaybackListener listener); + void removePlaybackListener_impl(PlaybackListener listener); + interface ControllerInfoProvider { String getPackageName_impl(); int getUid_impl(); diff --git a/media/java/android/media/update/SessionPlayer2Provider.java b/media/java/android/media/update/SessionPlayer2Provider.java new file mode 100644 index 000000000000..a084a59c5c06 --- /dev/null +++ b/media/java/android/media/update/SessionPlayer2Provider.java @@ -0,0 +1,53 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.update; + +import android.media.AudioAttributes; +import android.media.MediaItem2; +import android.media.MediaPlayer2; +import android.media.MediaPlayerInterface.PlaybackListener; +import android.media.MediaSession2.PlaylistParams; +import android.media.PlaybackState2; + +import java.util.List; +import java.util.concurrent.Executor; + +/** + * @hide + */ +public interface SessionPlayer2Provider { + void play_impl(); + void prepare_impl(); + void pause_impl(); + void stop_impl(); + void skipToPrevious_impl(); + void skipToNext_impl(); + void seekTo_impl(long pos); + void fastForward_impl(); + void rewind_impl(); + PlaybackState2 getPlaybackState_impl(); + void setAudioAttributes_impl(AudioAttributes attributes); + AudioAttributes getAudioAttributes_impl(); + void setPlaylist_impl(List<MediaItem2> list, PlaylistParams param); + List<MediaItem2> getPlaylist_impl(); + void setCurrentPlaylistItem_impl(int index); + void setPlaylistParams_impl(PlaylistParams params); + PlaylistParams getPlaylistParams_impl(); + void addPlaybackListener_impl(Executor executor, PlaybackListener listener); + void removePlaybackListener_impl(PlaybackListener listener); + MediaPlayer2 getPlayer_impl(); +} diff --git a/media/java/android/media/update/SessionToken2Provider.java b/media/java/android/media/update/SessionToken2Provider.java new file mode 100644 index 000000000000..95d6ce07b8a8 --- /dev/null +++ b/media/java/android/media/update/SessionToken2Provider.java @@ -0,0 +1,34 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.update; + +import android.os.Bundle; + +/** + * @hide + */ +public interface SessionToken2Provider { + String getPackageName_impl(); + String getId_imp(); + int getType_impl(); + int getUid_impl(); + Bundle toBundle_impl(); + + int hashCode_impl(); + boolean equals_impl(Object obj); + String toString_impl(); +} diff --git a/media/java/android/media/update/StaticProvider.java b/media/java/android/media/update/StaticProvider.java index 7faac951ce98..963bc7475ceb 100644 --- a/media/java/android/media/update/StaticProvider.java +++ b/media/java/android/media/update/StaticProvider.java @@ -30,10 +30,12 @@ import android.media.MediaPlayerInterface; import android.media.MediaSession2; import android.media.MediaSession2.SessionCallback; import android.media.MediaSessionService2; +import android.media.SessionPlayer2; import android.media.SessionToken2; import android.media.VolumeProvider; import android.media.update.MediaLibraryService2Provider.MediaLibrarySessionProvider; import android.media.update.MediaSession2Provider.ControllerInfoProvider; +import android.os.Bundle; import android.os.IInterface; import android.util.AttributeSet; import android.widget.MediaControlView2; @@ -71,4 +73,10 @@ public interface StaticProvider { MediaLibrarySession instance, MediaPlayerInterface player, String id, VolumeProvider volumeProvider, int ratingType, PendingIntent sessionActivity, Executor executor, MediaLibrarySessionCallback callback); + SessionToken2Provider createSessionToken2(Context context, SessionToken2 instance, + int uid, int type, String packageName, String serviceName, String id, + IInterface sessionBinderInterface); + SessionToken2 SessionToken2_fromBundle(Context context, Bundle bundle); + + SessionPlayer2Provider createSessionPlayer2(Context context, SessionPlayer2 instance); } diff --git a/media/java/android/media/update/TransportControlProvider.java b/media/java/android/media/update/TransportControlProvider.java index 0c87063efb44..44f82b295629 100644 --- a/media/java/android/media/update/TransportControlProvider.java +++ b/media/java/android/media/update/TransportControlProvider.java @@ -16,6 +16,8 @@ package android.media.update; +import android.media.PlaybackState2; + /** * @hide */ @@ -31,4 +33,6 @@ public interface TransportControlProvider { void rewind_impl(); void seekTo_impl(long pos); void setCurrentPlaylistItem_impl(int index); + + PlaybackState2 getPlaybackState_impl(); } diff --git a/services/core/java/com/android/server/media/MediaSession2Record.java b/services/core/java/com/android/server/media/MediaSession2Record.java index 4000a11dbf15..ffddf60817ee 100644 --- a/services/core/java/com/android/server/media/MediaSession2Record.java +++ b/services/core/java/com/android/server/media/MediaSession2Record.java @@ -44,138 +44,88 @@ class MediaSession2Record { private static final boolean DEBUG = true; // TODO(jaewan): Change private final Context mContext; + private final SessionToken2 mSessionToken; private final SessionDestroyedListener mSessionDestroyedListener; // TODO(jaewan): Replace these with the mContext.getMainExecutor() - private final Handler mMainHandler; private final Executor mMainExecutor; private MediaController2 mController; - private ControllerCallback mControllerCallback; - - private int mSessionPid; /** * Constructor */ - public MediaSession2Record(@NonNull Context context, + public MediaSession2Record(@NonNull Context context, @NonNull SessionToken2 token, @NonNull SessionDestroyedListener listener) { mContext = context; + mSessionToken = token; mSessionDestroyedListener = listener; - - mMainHandler = new Handler(Looper.getMainLooper()); - mMainExecutor = (runnable) -> { - mMainHandler.post(runnable); - }; - } - - public int getSessionPid() { - return mSessionPid; + mMainExecutor = (runnable) -> runnable.run(); } public Context getContext() { return mContext; } - @CallSuper public void onSessionDestroyed() { if (mController != null) { - mControllerCallback.destroy(); mController.close(); + // close() triggers ControllerCallback.onDisconnected() here already. mController = null; } - mSessionPid = 0; } - /** - * Create session token and tell server that session is now active. - * - * @param sessionPid session's pid - * @return a token if successfully set, {@code null} if sanity check fails. - */ - // TODO(jaewan): also add uid for multiuser support - @CallSuper - public @Nullable - SessionToken2 createSessionToken(int sessionPid, String packageName, String id, - IMediaSession2 sessionBinder) { + public boolean onSessionCreated(SessionToken2 token) { if (mController != null) { - if (mSessionPid != sessionPid) { - // A package uses the same id for session across the different process. - return null; - } - // If a session becomes inactive and then active again very quickly, previous 'inactive' - // may not have delivered yet. Check if it's the case and destroy controller before - // creating its session record to prevents getXXTokens() API from returning duplicated - // tokens. - // TODO(jaewan): Change this. If developer is really creating two sessions with the same - // id, this will silently invalidate previous session and no way for - // developers to know that. - // Instead, keep the list of static session ids from our APIs. - // Also change Controller2Impl.onConnectionChanged / getController. - // Also clean up ControllerCallback#destroy(). - if (DEBUG) { - Log.d(TAG, "Session is recreated almost immediately. " + this); - } - onSessionDestroyed(); + // Disclaimer: This may fail if following happens for an app. + // Step 1) Create a session in the process #1 + // Step 2) Process #1 is killed + // Step 3) Before the death of process #1 is delivered, + // (i.e. ControllerCallback#onDisconnected is called), + // new process is started and create another session with the same + // id in the new process. + // Step 4) fail!!! But this is tricky case that wouldn't happen in normal. + Log.w(TAG, "Cannot create a new session with the id=" + token.getId() + " in the" + + " pkg=" + token.getPackageName() + ". ID should be unique in a package"); + return false; } - mController = onCreateMediaController(packageName, id, sessionBinder); - mSessionPid = sessionPid; - return mController.getSessionToken(); + mController = new MediaController2(mContext, token, mMainExecutor, + new ControllerCallback()); + return true; } /** - * Called when session becomes active and needs controller to listen session's activeness. - * <p> - * Should be overridden by subclasses to create token with its own extra information. + * @return token */ - MediaController2 onCreateMediaController( - String packageName, String id, IMediaSession2 sessionBinder) { - SessionToken2 token = new SessionToken2( - SessionToken2.TYPE_SESSION, packageName, id, null, sessionBinder); - return createMediaController(token); - } - - final MediaController2 createMediaController(SessionToken2 token) { - mControllerCallback = new ControllerCallback(); - return new MediaController2(mContext, token, mMainExecutor, mControllerCallback); + public SessionToken2 getToken() { + return mSessionToken; } /** - * @return controller. Note that framework can only call oneway calls. + * @return controller */ - public SessionToken2 getToken() { - return mController == null ? null : mController.getSessionToken(); + public MediaController2 getController() { + return mController; } @Override public String toString() { return getToken() == null - ? "Token {null}" - : "SessionRecord {pid=" + mSessionPid + ", " + getToken().toString() + "}"; + ? "Token {null}" : "SessionRecord {" + getToken().toString() + "}"; } private class ControllerCallback extends MediaController2.ControllerCallback { - private final AtomicBoolean mIsActive = new AtomicBoolean(true); - - // This is called on the main thread with no lock. So place ensure followings. + // This is called on the random thread with no lock. So place ensure followings. // 1. Don't touch anything in the parent class that needs synchronization. // All other APIs in the MediaSession2Record assumes that server would use them with // the lock hold. - // 2. This can be called after the controller registered is released. + // 2. This can be called after the controller registered is closed. @Override public void onDisconnected() { - if (!mIsActive.get()) { - return; - } if (DEBUG) { Log.d(TAG, "onDisconnected, token=" + getToken()); } mSessionDestroyedListener.onSessionDestroyed(MediaSession2Record.this); } - - // TODO(jaewan): Remove this API when we revisit createSessionToken() - public void destroy() { - mIsActive.set(false); - } }; } diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index c7f6014fa1b0..1d6d1ed46f54 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -16,6 +16,8 @@ package com.android.server.media; +import static android.media.SessionToken2.TYPE_SESSION; + import android.app.ActivityManager; import android.app.INotificationManager; import android.app.KeyguardManager; @@ -28,6 +30,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.UserInfo; @@ -130,15 +133,9 @@ public class MediaSessionService extends SystemService implements Monitor { private final List<MediaSession2Record> mSessions = new ArrayList<>(); private final MediaSession2Record.SessionDestroyedListener mSessionDestroyedListener = - (MediaSession2Record record) -> { + (record) -> { synchronized (mLock) { - if (DEBUG) { - Log.d(TAG, record.toString() + " becomes inactive"); - } - record.onSessionDestroyed(); - if (!(record instanceof MediaSessionService2Record)) { - mSessions.remove(record); - } + destroySessionLocked(record); } }; @@ -446,14 +443,16 @@ public class MediaSessionService extends SystemService implements Monitor { } // TODO(jaewan): Query per users. + // TODO(jaewan): Similar codes are also at the updatable. Can't we share codes? + PackageManager manager = getContext().getPackageManager(); List<ResolveInfo> services = new ArrayList<>(); // If multiple actions are declared for a service, browser gets higher priority. - List<ResolveInfo> libraryServices = getContext().getPackageManager().queryIntentServices( + List<ResolveInfo> libraryServices = manager.queryIntentServices( new Intent(MediaLibraryService2.SERVICE_INTERFACE), PackageManager.GET_META_DATA); if (libraryServices != null) { services.addAll(libraryServices); } - List<ResolveInfo> sessionServices = getContext().getPackageManager().queryIntentServices( + List<ResolveInfo> sessionServices = manager.queryIntentServices( new Intent(MediaSessionService2.SERVICE_INTERFACE), PackageManager.GET_META_DATA); if (sessionServices != null) { services.addAll(sessionServices); @@ -468,12 +467,19 @@ public class MediaSessionService extends SystemService implements Monitor { continue; } ServiceInfo serviceInfo = services.get(i).serviceInfo; + int uid; + try { + uid = manager.getPackageUid(serviceInfo.packageName, + PackageManager.GET_META_DATA); + } catch (NameNotFoundException e) { + continue; + } String id = (serviceInfo.metaData != null) ? serviceInfo.metaData.getString( MediaSessionService2.SERVICE_META_DATA) : null; // Do basic sanity check // TODO(jaewan): also santity check if it's protected with the system|privileged // permission - boolean conflict = (getSessionRecordLocked(serviceInfo.name, id) != null); + boolean conflict = (getSessionRecordLocked(uid, serviceInfo.name, id) != null); if (conflict) { Log.w(TAG, serviceInfo.packageName + " contains multiple" + " MediaSessionService2s declared in the manifest with" @@ -481,10 +487,12 @@ public class MediaSessionService extends SystemService implements Monitor { + serviceInfo.packageName + "/" + serviceInfo.name); } else { int type = (libraryServices.contains(services.get(i))) - ? SessionToken2.TYPE_LIBRARY_SERVICE : SessionToken2.TYPE_SESSION_SERVICE; - MediaSessionService2Record record = - new MediaSessionService2Record(getContext(), mSessionDestroyedListener, - type, serviceInfo.packageName, serviceInfo.name, id); + ? SessionToken2.TYPE_LIBRARY_SERVICE + : SessionToken2.TYPE_SESSION_SERVICE; + SessionToken2 token = new SessionToken2(getContext(), uid, type, + serviceInfo.packageName, serviceInfo.name, id, null); + MediaSession2Record record = new MediaSession2Record(getContext(), + token, mSessionDestroyedListener); mSessions.add(record); } } @@ -497,17 +505,27 @@ public class MediaSessionService extends SystemService implements Monitor { } } - MediaSession2Record getSessionRecordLocked(String packageName, String id) { + private MediaSession2Record getSessionRecordLocked(int uid, String packageName, String id) { for (int i = 0; i < mSessions.size(); i++) { - MediaSession2Record record = mSessions.get(i); - if (record.getToken().getPackageName().equals(packageName) - && record.getToken().getId().equals(id)) { - return record; + SessionToken2 token = mSessions.get(i).getToken(); + if (token.getUid() == uid && token.getPackageName().equals(packageName) + && token.getId().equals(id)) { + return mSessions.get(i); } } return null; } + private void destroySessionLocked(MediaSession2Record record) { + if (DEBUG) { + Log.d(TAG, record.toString() + " becomes inactive"); + } + record.onSessionDestroyed(); + if (record.getToken().getType() == TYPE_SESSION) { + mSessions.remove(record); + } + } + private void enforcePackageName(String packageName, int uid) { if (TextUtils.isEmpty(packageName)) { throw new IllegalArgumentException("packageName may not be empty"); @@ -1410,29 +1428,55 @@ public class MediaSessionService extends SystemService implements Monitor { } @Override - public Bundle createSessionToken(String sessionPackage, String id, - IMediaSession2 sessionBinder) throws RemoteException { - int uid = Binder.getCallingUid(); - int pid = Binder.getCallingPid(); - - MediaSession2Record record; - SessionToken2 token; - // TODO(jaewan): Add sanity check for the token if calling package is from uid. + public boolean onSessionCreated(Bundle sessionToken) { + final int uid = Binder.getCallingUid(); + final int pid = Binder.getCallingPid(); + final SessionToken2 token = SessionToken2.fromBundle(getContext(), sessionToken); + if (token == null || token.getUid() != uid) { + Log.w(TAG, "onSessionCreated failed, expected caller uid=" + token.getUid() + + " but from uid=" + uid); + } + if (DEBUG) { + Log.d(TAG, "onSessionCreated " + token); + } synchronized (mLock) { - record = getSessionRecordLocked(sessionPackage, id); - if (record == null) { - record = new MediaSession2Record(getContext(), mSessionDestroyedListener); + MediaSession2Record record = getSessionRecordLocked( + uid, token.getPackageName(), token.getId()); + if (record != null) { + return record.onSessionCreated(token); + } else { + record = new MediaSession2Record( + getContext(), token, mSessionDestroyedListener); mSessions.add(record); + return record.onSessionCreated(token); } - token = record.createSessionToken(pid, sessionPackage, id, sessionBinder); - if (token == null) { - Log.d(TAG, "failed to create session token for " + sessionPackage - + " from pid=" + pid + ". Previously " + record); + } + } + + @Override + public void onSessionDestroyed(Bundle sessionToken) { + final int uid = Binder.getCallingUid(); + final int pid = Binder.getCallingPid(); + final SessionToken2 token = SessionToken2.fromBundle(getContext(), sessionToken); + if (token == null || token.getUid() != uid) { + Log.w(TAG, "onSessionDestroyed failed, expected caller uid=" + token.getUid() + + " but from uid=" + uid); + } + if (DEBUG) { + Log.d(TAG, "onSessionDestroyed " + token); + } + synchronized (mLock) { + MediaSession2Record record = getSessionRecordLocked( + uid, token.getPackageName(), token.getId()); + if (record != null) { + record.onSessionDestroyed(); } else { - Log.d(TAG, "session " + token + " is created"); + if (DEBUG) { + Log.d(TAG, "Cannot find a session record to destroy. uid=" + uid + + ", pkg=" + token.getPackageName() + ", id=" + token.getId()); + } } } - return token == null ? null : token.toBundle(); } // TODO(jaewan): Protect this API with permission @@ -1444,8 +1488,8 @@ public class MediaSessionService extends SystemService implements Monitor { synchronized (mLock) { for (int i = 0; i < mSessions.size(); i++) { MediaSession2Record record = mSessions.get(i); - boolean isSessionService = (record instanceof MediaSessionService2Record); - boolean isActive = record.getSessionPid() != 0; + boolean isSessionService = (record.getToken().getType() != TYPE_SESSION); + boolean isActive = record.getController() != null; if ((!activeSessionOnly && isSessionService) || (!sessionServiceOnly && isActive)) { SessionToken2 token = record.getToken(); diff --git a/services/core/java/com/android/server/media/MediaSessionService2Record.java b/services/core/java/com/android/server/media/MediaSessionService2Record.java deleted file mode 100644 index d033f552124f..000000000000 --- a/services/core/java/com/android/server/media/MediaSessionService2Record.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.media; - -import android.content.Context; -import android.media.IMediaSession2; -import android.media.MediaController2; -import android.media.SessionToken2; -import android.media.MediaSessionService2; - -/** - * Records a {@link MediaSessionService2}. - * <p> - * Owner of this object should handle synchronization. - */ -class MediaSessionService2Record extends MediaSession2Record { - private static final boolean DEBUG = true; // TODO(jaewan): Modify - private static final String TAG = "SessionService2Record"; - - private final int mType; - private final String mServiceName; - private final SessionToken2 mToken; - - public MediaSessionService2Record(Context context, - SessionDestroyedListener sessionDestroyedListener, int type, - String packageName, String serviceName, String id) { - super(context, sessionDestroyedListener); - mType = type; - mServiceName = serviceName; - mToken = new SessionToken2(mType, packageName, id, mServiceName, null); - } - - /** - * Overriden to change behavior of - * {@link #createSessionToken(int, String, String, IMediaSession2)}}. - */ - @Override - MediaController2 onCreateMediaController( - String packageName, String id, IMediaSession2 sessionBinder) { - SessionToken2 token = new SessionToken2(mType, packageName, id, mServiceName, sessionBinder); - return createMediaController(token); - } - - /** - * @return token with no session binder information. - */ - @Override - public SessionToken2 getToken() { - return mToken; - } -} |