[go: nahoru, domu]

blob: 700440e5128891376b05c78beb53df5f12762f17 [file] [log] [blame]
/*
* 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 androidx.media2.test.service.tests;
import static androidx.media.MediaSessionManager.RemoteUserInfo.LEGACY_CONTROLLER;
import static androidx.media2.SessionResult.RESULT_ERROR_INVALID_STATE;
import static androidx.media2.SessionResult.RESULT_SUCCESS;
import static androidx.media2.test.common.CommonConstants.CLIENT_PACKAGE_NAME;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.support.v4.media.MediaDescriptionCompat;
import android.support.v4.media.RatingCompat;
import android.support.v4.media.session.MediaControllerCompat;
import android.support.v4.media.session.MediaSessionCompat.QueueItem;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.media.AudioAttributesCompat;
import androidx.media.VolumeProviderCompat;
import androidx.media2.MediaItem;
import androidx.media2.MediaSession;
import androidx.media2.MediaSession.ControllerInfo;
import androidx.media2.MediaSession.SessionCallback;
import androidx.media2.MediaUtils;
import androidx.media2.Rating;
import androidx.media2.SessionCommand;
import androidx.media2.SessionCommandGroup;
import androidx.media2.SessionPlayer;
import androidx.media2.SessionResult;
import androidx.media2.test.common.MockActivity;
import androidx.media2.test.common.PollingCheck;
import androidx.media2.test.common.TestUtils;
import androidx.media2.test.common.TestUtils.SyncHandler;
import androidx.media2.test.service.MediaTestUtils;
import androidx.media2.test.service.MockPlayer;
import androidx.media2.test.service.MockRemotePlayer;
import androidx.media2.test.service.RemoteMediaControllerCompat;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Tests {@link SessionCallback} working with {@link MediaControllerCompat}.
*/
@RunWith(AndroidJUnit4.class)
@LargeTest
public class MediaSessionCallbackTestWithMediaControllerCompat extends MediaSessionTestBase {
private static final String TAG = "MediaSessionCallbackTestWithMediaControllerCompat";
private static final String EXPECTED_CONTROLLER_PACKAGE_NAME =
(Build.VERSION.SDK_INT < 21 || Build.VERSION.SDK_INT >= 24)
? CLIENT_PACKAGE_NAME : LEGACY_CONTROLLER;
PendingIntent mIntent;
MediaSession mSession;
RemoteMediaControllerCompat mController;
MockPlayer mPlayer;
AudioManager mAudioManager;
@Before
@Override
public void setUp() throws Exception {
super.setUp();
final Intent sessionActivity = new Intent(mContext, MockActivity.class);
// Create this test specific MediaSession to use our own Handler.
mIntent = PendingIntent.getActivity(mContext, 0, sessionActivity, 0);
mPlayer = new MockPlayer(1);
if (mSession != null && !mSession.isClosed()) {
mSession.close();
}
mSession = new MediaSession.Builder(mContext, mPlayer)
.setId(TAG)
.setSessionCallback(sHandlerExecutor, new SessionCallback() {
@Override
public SessionCommandGroup onConnect(MediaSession session,
ControllerInfo controller) {
if (EXPECTED_CONTROLLER_PACKAGE_NAME.equals(controller.getPackageName())) {
return super.onConnect(session, controller);
}
return null;
}
@Override
public MediaItem onCreateMediaItem(MediaSession session,
ControllerInfo controller, String mediaId) {
return MediaTestUtils.createMediaItem(mediaId);
}
})
.setSessionActivity(mIntent)
.build();
mController = new RemoteMediaControllerCompat(
mContext, mSession.getSessionCompat().getSessionToken(), true);
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
}
@After
@Override
public void cleanUp() throws Exception {
super.cleanUp();
if (mSession != null) {
mSession.close();
}
if (mController != null) {
mController.cleanUp();
mController = null;
}
}
@Test
public void testPlay() {
prepareLooper();
mController.getTransportControls().play();
try {
assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} catch (InterruptedException e) {
fail(e.getMessage());
}
assertTrue(mPlayer.mPlayCalled);
}
@Test
public void testPause() {
prepareLooper();
mController.getTransportControls().pause();
try {
assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} catch (InterruptedException e) {
fail(e.getMessage());
}
assertTrue(mPlayer.mPauseCalled);
}
@Test
public void testStop() {
prepareLooper();
// MediaControllerCompat#stop() will call MediaSession#pause() and MediaSession#seekTo(0).
// Therefore, the latch's initial count is 2.
MockPlayer player = new MockPlayer(2);
player.mCurrentPosition = 1530;
mSession.updatePlayer(player);
mController.getTransportControls().stop();
try {
assertTrue(player.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} catch (InterruptedException e) {
fail(e.getMessage());
}
assertTrue(player.mPauseCalled);
assertTrue(player.mSeekToCalled);
assertEquals(0, player.mSeekPosition);
}
@Test
public void testPrepare() {
prepareLooper();
mController.getTransportControls().prepare();
try {
assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} catch (InterruptedException e) {
fail(e.getMessage());
}
assertTrue(mPlayer.mPrepareCalled);
}
@Test
public void testSeekTo() {
prepareLooper();
final long seekPosition = 12125L;
mController.getTransportControls().seekTo(seekPosition);
try {
assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} catch (InterruptedException e) {
fail(e.getMessage());
}
assertTrue(mPlayer.mSeekToCalled);
assertEquals(seekPosition, mPlayer.mSeekPosition);
}
@Test
public void testSetPlaybackSpeed() {
prepareLooper();
final float testSpeed = 2.0f;
mController.getTransportControls().setPlaybackSpeed(testSpeed);
try {
assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} catch (InterruptedException e) {
fail(e.getMessage());
}
assertTrue(mPlayer.mSetPlaybackSpeedCalled);
assertEquals(testSpeed, mPlayer.mPlaybackSpeed, 0.0f);
}
@Test
public void testAddQueueItem() throws InterruptedException {
prepareLooper();
final int playlistSize = 10;
List<MediaItem> playlist = MediaTestUtils.createPlaylist(playlistSize);
mPlayer.mPlaylist = playlist;
mPlayer.notifyPlaylistChanged();
// Wait some time for setting the playlist.
Thread.sleep(TIMEOUT_MS);
// Prepare an item to add.
final String mediaId = "media_id";
MediaDescriptionCompat desc = new MediaDescriptionCompat.Builder()
.setMediaId(mediaId)
.build();
mController.addQueueItem(desc);
assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertTrue(mPlayer.mAddPlaylistItemCalled);
assertEquals(mediaId, mPlayer.mItem.getMediaId());
}
@Test
public void testAddQueueItemWithIndex() throws InterruptedException {
prepareLooper();
final int playlistSize = 10;
List<MediaItem> playlist = MediaTestUtils.createPlaylist(playlistSize);
mPlayer.mPlaylist = playlist;
mPlayer.notifyPlaylistChanged();
// Wait some time for setting the playlist.
Thread.sleep(TIMEOUT_MS);
// Prepare an item to add.
final int testIndex = 0;
final String mediaId = "media_id";
MediaDescriptionCompat desc = new MediaDescriptionCompat.Builder()
.setMediaId(mediaId)
.build();
mController.addQueueItem(desc, testIndex);
assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertTrue(mPlayer.mAddPlaylistItemCalled);
assertEquals(testIndex, mPlayer.mIndex);
assertEquals(mediaId, mPlayer.mItem.getMediaId());
}
@Test
public void testRemoveQueueItem() throws InterruptedException {
prepareLooper();
final int playlistSize = 10;
List<MediaItem> playlist = MediaTestUtils.createPlaylist(playlistSize);
mPlayer.mPlaylist = playlist;
mPlayer.notifyPlaylistChanged();
// Wait some time for setting the playlist.
Thread.sleep(TIMEOUT_MS);
// Select an item to remove.
final int targetIndex = 3;
final MediaItem targetItem = playlist.get(targetIndex);
MediaDescriptionCompat desc = new MediaDescriptionCompat.Builder()
.setMediaId(targetItem.getMediaId())
.build();
mController.removeQueueItem(desc);
assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertTrue(mPlayer.mRemovePlaylistItemCalled);
assertEquals(targetIndex, mPlayer.mIndex);
}
@Test
public void testSkipToPrevious() throws InterruptedException {
prepareLooper();
mController.getTransportControls().skipToPrevious();
assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertTrue(mPlayer.mSkipToPreviousItemCalled);
}
@Test
public void testSkipToNext() throws InterruptedException {
prepareLooper();
mController.getTransportControls().skipToNext();
assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertTrue(mPlayer.mSkipToNextItemCalled);
}
@Test
public void testSkipToQueueItem() throws InterruptedException {
prepareLooper();
final int playlistSize = 10;
List<MediaItem> playlist = MediaTestUtils.createPlaylist(playlistSize);
mPlayer.mPlaylist = playlist;
mPlayer.notifyPlaylistChanged();
// Wait some time for setting the playlist.
Thread.sleep(TIMEOUT_MS);
// Get Queue from local MediaControllerCompat.
List<QueueItem> queue = mSession.getSessionCompat().getController().getQueue();
final int targetIndex = 3;
mController.getTransportControls().skipToQueueItem(queue.get(targetIndex).getQueueId());
assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertTrue(mPlayer.mSkipToPlaylistItemCalled);
assertEquals(targetIndex, mPlayer.mIndex);
}
@Test
public void testSetShuffleMode() throws InterruptedException {
prepareLooper();
final int testShuffleMode = SessionPlayer.SHUFFLE_MODE_GROUP;
mController.getTransportControls().setShuffleMode(testShuffleMode);
assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertTrue(mPlayer.mSetShuffleModeCalled);
assertEquals(testShuffleMode, mPlayer.mShuffleMode);
}
@Test
public void testSetRepeatMode() throws InterruptedException {
prepareLooper();
final int testRepeatMode = SessionPlayer.REPEAT_MODE_GROUP;
mController.getTransportControls().setRepeatMode(testRepeatMode);
assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertTrue(mPlayer.mSetRepeatModeCalled);
assertEquals(testRepeatMode, mPlayer.mRepeatMode);
}
@Test
public void testSetVolumeTo() throws Exception {
prepareLooper();
final int maxVolume = 100;
final int currentVolume = 23;
final int volumeControlType = VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE;
MockRemotePlayer remotePlayer =
new MockRemotePlayer(volumeControlType, maxVolume, currentVolume);
mSession.updatePlayer(remotePlayer);
final int targetVolume = 50;
mController.setVolumeTo(targetVolume, 0 /* flags */);
assertTrue(remotePlayer.mLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertTrue(remotePlayer.mSetVolumeToCalled);
assertEquals(targetVolume, (int) remotePlayer.mCurrentVolume);
}
@Test
public void testAdjustVolume() throws Exception {
prepareLooper();
final int maxVolume = 100;
final int currentVolume = 23;
final int volumeControlType = VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE;
MockRemotePlayer remotePlayer =
new MockRemotePlayer(volumeControlType, maxVolume, currentVolume);
mSession.updatePlayer(remotePlayer);
final int direction = AudioManager.ADJUST_RAISE;
mController.adjustVolume(direction, 0 /* flags */);
assertTrue(remotePlayer.mLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertTrue(remotePlayer.mAdjustVolumeCalled);
assertEquals(direction, remotePlayer.mDirection);
}
@Test
public void testSetVolumeWithLocalVolume() throws Exception {
prepareLooper();
if (Build.VERSION.SDK_INT >= 21 && mAudioManager.isVolumeFixed()) {
// This test is not eligible for this device.
return;
}
// Here, we intentionally choose STREAM_ALARM in order not to consider
// 'Do Not Disturb' or 'Volume limit'.
final int stream = AudioManager.STREAM_ALARM;
final int maxVolume = mAudioManager.getStreamMaxVolume(stream);
final int minVolume =
Build.VERSION.SDK_INT >= 28 ? mAudioManager.getStreamMinVolume(stream) : 0;
Log.d(TAG, "maxVolume=" + maxVolume + ", minVolume=" + minVolume);
if (maxVolume <= minVolume) {
return;
}
// Set stream of the session.
AudioAttributesCompat attrs = new AudioAttributesCompat.Builder()
.setLegacyStreamType(stream)
.build();
mPlayer.setAudioAttributes(attrs);
mSession.updatePlayer(mPlayer);
final int originalVolume = mAudioManager.getStreamVolume(stream);
final int targetVolume = originalVolume == minVolume
? originalVolume + 1 : originalVolume - 1;
Log.d(TAG, "originalVolume=" + originalVolume + ", targetVolume=" + targetVolume);
mController.setVolumeTo(targetVolume, AudioManager.FLAG_SHOW_UI);
new PollingCheck(TIMEOUT_MS) {
@Override
protected boolean check() {
return targetVolume == mAudioManager.getStreamVolume(stream);
}
}.run();
// Set back to original volume.
mAudioManager.setStreamVolume(stream, originalVolume, 0 /* flags */);
}
@Test
public void testAdjustVolumeWithLocalVolume() throws Exception {
prepareLooper();
if (Build.VERSION.SDK_INT >= 21 && mAudioManager.isVolumeFixed()) {
// This test is not eligible for this device.
return;
}
// Here, we intentionally choose STREAM_ALARM in order not to consider
// 'Do Not Disturb' or 'Volume limit'.
final int stream = AudioManager.STREAM_ALARM;
final int maxVolume = mAudioManager.getStreamMaxVolume(stream);
final int minVolume =
Build.VERSION.SDK_INT >= 28 ? mAudioManager.getStreamMinVolume(stream) : 0;
Log.d(TAG, "maxVolume=" + maxVolume + ", minVolume=" + minVolume);
if (maxVolume <= minVolume) {
return;
}
// Set stream of the session.
AudioAttributesCompat attrs = new AudioAttributesCompat.Builder()
.setLegacyStreamType(stream)
.build();
mPlayer.setAudioAttributes(attrs);
mSession.updatePlayer(mPlayer);
final int originalVolume = mAudioManager.getStreamVolume(stream);
final int direction = originalVolume == minVolume
? AudioManager.ADJUST_RAISE : AudioManager.ADJUST_LOWER;
final int targetVolume = originalVolume + direction;
Log.d(TAG, "originalVolume=" + originalVolume + ", targetVolume=" + targetVolume);
mController.adjustVolume(direction, AudioManager.FLAG_SHOW_UI);
new PollingCheck(TIMEOUT_MS) {
@Override
protected boolean check() {
return targetVolume == mAudioManager.getStreamVolume(stream);
}
}.run();
// Set back to original volume.
mAudioManager.setStreamVolume(stream, originalVolume, 0 /* flags */);
}
@Test
public void testSendCommand() throws InterruptedException {
prepareLooper();
// TODO(jaewan): Need to revisit with the permission.
final String testCommand = "test_command";
final Bundle testArgs = new Bundle();
testArgs.putString("args", "test_args");
final CountDownLatch latch = new CountDownLatch(1);
final SessionCallback callback = new SessionCallback() {
@Override
public SessionCommandGroup onConnect(@NonNull MediaSession session,
@NonNull ControllerInfo controller) {
SessionCommandGroup commands = super.onConnect(session, controller);
commands.addCommand(new SessionCommand(testCommand, null));
return commands;
}
@Override
public SessionResult onCustomCommand(MediaSession session,
ControllerInfo controller, SessionCommand customCommand, Bundle args) {
assertEquals(EXPECTED_CONTROLLER_PACKAGE_NAME, controller.getPackageName());
assertEquals(testCommand, customCommand.getCustomCommand());
assertTrue(TestUtils.equals(testArgs, args));
latch.countDown();
return new SessionResult(RESULT_SUCCESS, null);
}
};
mSession.close();
mSession = new MediaSession.Builder(mContext, mPlayer)
.setSessionCallback(sHandlerExecutor, callback).setId(TAG).build();
final RemoteMediaControllerCompat controller = new RemoteMediaControllerCompat(
mContext, mSession.getSessionCompat().getSessionToken(), true);
controller.sendCommand(testCommand, testArgs, null);
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
@Test
public void testControllerCallback_sessionRejects() throws Exception {
prepareLooper();
final SessionCallback sessionCallback = new SessionCallback() {
@Override
public SessionCommandGroup onConnect(MediaSession session,
ControllerInfo controller) {
return null;
}
};
sHandler.postAndSync(new Runnable() {
@Override
public void run() {
mSession.close();
mSession = new MediaSession.Builder(mContext, mPlayer)
.setSessionCallback(sHandlerExecutor, sessionCallback).build();
}
});
// Session will not accept the controller's commands.
RemoteMediaControllerCompat controller = new RemoteMediaControllerCompat(
mContext, mSession.getSessionCompat().getSessionToken(), true);
controller.getTransportControls().play();
assertFalse(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
@Test
public void testFastForward() throws InterruptedException {
prepareLooper();
final CountDownLatch latch = new CountDownLatch(1);
final SessionCallback callback = new SessionCallback() {
@Override
public int onFastForward(MediaSession session, ControllerInfo controller) {
assertEquals(EXPECTED_CONTROLLER_PACKAGE_NAME, controller.getPackageName());
latch.countDown();
return RESULT_SUCCESS;
}
};
try (MediaSession session = new MediaSession.Builder(mContext, mPlayer)
.setSessionCallback(sHandlerExecutor, callback)
.setId("testFastForward").build()) {
RemoteMediaControllerCompat controller = new RemoteMediaControllerCompat(
mContext, session.getSessionCompat().getSessionToken(), true);
controller.getTransportControls().fastForward();
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
}
@Test
public void testRewind() throws InterruptedException {
prepareLooper();
final CountDownLatch latch = new CountDownLatch(1);
final SessionCallback callback = new SessionCallback() {
@Override
public int onRewind(MediaSession session, ControllerInfo controller) {
assertEquals(EXPECTED_CONTROLLER_PACKAGE_NAME, controller.getPackageName());
latch.countDown();
return RESULT_SUCCESS;
}
};
try (MediaSession session = new MediaSession.Builder(mContext, mPlayer)
.setSessionCallback(sHandlerExecutor, callback)
.setId("testRewind").build()) {
RemoteMediaControllerCompat controller = new RemoteMediaControllerCompat(
mContext, session.getSessionCompat().getSessionToken(), true);
controller.getTransportControls().rewind();
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
}
@Test
public void testPlayFromSearch() throws InterruptedException {
prepareLooper();
final String request = "random query";
final Bundle bundle = new Bundle();
bundle.putString("key", "value");
final CountDownLatch latch = new CountDownLatch(1);
final SessionCallback callback = new SessionCallback() {
@Override
public int onPlayFromSearch(MediaSession session, ControllerInfo controller,
String query, Bundle extras) {
super.onPlayFromSearch(session, controller, query, extras);
assertEquals(EXPECTED_CONTROLLER_PACKAGE_NAME, controller.getPackageName());
assertEquals(request, query);
assertTrue(TestUtils.equals(bundle, extras));
latch.countDown();
return RESULT_SUCCESS;
}
};
try (MediaSession session = new MediaSession.Builder(mContext, mPlayer)
.setSessionCallback(sHandlerExecutor, callback)
.setId("testPlayFromSearch").build()) {
RemoteMediaControllerCompat controller = new RemoteMediaControllerCompat(
mContext, session.getSessionCompat().getSessionToken(), true);
controller.getTransportControls().playFromSearch(request, bundle);
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
}
@Test
public void testPlayFromUri() throws InterruptedException {
prepareLooper();
final Uri request = Uri.parse("foo://boo");
final Bundle bundle = new Bundle();
bundle.putString("key", "value");
final CountDownLatch latch = new CountDownLatch(1);
final SessionCallback callback = new SessionCallback() {
@Override
public int onPlayFromUri(MediaSession session, ControllerInfo controller, Uri uri,
Bundle extras) {
assertEquals(EXPECTED_CONTROLLER_PACKAGE_NAME, controller.getPackageName());
assertEquals(request, uri);
assertTrue(TestUtils.equals(bundle, extras));
latch.countDown();
return RESULT_SUCCESS;
}
};
try (MediaSession session = new MediaSession.Builder(mContext, mPlayer)
.setSessionCallback(sHandlerExecutor, callback)
.setId("testPlayFromUri").build()) {
RemoteMediaControllerCompat controller = new RemoteMediaControllerCompat(
mContext, session.getSessionCompat().getSessionToken(), true);
controller.getTransportControls().playFromUri(request, bundle);
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
}
@Test
public void testPlayFromMediaId() throws InterruptedException {
prepareLooper();
final String request = "media_id";
final Bundle bundle = new Bundle();
bundle.putString("key", "value");
final CountDownLatch latch = new CountDownLatch(1);
final SessionCallback callback = new SessionCallback() {
@Override
public int onPlayFromMediaId(MediaSession session, ControllerInfo controller,
String mediaId, Bundle extras) {
assertEquals(EXPECTED_CONTROLLER_PACKAGE_NAME, controller.getPackageName());
assertEquals(request, mediaId);
assertTrue(TestUtils.equals(bundle, extras));
latch.countDown();
return RESULT_SUCCESS;
}
};
try (MediaSession session = new MediaSession.Builder(mContext, mPlayer)
.setSessionCallback(sHandlerExecutor, callback)
.setId("testPlayFromMediaId").build()) {
RemoteMediaControllerCompat controller = new RemoteMediaControllerCompat(
mContext, session.getSessionCompat().getSessionToken(), true);
controller.getTransportControls().playFromMediaId(request, bundle);
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
}
@Test
public void testPrepareFromSearch() throws InterruptedException {
prepareLooper();
final String request = "random query";
final Bundle bundle = new Bundle();
bundle.putString("key", "value");
final CountDownLatch latch = new CountDownLatch(1);
final SessionCallback callback = new SessionCallback() {
@Override
public int onPrepareFromSearch(MediaSession session, ControllerInfo controller,
String query, Bundle extras) {
assertEquals(EXPECTED_CONTROLLER_PACKAGE_NAME, controller.getPackageName());
assertEquals(request, query);
assertTrue(TestUtils.equals(bundle, extras));
latch.countDown();
return RESULT_SUCCESS;
}
};
try (MediaSession session = new MediaSession.Builder(mContext, mPlayer)
.setSessionCallback(sHandlerExecutor, callback)
.setId("testPrepareFromSearch").build()) {
RemoteMediaControllerCompat controller = new RemoteMediaControllerCompat(
mContext, session.getSessionCompat().getSessionToken(), true);
controller.getTransportControls().prepareFromSearch(request, bundle);
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
}
@Test
public void testPrepareFromUri() throws InterruptedException {
prepareLooper();
final Uri request = Uri.parse("foo://boo");
final Bundle bundle = new Bundle();
bundle.putString("key", "value");
final CountDownLatch latch = new CountDownLatch(1);
final SessionCallback callback = new SessionCallback() {
@Override
public int onPrepareFromUri(MediaSession session, ControllerInfo controller, Uri uri,
Bundle extras) {
assertEquals(EXPECTED_CONTROLLER_PACKAGE_NAME, controller.getPackageName());
assertEquals(request, uri);
assertTrue(TestUtils.equals(bundle, extras));
latch.countDown();
return RESULT_SUCCESS;
}
};
try (MediaSession session = new MediaSession.Builder(mContext, mPlayer)
.setSessionCallback(sHandlerExecutor, callback)
.setId("testPrepareFromUri").build()) {
RemoteMediaControllerCompat controller = new RemoteMediaControllerCompat(
mContext, session.getSessionCompat().getSessionToken(), true);
controller.getTransportControls().prepareFromUri(request, bundle);
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
}
@Test
public void testPrepareFromMediaId() throws InterruptedException {
prepareLooper();
final String request = "media_id";
final Bundle bundle = new Bundle();
bundle.putString("key", "value");
final CountDownLatch latch = new CountDownLatch(1);
final SessionCallback callback = new SessionCallback() {
@Override
public int onPrepareFromMediaId(MediaSession session, ControllerInfo controller,
String mediaId, Bundle extras) {
assertEquals(EXPECTED_CONTROLLER_PACKAGE_NAME, controller.getPackageName());
assertEquals(request, mediaId);
assertTrue(TestUtils.equals(bundle, extras));
latch.countDown();
return RESULT_SUCCESS;
}
};
try (MediaSession session = new MediaSession.Builder(mContext, mPlayer)
.setSessionCallback(sHandlerExecutor, callback)
.setId("testPrepareFromMediaId").build()) {
RemoteMediaControllerCompat controller = new RemoteMediaControllerCompat(
mContext, session.getSessionCompat().getSessionToken(), true);
controller.getTransportControls().prepareFromMediaId(request, bundle);
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
}
@Test
public void testSetRating() throws InterruptedException {
prepareLooper();
final int ratingType = RatingCompat.RATING_5_STARS;
final float ratingValue = 3.5f;
final RatingCompat rating = RatingCompat.newStarRating(ratingType, ratingValue);
final String mediaId = "media_id";
final CountDownLatch latch = new CountDownLatch(1);
final SessionCallback callback = new SessionCallback() {
@Override
public int onSetRating(MediaSession session, ControllerInfo controller,
String mediaIdOut, Rating ratingOut) {
assertEquals(EXPECTED_CONTROLLER_PACKAGE_NAME, controller.getPackageName());
assertEquals(mediaId, mediaIdOut);
assertEquals(MediaUtils.convertToRating(rating), ratingOut);
latch.countDown();
return RESULT_SUCCESS;
}
};
mPlayer.mCurrentMediaItem = MediaTestUtils.createMediaItem(mediaId);
try (MediaSession session = new MediaSession.Builder(mContext, mPlayer)
.setSessionCallback(sHandlerExecutor, callback)
.setId("testSetRating").build()) {
RemoteMediaControllerCompat controller = new RemoteMediaControllerCompat(
mContext, session.getSessionCompat().getSessionToken(), true);
controller.getTransportControls().setRating(rating);
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
}
@Test
public void testOnCommandCallback() throws InterruptedException {
prepareLooper();
final ArrayList<SessionCommand> commands = new ArrayList<>();
final CountDownLatch latchForPause = new CountDownLatch(1);
final SessionCallback callback = new SessionCallback() {
@Override
public int onCommandRequest(MediaSession session, ControllerInfo controllerInfo,
SessionCommand command) {
assertEquals(EXPECTED_CONTROLLER_PACKAGE_NAME, controllerInfo.getPackageName());
assertFalse(controllerInfo.isTrusted());
commands.add(command);
if (command.getCommandCode() == SessionCommand.COMMAND_CODE_PLAYER_PAUSE) {
latchForPause.countDown();
return RESULT_ERROR_INVALID_STATE;
}
return RESULT_SUCCESS;
}
};
sHandler.postAndSync(new Runnable() {
@Override
public void run() {
mSession.close();
mPlayer = new MockPlayer(1);
mSession = new MediaSession.Builder(mContext, mPlayer)
.setSessionCallback(sHandlerExecutor, callback).build();
}
});
RemoteMediaControllerCompat controller = new RemoteMediaControllerCompat(
mContext, mSession.getSessionCompat().getSessionToken(), true);
controller.getTransportControls().pause();
assertTrue(latchForPause.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertFalse(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertFalse(mPlayer.mPauseCalled);
assertEquals(1, commands.size());
assertEquals(SessionCommand.COMMAND_CODE_PLAYER_PAUSE,
(long) commands.get(0).getCommandCode());
controller.getTransportControls().play();
assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertTrue(mPlayer.mPlayCalled);
assertFalse(mPlayer.mPauseCalled);
assertEquals(2, commands.size());
assertEquals(SessionCommand.COMMAND_CODE_PLAYER_PLAY,
(long) commands.get(1).getCommandCode());
}
/**
* Test potential deadlock for calls between controller and session.
*/
@Test
@LargeTest
public void testDeadlock() throws InterruptedException {
prepareLooper();
sHandler.postAndSync(new Runnable() {
@Override
public void run() {
mSession.close();
mSession = null;
}
});
// Two more threads are needed not to block test thread nor test wide thread (sHandler).
final HandlerThread sessionThread = new HandlerThread("testDeadlock_session");
final HandlerThread testThread = new HandlerThread("testDeadlock_test");
sessionThread.start();
testThread.start();
final SyncHandler sessionHandler = new SyncHandler(sessionThread.getLooper());
final Handler testHandler = new Handler(testThread.getLooper());
final CountDownLatch latch = new CountDownLatch(1);
try {
final MockPlayer player = new MockPlayer(0);
sessionHandler.postAndSync(new Runnable() {
@Override
public void run() {
mSession = new MediaSession.Builder(mContext, mPlayer)
.setSessionCallback(sHandlerExecutor, new SessionCallback() {})
.setId("testDeadlock").build();
}
});
final RemoteMediaControllerCompat controller = new RemoteMediaControllerCompat(
mContext, mSession.getSessionCompat().getSessionToken(), true);
testHandler.post(new Runnable() {
@Override
public void run() {
final int state = SessionPlayer.PLAYER_STATE_ERROR;
for (int i = 0; i < 100; i++) {
// triggers call from session to controller.
player.notifyPlayerStateChanged(state);
// triggers call from controller to session.
controller.getTransportControls().play();
// Repeat above
player.notifyPlayerStateChanged(state);
controller.getTransportControls().pause();
player.notifyPlayerStateChanged(state);
controller.getTransportControls().stop();
player.notifyPlayerStateChanged(state);
controller.getTransportControls().skipToNext();
player.notifyPlayerStateChanged(state);
controller.getTransportControls().skipToPrevious();
}
// This may hang if deadlock happens.
latch.countDown();
}
});
assertTrue(latch.await(3, TimeUnit.SECONDS));
} finally {
if (mSession != null) {
sessionHandler.postAndSync(new Runnable() {
@Override
public void run() {
// Clean up here because sessionHandler will be removed afterwards.
mSession.close();
mSession = null;
}
});
}
if (Build.VERSION.SDK_INT >= 18) {
sessionThread.quitSafely();
testThread.quitSafely();
} else {
sessionThread.quit();
testThread.quit();
}
}
}
@Test
@LargeTest
public void testControllerAfterSessionIsGone() throws InterruptedException {
prepareLooper();
mSession.close();
testSessionCallbackIsNotCalled();
// Ensure that the controller cannot use newly create session with the same ID.
// Recreated session has different session stub, so previously created controller
// shouldn't be available.
mSession = new MediaSession.Builder(mContext, mPlayer)
.setSessionCallback(sHandlerExecutor, new SessionCallback() {})
.setId(TAG)
.build();
testSessionCallbackIsNotCalled();
}
void testSessionCallbackIsNotCalled() throws InterruptedException {
mController.getTransportControls().play();
assertFalse(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
}