MediaSession2: Send session info when a controller is connected
Bug: 72547163
Test: Run all tests once
Change-Id: I55730536cce6a938f8117abb433b492ac8e36bfc
diff --git a/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl b/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl
index b3c83e5..a443bf8 100644
--- a/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl
+++ b/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl
@@ -16,6 +16,7 @@
package com.android.media;
+import android.app.PendingIntent;
import android.os.Bundle;
import android.os.ResultReceiver;
@@ -33,8 +34,10 @@
void onPlaylistParamsChanged(in Bundle params);
void onPlaybackInfoChanged(in Bundle playbackInfo);
-
- void onConnected(IMediaSession2 sessionBinder, in Bundle commandGroup, in Bundle playbackState);
+ // TODO(jaewan): Handle when the playlist becomes too huge.
+ void onConnected(IMediaSession2 sessionBinder, in Bundle commandGroup, in Bundle playbackState,
+ in Bundle playbackInfo, in Bundle params, in List<Bundle> playlist, int ratingType,
+ in PendingIntent sessionActivity);
void onDisconnected();
void onCustomLayoutChanged(in List<Bundle> commandButtonlist);
diff --git a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
index 3b31d9e..411aeec 100644
--- a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
@@ -77,6 +77,10 @@
@GuardedBy("mLock")
private PlaybackInfo mPlaybackInfo;
@GuardedBy("mLock")
+ private int mRatingType;
+ @GuardedBy("mLock")
+ private PendingIntent mSessionActivity;
+ @GuardedBy("mLock")
private CommandGroup mCommandGroup;
// Assignment should be used with the lock hold, but should be used without a lock to prevent
@@ -276,14 +280,12 @@
//////////////////////////////////////////////////////////////////////////////////////
@Override
public PendingIntent getSessionActivity_impl() {
- // TODO(jaewan): Implement
- return null;
+ return mSessionActivity;
}
@Override
public int getRatingType_impl() {
- // TODO(jaewan): Implement
- return 0;
+ return mRatingType;
}
@Override
@@ -508,7 +510,9 @@
// Should be used without a lock to prevent potential deadlock.
private void onConnectedNotLocked(IMediaSession2 sessionBinder,
- final CommandGroup commandGroup, final PlaybackState2 state) {
+ final CommandGroup commandGroup, final PlaybackState2 state, final PlaybackInfo info,
+ final PlaylistParams params, final List<MediaItem2> playlist, final int ratingType,
+ final PendingIntent sessionActivity) {
if (DEBUG) {
Log.d(TAG, "onConnectedNotLocked sessionBinder=" + sessionBinder
+ ", commands=" + commandGroup);
@@ -532,6 +536,11 @@
}
mCommandGroup = commandGroup;
mPlaybackState = state;
+ mPlaybackInfo = info;
+ mPlaylistParams = params;
+ mPlaylist = playlist;
+ mRatingType = ratingType;
+ mSessionActivity = sessionActivity;
mSessionBinder = sessionBinder;
try {
// Implementation for the local binder is no-op,
@@ -645,6 +654,9 @@
@Override
public void onPlaybackInfoChanged(Bundle playbackInfo) throws RuntimeException {
+ if (DEBUG) {
+ Log.d(TAG, "onPlaybackInfoChanged");
+ }
final MediaController2Impl controller;
try {
controller = getController();
@@ -658,7 +670,8 @@
@Override
public void onConnected(IMediaSession2 sessionBinder, Bundle commandGroup,
- Bundle playbackState) {
+ Bundle playbackState, Bundle playbackInfo, Bundle playlistParams, List<Bundle>
+ playlist, int ratingType, PendingIntent sessionActivity) {
final MediaController2Impl controller = mController.get();
if (controller == null) {
if (DEBUG) {
@@ -666,9 +679,20 @@
}
return;
}
+ final Context context = controller.getContext();
+ List<MediaItem2> list = new ArrayList<>();
+ for (int i = 0; i < playlist.size(); i++) {
+ MediaItem2 item = MediaItem2.fromBundle(context, playlist.get(i));
+ if (item != null) {
+ list.add(item);
+ }
+ }
controller.onConnectedNotLocked(sessionBinder,
- CommandGroup.fromBundle(controller.getContext(), commandGroup),
- PlaybackState2.fromBundle(controller.getContext(), playbackState));
+ CommandGroup.fromBundle(context, commandGroup),
+ PlaybackState2.fromBundle(context, playbackState),
+ PlaybackInfoImpl.fromBundle(context, playbackInfo),
+ PlaylistParams.fromBundle(context, playlistParams),
+ list, ratingType, sessionActivity);
}
@Override
@@ -706,6 +730,7 @@
// Illegal call. Ignore
return;
}
+ // TODO(jaewan): Fix here. It's controller feature so shouldn't use browser
final MediaBrowser2Impl browser;
try {
browser = getBrowser();
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
index 0b7c546..f323a17 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
@@ -85,6 +85,8 @@
private final SessionToken2 mSessionToken;
private final AudioManager mAudioManager;
private final List<PlaybackListenerHolder> mListeners = new ArrayList<>();
+ private final int mRatingType;
+ private final PendingIntent mSessionActivity;
@GuardedBy("mLock")
private MediaPlayerInterface mPlayer;
@@ -102,7 +104,6 @@
/**
* Can be only called by the {@link Builder#build()}.
*
- * @param instance
* @param context
* @param player
* @param id
@@ -123,6 +124,8 @@
mId = id;
mCallback = callback;
mCallbackExecutor = callbackExecutor;
+ mRatingType = ratingType;
+ mSessionActivity = sessionActivity;
mSessionStub = new MediaSession2Stub(this);
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
@@ -195,9 +198,6 @@
if (player == null) {
throw new IllegalArgumentException("player shouldn't be null");
}
- if (player == mPlayer) {
- return;
- }
PlaybackInfo info =
createPlaybackInfo(null /* VolumeProvider */, player.getAudioAttributes());
synchronized (mLock) {
@@ -218,10 +218,6 @@
if (volumeProvider == null) {
throw new IllegalArgumentException("volumeProvider shouldn't be null");
}
- if (player == mPlayer) {
- return;
- }
-
PlaybackInfo info = createPlaybackInfo(volumeProvider, player.getAudioAttributes());
synchronized (mLock) {
setPlayerLocked(player);
@@ -244,12 +240,25 @@
private PlaybackInfo createPlaybackInfo(VolumeProvider2 volumeProvider, AudioAttributes attrs) {
PlaybackInfo info;
if (volumeProvider == null) {
- int stream = attrs == null ? AudioManager.STREAM_MUSIC : attrs.getVolumeControlStream();
+ int stream;
+ if (attrs == null) {
+ stream = AudioManager.STREAM_MUSIC;
+ } else {
+ stream = attrs.getVolumeControlStream();
+ if (stream == AudioManager.USE_DEFAULT_STREAM_TYPE) {
+ // It may happen if the AudioAttributes doesn't have usage.
+ // Change it to the STREAM_MUSIC because it's not supported by audio manager
+ // for querying volume level.
+ stream = AudioManager.STREAM_MUSIC;
+ }
+ }
info = PlaybackInfoImpl.createPlaybackInfo(
mContext,
PlaybackInfo.PLAYBACK_TYPE_LOCAL,
attrs,
- VolumeProvider2.VOLUME_CONTROL_ABSOLUTE,
+ mAudioManager.isVolumeFixed()
+ ? VolumeProvider2.VOLUME_CONTROL_FIXED
+ : VolumeProvider2.VOLUME_CONTROL_ABSOLUTE,
mAudioManager.getStreamMaxVolume(stream),
mAudioManager.getStreamVolume(stream));
} else {
@@ -563,6 +572,20 @@
return mVolumeProvider;
}
+ PlaybackInfo getPlaybackInfo() {
+ synchronized (mLock) {
+ return mPlaybackInfo;
+ }
+ }
+
+ int getRatingType() {
+ return mRatingType;
+ }
+
+ PendingIntent getSessionActivity() {
+ return mSessionActivity;
+ }
+
private static class MyPlaybackListener implements MediaPlayerInterface.PlaybackListener {
private final WeakReference<MediaSession2Impl> mSession;
private final MediaPlayerInterface mPlayer;
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
index 3391236..114cffb 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
@@ -16,6 +16,7 @@
package com.android.media;
+import android.app.PendingIntent;
import android.content.Context;
import android.media.MediaController2;
import android.media.MediaController2.PlaybackInfo;
@@ -133,10 +134,36 @@
// TODO(jaewan): Should we protect getting playback state?
final PlaybackState2 state = session.getInstance().getPlaybackState();
final Bundle playbackStateBundle = (state != null) ? state.toBundle() : null;
+ final Bundle playbackInfoBundle =
+ ((PlaybackInfoImpl) session.getPlaybackInfo().getProvider()).toBundle();
+ final PlaylistParams params = session.getInstance().getPlaylistParams();
+ final Bundle paramsBundle = (params != null) ? params.toBundle() : null;
+ final int ratingType = session.getRatingType();
+ final PendingIntent sessionActivity = session.getSessionActivity();
+ final List<MediaItem2> playlist = session.getInstance().getPlaylist();
+ final List<Bundle> playlistBundle = new ArrayList<>();
+ if (playlist != null) {
+ // TODO(jaewan): Find a way to avoid concurrent modification exception.
+ for (int i = 0; i < playlist.size(); i++) {
+ final MediaItem2 item = playlist.get(i);
+ if (item != null) {
+ final Bundle itemBundle = item.toBundle();
+ if (itemBundle != null) {
+ playlistBundle.add(itemBundle);
+ }
+ }
+ }
+ }
+ // Double check if session is still there, because close() can be called in another
+ // thread.
+ if (mSession.get() == null) {
+ return;
+ }
try {
callback.onConnected(MediaSession2Stub.this,
- allowedCommands.toBundle(), playbackStateBundle);
+ allowedCommands.toBundle(), playbackStateBundle, playbackInfoBundle,
+ paramsBundle, playlistBundle, ratingType, sessionActivity);
} catch (RemoteException e) {
// Controller may be died prematurely.
// TODO(jaewan): Handle here.
diff --git a/packages/MediaComponents/test/AndroidManifest.xml b/packages/MediaComponents/test/AndroidManifest.xml
index 30bac87..48e4292 100644
--- a/packages/MediaComponents/test/AndroidManifest.xml
+++ b/packages/MediaComponents/test/AndroidManifest.xml
@@ -20,14 +20,7 @@
<application android:label="Media API Test">
<uses-library android:name="android.test.runner" />
- <activity android:name="android.widget2.VideoView2TestActivity"
- android:configChanges="keyboardHidden|orientation|screenSize"
- android:label="VideoView2TestActivity">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
- </intent-filter>
- </activity>
+ <activity android:name="android.media.MockActivity" />
<!-- Keep the test services synced together with the TestUtils.java -->
<service android:name="android.media.MockMediaSessionService2">
diff --git a/packages/MediaComponents/test/src/android/media/MediaController2Test.java b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
index f1fdf2e..139cc65 100644
--- a/packages/MediaComponents/test/src/android/media/MediaController2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
@@ -16,7 +16,9 @@
package android.media;
+import android.app.PendingIntent;
import android.content.Context;
+import android.content.Intent;
import android.media.MediaPlayerInterface.PlaybackListener;
import android.media.MediaSession2.Command;
import android.media.MediaSession2.ControllerInfo;
@@ -56,7 +58,9 @@
@FlakyTest
public class MediaController2Test extends MediaSession2TestBase {
private static final String TAG = "MediaController2Test";
+ private static final int DEFAULT_RATING_TYPE = Rating2.RATING_5_STARS;
+ PendingIntent mIntent;
MediaSession2 mSession;
MediaController2 mController;
MockPlayer mPlayer;
@@ -65,10 +69,15 @@
@Override
public void setUp() throws Exception {
super.setUp();
+ final Intent sessionActivity = new Intent(mContext, MockActivity.class);
// Create this test specific MediaSession2 to use our own Handler.
+ mIntent = PendingIntent.getActivity(mContext, 0, sessionActivity, 0);
+
mPlayer = new MockPlayer(1);
mSession = new MediaSession2.Builder(mContext, mPlayer)
.setSessionCallback(sHandlerExecutor, new SessionCallback(mContext))
+ .setRatingType(DEFAULT_RATING_TYPE)
+ .setSessionActivity(mIntent)
.setId(TAG).build();
mController = createController(mSession.getToken());
TestServiceRegistry.getInstance().setHandler(sHandler);
@@ -199,6 +208,18 @@
}
@Test
+ public void testGetRatingType() throws InterruptedException {
+ assertEquals(DEFAULT_RATING_TYPE, mController.getRatingType());
+ }
+
+ @Test
+ public void testGetSessionActivity() throws InterruptedException {
+ PendingIntent sessionActivity = mController.getSessionActivity();
+ assertEquals(mContext.getPackageName(), sessionActivity.getCreatorPackage());
+ assertEquals(Process.myUid(), sessionActivity.getCreatorUid());
+ }
+
+ @Test
public void testGetSetPlaylistParams() throws Exception {
final PlaylistParams params = new PlaylistParams(mContext,
PlaylistParams.REPEAT_MODE_ALL,
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2Test.java b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
index 43a6c2c..b00633b 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
@@ -27,6 +27,9 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
+import android.content.Context;
+import android.media.AudioManager;
+import android.media.MediaController2.PlaybackInfo;
import android.media.MediaPlayerInterface.PlaybackListener;
import android.media.MediaSession2.Builder;
import android.media.MediaSession2.Command;
@@ -99,17 +102,15 @@
@Test
public void testSetPlayer() throws Exception {
- sHandler.postAndSync(() -> {
- MockPlayer player = new MockPlayer(0);
- // Test if setPlayer doesn't crash with various situations.
- mSession.setPlayer(mPlayer);
- mSession.setPlayer(player);
- mSession.close();
- });
+ MockPlayer player = new MockPlayer(0);
+ // Test if setPlayer doesn't crash with various situations.
+ mSession.setPlayer(mPlayer);
+ mSession.setPlayer(player);
+ mSession.close();
}
@Test
- public void testSetPlayerWithVolumeProvider() throws Exception {
+ public void testSetPlayer_playbackInfo() throws Exception {
MockPlayer player = new MockPlayer(0);
AudioAttributes attrs = new AudioAttributes.Builder()
.setContentType(CONTENT_TYPE_MUSIC)
@@ -125,7 +126,7 @@
final CountDownLatch latch = new CountDownLatch(1);
final TestControllerCallbackInterface callback = new TestControllerCallbackInterface() {
@Override
- public void onPlaybackInfoChanged(MediaController2.PlaybackInfo info) {
+ public void onPlaybackInfoChanged(PlaybackInfo info) {
assertEquals(MediaController2.PlaybackInfo.PLAYBACK_TYPE_REMOTE,
info.getPlaybackType());
assertEquals(attrs, info.getAudioAttributes());
@@ -136,18 +137,30 @@
}
};
+ mSession.setPlayer(player);
+
final MediaController2 controller = createController(mSession.getToken(), true, callback);
- assertNull(controller.getPlaybackInfo());
+ PlaybackInfo info = controller.getPlaybackInfo();
+ assertNotNull(info);
+ assertEquals(PlaybackInfo.PLAYBACK_TYPE_LOCAL, info.getPlaybackType());
+ assertEquals(attrs, info.getAudioAttributes());
+ AudioManager manager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ int localVolumeControlType = manager.isVolumeFixed()
+ ? VolumeProvider2.VOLUME_CONTROL_FIXED : VolumeProvider2.VOLUME_CONTROL_ABSOLUTE;
+ assertEquals(localVolumeControlType, info.getControlType());
+ assertEquals(manager.getStreamMaxVolume(AudioManager.STREAM_MUSIC), info.getMaxVolume());
+ assertEquals(manager.getStreamVolume(AudioManager.STREAM_MUSIC), info.getCurrentVolume());
mSession.setPlayer(player, volumeProvider);
assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
- MediaController2.PlaybackInfo infoOut = controller.getPlaybackInfo();
- assertEquals(MediaController2.PlaybackInfo.PLAYBACK_TYPE_REMOTE, infoOut.getPlaybackType());
- assertEquals(attrs, infoOut.getAudioAttributes());
- assertEquals(volumeControlType, infoOut.getPlaybackType());
- assertEquals(maxVolume, infoOut.getMaxVolume());
- assertEquals(currentVolume, infoOut.getCurrentVolume());
+ info = controller.getPlaybackInfo();
+ assertNotNull(info);
+ assertEquals(PlaybackInfo.PLAYBACK_TYPE_REMOTE, info.getPlaybackType());
+ assertEquals(attrs, info.getAudioAttributes());
+ assertEquals(volumeControlType, info.getControlType());
+ assertEquals(maxVolume, info.getMaxVolume());
+ assertEquals(currentVolume, info.getCurrentVolume());
}
@Test
diff --git a/packages/MediaComponents/test/src/android/media/MockActivity.java b/packages/MediaComponents/test/src/android/media/MockActivity.java
new file mode 100644
index 0000000..4627530
--- /dev/null
+++ b/packages/MediaComponents/test/src/android/media/MockActivity.java
@@ -0,0 +1,22 @@
+/*
+ * 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.app.Activity;
+
+public class MockActivity extends Activity {
+}