Loading Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -417,6 +417,8 @@ java_library { "media/java/android/media/IMediaRouterService.aidl", "media/java/android/media/IMediaScannerListener.aidl", "media/java/android/media/IMediaScannerService.aidl", "media/java/android/media/IMediaSession2.aidl", "media/java/android/media/IMediaSession2Callback.aidl", "media/java/android/media/IPlaybackConfigDispatcher.aidl", ":libaudioclient_aidl", "media/java/android/media/IRecordingConfigDispatcher.aidl", Loading media/java/android/media/IMediaSession2.aidl 0 → 100644 +68 −0 Original line number Diff line number Diff line /* * 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.media.session.PlaybackState; import android.media.IMediaSession2Callback; /** * Interface to MediaSession2. Framework MUST only call oneway APIs. * * @hide */ // TODO(jaewan): Make this oneway interface. // Malicious app can fake session binder and holds commands from controller. 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. // TODO(jaewan): We may consider to add another binder just for the connection // 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); ////////////////////////////////////////////////////////////////////////////////////////////// // Playback controls. ////////////////////////////////////////////////////////////////////////////////////////////// oneway void play(IMediaSession2Callback caller); oneway void pause(IMediaSession2Callback caller); oneway void stop(IMediaSession2Callback caller); oneway void skipToPrevious(IMediaSession2Callback caller); oneway void skipToNext(IMediaSession2Callback caller); PlaybackState getPlaybackState(); ////////////////////////////////////////////////////////////////////////////////////////////// // 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); } media/java/android/media/IMediaSession2Callback.aidl 0 → 100644 +46 −0 Original line number Diff line number Diff line /* * 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.media.session.PlaybackState; import android.media.IMediaSession2; /** * Interface from MediaSession2 to MediaSession2Record. * <p> * Keep this interface oneway. Otherwise a malicious app may implement fake version of this, * and holds calls from session to make session owner(s) frozen. * * @hide */ oneway interface IMediaSession2Callback { void onPlaybackStateChanged(in PlaybackState state); /** * Called only when the controller is created with service's token. * * @param sessionBinder {@code null} if the connect is rejected or is disconnected. a session * binder if the connect is accepted. * @param commands initially allowed commands. */ // TODO(jaewan): Also need to pass flags for allowed actions for permission check. // For example, a media can allow setRating only for whitelisted apps // it's better for controller to know such information in advance. // Follow-up TODO: Add similar functions to the session. // TODO(jaewan): Is term 'accepted/rejected' correct? For permission, 'grant' is used. void onConnectionChanged(IMediaSession2 sessionBinder, long commands); } media/java/android/media/MediaController2.java 0 → 100644 +196 −0 Original line number Diff line number Diff line /* * 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.NonNull; import android.annotation.Nullable; import android.content.Context; import android.media.MediaSession2.CommandFlags; import android.media.MediaSession2.ControllerInfo; import android.media.session.PlaybackState; import android.media.update.ApiLoader; import android.media.update.MediaController2Provider; import android.os.Handler; import java.util.concurrent.Executor; /** * Allows an app to interact with an active {@link MediaSession2} or a * {@link MediaSessionService2} in any status. Media buttons and other commands can be sent to * the session. * <p> * When you're done, use {@link #release()} to clean up resources. This also helps session service * to be destroyed when there's no controller associated with it. * <p> * When controlling {@link MediaSession2}, the controller will be available immediately after * the creation. * <p> * When controlling {@link MediaSessionService2}, the {@link MediaController2} would be * available only if the session service allows this controller by * {@link MediaSession2.SessionCallback#onConnect(ControllerInfo)} for the service. Wait * {@link ControllerCallback#onConnected(long)} or {@link ControllerCallback#onDisconnected()} for * the result. * <p> * A controller can be created through {@link MediaPlayerSessionManager} if you hold the * signature|privileged permission "android.permission.MEDIA_CONTENT_CONTROL" permission or are * an enabled notification listener or by getting a {@link SessionToken} directly the * the session owner. * <p> * MediaController2 objects are thread-safe. * <p> * @see MediaSession2 * @see MediaSessionService2 * @hide */ // TODO(jaewan): Unhide // TODO(jaewan): Revisit comments. Currently MediaBrowser case is missing. public class MediaController2 extends MediaPlayerBase { /** * Interface for listening to change in activeness of the {@link MediaSession2}. It's * active if and only if it has set a player. */ public abstract static class ControllerCallback { /** * Called when the controller is successfully connected to the session. The controller * becomes available afterwards. * * @param commands commands that's allowed by the session. */ public void onConnected(@CommandFlags long commands) { } /** * Called when the session refuses the controller or the controller is disconnected from * the session. The controller becomes unavailable afterwards and the callback wouldn't * be called. * <p> * It will be also called after the {@link #release()}, so you can put clean up code here. * You don't need to call {@link #release()} after this. */ public void onDisconnected() { } } private final MediaController2Provider mProvider; /** * Create a {@link MediaController2} from the {@link SessionToken}. This connects to the session * and may wake up the service if it's not available. * * @param context Context * @param token token to connect to * @param callback controller callback to receive changes in * @param executor executor to run callbacks on. */ // TODO(jaewan): Put @CallbackExecutor to the constructor. public MediaController2(@NonNull Context context, @NonNull SessionToken token, @NonNull ControllerCallback callback, @NonNull Executor executor) { super(); // 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 = ApiLoader.getProvider(context) .createMediaController2(this, context, token, callback, executor); } /** * Release this object, and disconnect from the session. After this, callbacks wouldn't be * received. */ public void release() { mProvider.release_impl(); } /** * @hide */ public MediaController2Provider getProvider() { return mProvider; } /** * @return token */ public @NonNull SessionToken getSessionToken() { return mProvider.getSessionToken_impl(); } /** * Returns whether this class is connected to active {@link MediaSession2} or not. */ public boolean isConnected() { return mProvider.isConnected_impl(); } @Override public void play() { mProvider.play_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 @Nullable PlaybackState getPlaybackState() { return mProvider.getPlaybackState_impl(); } /** * Add a {@link PlaybackListener} to listen changes in the * {@link MediaSession2}. * * @param listener the listener that will be run * @param handler the Handler that will receive the listener * @throws IllegalArgumentException Called when either the listener or handler is {@code null}. */ // TODO(jaewan): Match with the addSessionAvailabilityListener() that tells the current state // through the listener. // TODO(jaewan): Can handler be null? Follow the API guideline after it's finalized. @Override public void addPlaybackListener(@NonNull PlaybackListener listener, @NonNull Handler handler) { mProvider.addPlaybackListener_impl(listener, handler); } /** * Remove previously added {@link PlaybackListener}. * * @param listener the listener to be removed * @throws IllegalArgumentException if the listener is {@code null}. */ @Override public void removePlaybackListener(@NonNull PlaybackListener listener) { mProvider.removePlaybackListener_impl(listener); } } media/java/android/media/MediaPlayerBase.java 0 → 100644 +69 −0 Original line number Diff line number Diff line /* * 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.media.session.PlaybackState; import android.os.Handler; /** * Tentative interface for all media players that want media session. * APIs are named to avoid conflicts with MediaPlayer APIs. * All calls should be asynchrounous. * * @hide */ // TODO(wjia) Finalize the list of MediaPlayer2, which MediaPlayerBase's APIs will be come from. public abstract class MediaPlayerBase { /** * Listens change in {@link PlaybackState}. */ public interface PlaybackListener { /** * Called when {@link PlaybackState} for this player is changed. */ void onPlaybackChanged(PlaybackState state); } // TODO(jaewan): setDataSources()? // TODO(jaewan): Add release() or do that in stop()? // TODO(jaewan): Add set/getSupportedActions(). public abstract void play(); public abstract void pause(); public abstract void stop(); public abstract void skipToPrevious(); public abstract void skipToNext(); // Currently PlaybackState's error message is the content title (for testing only) // TODO(jaewan): Add metadata support public abstract PlaybackState getPlaybackState(); /** * Add a {@link PlaybackListener} to be invoked when the playback state is changed. * * @param listener the listener that will be run * @param handler the Handler that will receive the listener */ public abstract void addPlaybackListener(PlaybackListener listener, Handler handler); /** * Remove previously added {@link PlaybackListener}. * * @param listener the listener to be removed */ public abstract void removePlaybackListener(PlaybackListener listener); } Loading
Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -417,6 +417,8 @@ java_library { "media/java/android/media/IMediaRouterService.aidl", "media/java/android/media/IMediaScannerListener.aidl", "media/java/android/media/IMediaScannerService.aidl", "media/java/android/media/IMediaSession2.aidl", "media/java/android/media/IMediaSession2Callback.aidl", "media/java/android/media/IPlaybackConfigDispatcher.aidl", ":libaudioclient_aidl", "media/java/android/media/IRecordingConfigDispatcher.aidl", Loading
media/java/android/media/IMediaSession2.aidl 0 → 100644 +68 −0 Original line number Diff line number Diff line /* * 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.media.session.PlaybackState; import android.media.IMediaSession2Callback; /** * Interface to MediaSession2. Framework MUST only call oneway APIs. * * @hide */ // TODO(jaewan): Make this oneway interface. // Malicious app can fake session binder and holds commands from controller. 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. // TODO(jaewan): We may consider to add another binder just for the connection // 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); ////////////////////////////////////////////////////////////////////////////////////////////// // Playback controls. ////////////////////////////////////////////////////////////////////////////////////////////// oneway void play(IMediaSession2Callback caller); oneway void pause(IMediaSession2Callback caller); oneway void stop(IMediaSession2Callback caller); oneway void skipToPrevious(IMediaSession2Callback caller); oneway void skipToNext(IMediaSession2Callback caller); PlaybackState getPlaybackState(); ////////////////////////////////////////////////////////////////////////////////////////////// // 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); }
media/java/android/media/IMediaSession2Callback.aidl 0 → 100644 +46 −0 Original line number Diff line number Diff line /* * 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.media.session.PlaybackState; import android.media.IMediaSession2; /** * Interface from MediaSession2 to MediaSession2Record. * <p> * Keep this interface oneway. Otherwise a malicious app may implement fake version of this, * and holds calls from session to make session owner(s) frozen. * * @hide */ oneway interface IMediaSession2Callback { void onPlaybackStateChanged(in PlaybackState state); /** * Called only when the controller is created with service's token. * * @param sessionBinder {@code null} if the connect is rejected or is disconnected. a session * binder if the connect is accepted. * @param commands initially allowed commands. */ // TODO(jaewan): Also need to pass flags for allowed actions for permission check. // For example, a media can allow setRating only for whitelisted apps // it's better for controller to know such information in advance. // Follow-up TODO: Add similar functions to the session. // TODO(jaewan): Is term 'accepted/rejected' correct? For permission, 'grant' is used. void onConnectionChanged(IMediaSession2 sessionBinder, long commands); }
media/java/android/media/MediaController2.java 0 → 100644 +196 −0 Original line number Diff line number Diff line /* * 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.NonNull; import android.annotation.Nullable; import android.content.Context; import android.media.MediaSession2.CommandFlags; import android.media.MediaSession2.ControllerInfo; import android.media.session.PlaybackState; import android.media.update.ApiLoader; import android.media.update.MediaController2Provider; import android.os.Handler; import java.util.concurrent.Executor; /** * Allows an app to interact with an active {@link MediaSession2} or a * {@link MediaSessionService2} in any status. Media buttons and other commands can be sent to * the session. * <p> * When you're done, use {@link #release()} to clean up resources. This also helps session service * to be destroyed when there's no controller associated with it. * <p> * When controlling {@link MediaSession2}, the controller will be available immediately after * the creation. * <p> * When controlling {@link MediaSessionService2}, the {@link MediaController2} would be * available only if the session service allows this controller by * {@link MediaSession2.SessionCallback#onConnect(ControllerInfo)} for the service. Wait * {@link ControllerCallback#onConnected(long)} or {@link ControllerCallback#onDisconnected()} for * the result. * <p> * A controller can be created through {@link MediaPlayerSessionManager} if you hold the * signature|privileged permission "android.permission.MEDIA_CONTENT_CONTROL" permission or are * an enabled notification listener or by getting a {@link SessionToken} directly the * the session owner. * <p> * MediaController2 objects are thread-safe. * <p> * @see MediaSession2 * @see MediaSessionService2 * @hide */ // TODO(jaewan): Unhide // TODO(jaewan): Revisit comments. Currently MediaBrowser case is missing. public class MediaController2 extends MediaPlayerBase { /** * Interface for listening to change in activeness of the {@link MediaSession2}. It's * active if and only if it has set a player. */ public abstract static class ControllerCallback { /** * Called when the controller is successfully connected to the session. The controller * becomes available afterwards. * * @param commands commands that's allowed by the session. */ public void onConnected(@CommandFlags long commands) { } /** * Called when the session refuses the controller or the controller is disconnected from * the session. The controller becomes unavailable afterwards and the callback wouldn't * be called. * <p> * It will be also called after the {@link #release()}, so you can put clean up code here. * You don't need to call {@link #release()} after this. */ public void onDisconnected() { } } private final MediaController2Provider mProvider; /** * Create a {@link MediaController2} from the {@link SessionToken}. This connects to the session * and may wake up the service if it's not available. * * @param context Context * @param token token to connect to * @param callback controller callback to receive changes in * @param executor executor to run callbacks on. */ // TODO(jaewan): Put @CallbackExecutor to the constructor. public MediaController2(@NonNull Context context, @NonNull SessionToken token, @NonNull ControllerCallback callback, @NonNull Executor executor) { super(); // 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 = ApiLoader.getProvider(context) .createMediaController2(this, context, token, callback, executor); } /** * Release this object, and disconnect from the session. After this, callbacks wouldn't be * received. */ public void release() { mProvider.release_impl(); } /** * @hide */ public MediaController2Provider getProvider() { return mProvider; } /** * @return token */ public @NonNull SessionToken getSessionToken() { return mProvider.getSessionToken_impl(); } /** * Returns whether this class is connected to active {@link MediaSession2} or not. */ public boolean isConnected() { return mProvider.isConnected_impl(); } @Override public void play() { mProvider.play_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 @Nullable PlaybackState getPlaybackState() { return mProvider.getPlaybackState_impl(); } /** * Add a {@link PlaybackListener} to listen changes in the * {@link MediaSession2}. * * @param listener the listener that will be run * @param handler the Handler that will receive the listener * @throws IllegalArgumentException Called when either the listener or handler is {@code null}. */ // TODO(jaewan): Match with the addSessionAvailabilityListener() that tells the current state // through the listener. // TODO(jaewan): Can handler be null? Follow the API guideline after it's finalized. @Override public void addPlaybackListener(@NonNull PlaybackListener listener, @NonNull Handler handler) { mProvider.addPlaybackListener_impl(listener, handler); } /** * Remove previously added {@link PlaybackListener}. * * @param listener the listener to be removed * @throws IllegalArgumentException if the listener is {@code null}. */ @Override public void removePlaybackListener(@NonNull PlaybackListener listener) { mProvider.removePlaybackListener_impl(listener); } }
media/java/android/media/MediaPlayerBase.java 0 → 100644 +69 −0 Original line number Diff line number Diff line /* * 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.media.session.PlaybackState; import android.os.Handler; /** * Tentative interface for all media players that want media session. * APIs are named to avoid conflicts with MediaPlayer APIs. * All calls should be asynchrounous. * * @hide */ // TODO(wjia) Finalize the list of MediaPlayer2, which MediaPlayerBase's APIs will be come from. public abstract class MediaPlayerBase { /** * Listens change in {@link PlaybackState}. */ public interface PlaybackListener { /** * Called when {@link PlaybackState} for this player is changed. */ void onPlaybackChanged(PlaybackState state); } // TODO(jaewan): setDataSources()? // TODO(jaewan): Add release() or do that in stop()? // TODO(jaewan): Add set/getSupportedActions(). public abstract void play(); public abstract void pause(); public abstract void stop(); public abstract void skipToPrevious(); public abstract void skipToNext(); // Currently PlaybackState's error message is the content title (for testing only) // TODO(jaewan): Add metadata support public abstract PlaybackState getPlaybackState(); /** * Add a {@link PlaybackListener} to be invoked when the playback state is changed. * * @param listener the listener that will be run * @param handler the Handler that will receive the listener */ public abstract void addPlaybackListener(PlaybackListener listener, Handler handler); /** * Remove previously added {@link PlaybackListener}. * * @param listener the listener to be removed */ public abstract void removePlaybackListener(PlaybackListener listener); }