Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package android.media.cts;
     17 
     18 import static android.media.AudioAttributes.USAGE_GAME;
     19 import static android.media.cts.Utils.compareRemoteUserInfo;
     20 
     21 import android.app.PendingIntent;
     22 import android.content.ComponentName;
     23 import android.content.Context;
     24 import android.content.Intent;
     25 import android.media.AudioAttributes;
     26 import android.media.AudioManager;
     27 import android.media.MediaDescription;
     28 import android.media.MediaMetadata;
     29 import android.media.Rating;
     30 import android.media.VolumeProvider;
     31 import android.media.session.MediaController;
     32 import android.media.session.MediaSession;
     33 import android.media.session.MediaSession.QueueItem;
     34 import android.media.session.MediaSessionManager;
     35 import android.media.session.MediaSessionManager.RemoteUserInfo;
     36 import android.media.session.PlaybackState;
     37 import android.os.Bundle;
     38 import android.os.Handler;
     39 import android.os.Looper;
     40 import android.os.Parcel;
     41 import android.os.Process;
     42 import android.platform.test.annotations.AppModeFull;
     43 import android.test.AndroidTestCase;
     44 import android.view.KeyEvent;
     45 
     46 import java.util.ArrayList;
     47 import java.util.List;
     48 import java.util.concurrent.CountDownLatch;
     49 import java.util.concurrent.TimeUnit;
     50 
     51 @AppModeFull(reason = "TODO: evaluate and port to instant")
     52 public class MediaSessionTest extends AndroidTestCase {
     53     // The maximum time to wait for an operation that is expected to succeed.
     54     private static final long TIME_OUT_MS = 3000L;
     55     // The maximum time to wait for an operation that is expected to fail.
     56     private static final long WAIT_MS = 100L;
     57     private static final int MAX_AUDIO_INFO_CHANGED_CALLBACK_COUNT = 10;
     58     private static final String TEST_SESSION_TAG = "test-session-tag";
     59     private static final String TEST_KEY = "test-key";
     60     private static final String TEST_VALUE = "test-val";
     61     private static final String TEST_SESSION_EVENT = "test-session-event";
     62     private static final int TEST_CURRENT_VOLUME = 10;
     63     private static final int TEST_MAX_VOLUME = 11;
     64     private static final long TEST_QUEUE_ID = 12L;
     65     private static final long TEST_ACTION = 55L;
     66 
     67     private AudioManager mAudioManager;
     68     private Handler mHandler = new Handler(Looper.getMainLooper());
     69     private Object mWaitLock = new Object();
     70     private MediaControllerCallback mCallback = new MediaControllerCallback();
     71     private MediaSession mSession;
     72     private RemoteUserInfo mKeyDispatcherInfo;
     73 
     74     @Override
     75     protected void setUp() throws Exception {
     76         super.setUp();
     77         mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
     78         mSession = new MediaSession(getContext(), TEST_SESSION_TAG);
     79         mKeyDispatcherInfo = new MediaSessionManager.RemoteUserInfo(
     80                 getContext().getPackageName(), Process.myPid(), Process.myUid());
     81     }
     82 
     83     @Override
     84     protected void tearDown() throws Exception {
     85         // It is OK to call release() twice.
     86         mSession.release();
     87         super.tearDown();
     88     }
     89 
     90     /**
     91      * Tests that a session can be created and that all the fields are
     92      * initialized correctly.
     93      */
     94     public void testCreateSession() throws Exception {
     95         assertNotNull(mSession.getSessionToken());
     96         assertFalse("New session should not be active", mSession.isActive());
     97 
     98         // Verify by getting the controller and checking all its fields
     99         MediaController controller = mSession.getController();
    100         assertNotNull(controller);
    101         verifyNewSession(controller);
    102     }
    103 
    104     /**
    105      * Tests MediaSession.Token created in the constructor of MediaSession.
    106      */
    107     public void testSessionToken() throws Exception {
    108         MediaSession.Token sessionToken = mSession.getSessionToken();
    109 
    110         assertNotNull(sessionToken);
    111         assertEquals(0, sessionToken.describeContents());
    112 
    113         // Test writeToParcel
    114         Parcel p = Parcel.obtain();
    115         sessionToken.writeToParcel(p, 0);
    116         p.setDataPosition(0);
    117         MediaSession.Token token = MediaSession.Token.CREATOR.createFromParcel(p);
    118         assertEquals(token, sessionToken);
    119         p.recycle();
    120     }
    121 
    122     /**
    123      * Tests that the various configuration bits on a session get passed to the
    124      * controller.
    125      */
    126     public void testConfigureSession() throws Exception {
    127         MediaController controller = mSession.getController();
    128         controller.registerCallback(mCallback, mHandler);
    129         final MediaController.Callback callback = (MediaController.Callback) mCallback;
    130 
    131         synchronized (mWaitLock) {
    132             // test setExtras
    133             mCallback.resetLocked();
    134             final Bundle extras = new Bundle();
    135             extras.putString(TEST_KEY, TEST_VALUE);
    136             mSession.setExtras(extras);
    137             mWaitLock.wait(TIME_OUT_MS);
    138             assertTrue(mCallback.mOnExtraChangedCalled);
    139             // just call the callback once directly so it's marked as tested
    140             callback.onExtrasChanged(mCallback.mExtras);
    141 
    142             Bundle extrasOut = mCallback.mExtras;
    143             assertNotNull(extrasOut);
    144             assertEquals(TEST_VALUE, extrasOut.get(TEST_KEY));
    145 
    146             extrasOut = controller.getExtras();
    147             assertNotNull(extrasOut);
    148             assertEquals(TEST_VALUE, extrasOut.get(TEST_KEY));
    149 
    150             // test setFlags
    151             mSession.setFlags(5);
    152             assertEquals(5, controller.getFlags());
    153 
    154             // test setMetadata
    155             mCallback.resetLocked();
    156             MediaMetadata metadata =
    157                     new MediaMetadata.Builder().putString(TEST_KEY, TEST_VALUE).build();
    158             mSession.setMetadata(metadata);
    159             mWaitLock.wait(TIME_OUT_MS);
    160             assertTrue(mCallback.mOnMetadataChangedCalled);
    161             // just call the callback once directly so it's marked as tested
    162             callback.onMetadataChanged(mCallback.mMediaMetadata);
    163 
    164             MediaMetadata metadataOut = mCallback.mMediaMetadata;
    165             assertNotNull(metadataOut);
    166             assertEquals(TEST_VALUE, metadataOut.getString(TEST_KEY));
    167 
    168             metadataOut = controller.getMetadata();
    169             assertNotNull(metadataOut);
    170             assertEquals(TEST_VALUE, metadataOut.getString(TEST_KEY));
    171 
    172             // test setPlaybackState
    173             mCallback.resetLocked();
    174             PlaybackState state = new PlaybackState.Builder().setActions(TEST_ACTION).build();
    175             mSession.setPlaybackState(state);
    176             mWaitLock.wait(TIME_OUT_MS);
    177             assertTrue(mCallback.mOnPlaybackStateChangedCalled);
    178             // just call the callback once directly so it's marked as tested
    179             callback.onPlaybackStateChanged(mCallback.mPlaybackState);
    180 
    181             PlaybackState stateOut = mCallback.mPlaybackState;
    182             assertNotNull(stateOut);
    183             assertEquals(TEST_ACTION, stateOut.getActions());
    184 
    185             stateOut = controller.getPlaybackState();
    186             assertNotNull(stateOut);
    187             assertEquals(TEST_ACTION, stateOut.getActions());
    188 
    189             // test setQueue and setQueueTitle
    190             mCallback.resetLocked();
    191             List<QueueItem> queue = new ArrayList<>();
    192             QueueItem item = new QueueItem(new MediaDescription.Builder()
    193                     .setMediaId(TEST_VALUE).setTitle("title").build(), TEST_QUEUE_ID);
    194             queue.add(item);
    195             mSession.setQueue(queue);
    196             mWaitLock.wait(TIME_OUT_MS);
    197             assertTrue(mCallback.mOnQueueChangedCalled);
    198             // just call the callback once directly so it's marked as tested
    199             callback.onQueueChanged(mCallback.mQueue);
    200 
    201             mSession.setQueueTitle(TEST_VALUE);
    202             mWaitLock.wait(TIME_OUT_MS);
    203             assertTrue(mCallback.mOnQueueTitleChangedCalled);
    204 
    205             assertEquals(TEST_VALUE, mCallback.mTitle);
    206             assertEquals(queue.size(), mCallback.mQueue.size());
    207             assertEquals(TEST_QUEUE_ID, mCallback.mQueue.get(0).getQueueId());
    208             assertEquals(TEST_VALUE, mCallback.mQueue.get(0).getDescription().getMediaId());
    209 
    210             assertEquals(TEST_VALUE, controller.getQueueTitle());
    211             assertEquals(queue.size(), controller.getQueue().size());
    212             assertEquals(TEST_QUEUE_ID, controller.getQueue().get(0).getQueueId());
    213             assertEquals(TEST_VALUE, controller.getQueue().get(0).getDescription().getMediaId());
    214 
    215             mCallback.resetLocked();
    216             mSession.setQueue(null);
    217             mWaitLock.wait(TIME_OUT_MS);
    218             assertTrue(mCallback.mOnQueueChangedCalled);
    219             // just call the callback once directly so it's marked as tested
    220             callback.onQueueChanged(mCallback.mQueue);
    221 
    222             mSession.setQueueTitle(null);
    223             mWaitLock.wait(TIME_OUT_MS);
    224             assertTrue(mCallback.mOnQueueTitleChangedCalled);
    225             // just call the callback once directly so it's marked as tested
    226             callback.onQueueTitleChanged(mCallback.mTitle);
    227 
    228             assertNull(mCallback.mTitle);
    229             assertNull(mCallback.mQueue);
    230             assertNull(controller.getQueueTitle());
    231             assertNull(controller.getQueue());
    232 
    233             // test setSessionActivity
    234             Intent intent = new Intent("cts.MEDIA_SESSION_ACTION");
    235             PendingIntent pi = PendingIntent.getActivity(getContext(), 555, intent, 0);
    236             mSession.setSessionActivity(pi);
    237             assertEquals(pi, controller.getSessionActivity());
    238 
    239             // test setActivity
    240             mSession.setActive(true);
    241             assertTrue(mSession.isActive());
    242 
    243             // test sendSessionEvent
    244             mCallback.resetLocked();
    245             mSession.sendSessionEvent(TEST_SESSION_EVENT, extras);
    246             mWaitLock.wait(TIME_OUT_MS);
    247 
    248             assertTrue(mCallback.mOnSessionEventCalled);
    249             assertEquals(TEST_SESSION_EVENT, mCallback.mEvent);
    250             assertEquals(TEST_VALUE, mCallback.mExtras.getString(TEST_KEY));
    251             // just call the callback once directly so it's marked as tested
    252             callback.onSessionEvent(mCallback.mEvent, mCallback.mExtras);
    253 
    254             // test release
    255             mCallback.resetLocked();
    256             mSession.release();
    257             mWaitLock.wait(TIME_OUT_MS);
    258             assertTrue(mCallback.mOnSessionDestroyedCalled);
    259             // just call the callback once directly so it's marked as tested
    260             callback.onSessionDestroyed();
    261         }
    262     }
    263 
    264     /**
    265      * Test {@link MediaSession#setPlaybackToLocal} and {@link MediaSession#setPlaybackToRemote}.
    266      */
    267     public void testPlaybackToLocalAndRemote() throws Exception {
    268         MediaController controller = mSession.getController();
    269         controller.registerCallback(mCallback, mHandler);
    270 
    271         synchronized (mWaitLock) {
    272             // test setPlaybackToRemote, do this before testing setPlaybackToLocal
    273             // to ensure it switches correctly.
    274             mCallback.resetLocked();
    275             try {
    276                 mSession.setPlaybackToRemote(null);
    277                 fail("Expected IAE for setPlaybackToRemote(null)");
    278             } catch (IllegalArgumentException e) {
    279                 // expected
    280             }
    281             VolumeProvider vp = new VolumeProvider(VolumeProvider.VOLUME_CONTROL_FIXED,
    282                     TEST_MAX_VOLUME, TEST_CURRENT_VOLUME) {};
    283             mSession.setPlaybackToRemote(vp);
    284 
    285             MediaController.PlaybackInfo info = null;
    286             for (int i = 0; i < MAX_AUDIO_INFO_CHANGED_CALLBACK_COUNT; ++i) {
    287                 mCallback.mOnAudioInfoChangedCalled = false;
    288                 mWaitLock.wait(TIME_OUT_MS);
    289                 assertTrue(mCallback.mOnAudioInfoChangedCalled);
    290                 info = mCallback.mPlaybackInfo;
    291                 if (info != null && info.getCurrentVolume() == TEST_CURRENT_VOLUME
    292                         && info.getMaxVolume() == TEST_MAX_VOLUME
    293                         && info.getVolumeControl() == VolumeProvider.VOLUME_CONTROL_FIXED
    294                         && info.getPlaybackType()
    295                                 == MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE) {
    296                     break;
    297                 }
    298             }
    299             assertNotNull(info);
    300             assertEquals(MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE, info.getPlaybackType());
    301             assertEquals(TEST_MAX_VOLUME, info.getMaxVolume());
    302             assertEquals(TEST_CURRENT_VOLUME, info.getCurrentVolume());
    303             assertEquals(VolumeProvider.VOLUME_CONTROL_FIXED, info.getVolumeControl());
    304 
    305             info = controller.getPlaybackInfo();
    306             assertNotNull(info);
    307             assertEquals(MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE, info.getPlaybackType());
    308             assertEquals(TEST_MAX_VOLUME, info.getMaxVolume());
    309             assertEquals(TEST_CURRENT_VOLUME, info.getCurrentVolume());
    310             assertEquals(VolumeProvider.VOLUME_CONTROL_FIXED, info.getVolumeControl());
    311 
    312             // test setPlaybackToLocal
    313             AudioAttributes attrs = new AudioAttributes.Builder().setUsage(USAGE_GAME).build();
    314             mSession.setPlaybackToLocal(attrs);
    315 
    316             info = controller.getPlaybackInfo();
    317             assertNotNull(info);
    318             assertEquals(MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL, info.getPlaybackType());
    319             assertEquals(attrs, info.getAudioAttributes());
    320         }
    321     }
    322 
    323     /**
    324      * Test {@link MediaSession.Callback#onMediaButtonEvent}.
    325      */
    326     public void testCallbackOnMediaButtonEvent() throws Exception {
    327         MediaSessionCallback sessionCallback = new MediaSessionCallback();
    328         mSession.setCallback(sessionCallback, new Handler(Looper.getMainLooper()));
    329         mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS);
    330         mSession.setActive(true);
    331 
    332         Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON).setComponent(
    333                 new ComponentName(getContext(), getContext().getClass()));
    334         PendingIntent pi = PendingIntent.getBroadcast(getContext(), 0, mediaButtonIntent, 0);
    335         mSession.setMediaButtonReceiver(pi);
    336 
    337         // Set state to STATE_PLAYING to get higher priority.
    338         setPlaybackState(PlaybackState.STATE_PLAYING);
    339 
    340         // A media playback is also needed to receive media key events.
    341         Utils.assertMediaPlaybackStarted(getContext());
    342 
    343         sessionCallback.reset(1);
    344         simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PLAY);
    345         assertTrue(sessionCallback.await(TIME_OUT_MS));
    346         assertEquals(1, sessionCallback.mOnPlayCalledCount);
    347         assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo));
    348 
    349         sessionCallback.reset(1);
    350         simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PAUSE);
    351         assertTrue(sessionCallback.await(TIME_OUT_MS));
    352         assertTrue(sessionCallback.mOnPauseCalled);
    353         assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo));
    354 
    355         sessionCallback.reset(1);
    356         simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_NEXT);
    357         assertTrue(sessionCallback.await(TIME_OUT_MS));
    358         assertTrue(sessionCallback.mOnSkipToNextCalled);
    359         assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo));
    360 
    361         sessionCallback.reset(1);
    362         simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PREVIOUS);
    363         assertTrue(sessionCallback.await(TIME_OUT_MS));
    364         assertTrue(sessionCallback.mOnSkipToPreviousCalled);
    365         assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo));
    366 
    367         sessionCallback.reset(1);
    368         simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_STOP);
    369         assertTrue(sessionCallback.await(TIME_OUT_MS));
    370         assertTrue(sessionCallback.mOnStopCalled);
    371         assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo));
    372 
    373         sessionCallback.reset(1);
    374         simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD);
    375         assertTrue(sessionCallback.await(TIME_OUT_MS));
    376         assertTrue(sessionCallback.mOnFastForwardCalled);
    377         assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo));
    378 
    379         sessionCallback.reset(1);
    380         simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_REWIND);
    381         assertTrue(sessionCallback.await(TIME_OUT_MS));
    382         assertTrue(sessionCallback.mOnRewindCalled);
    383         assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo));
    384 
    385         // Test PLAY_PAUSE button twice.
    386         // First, simulate PLAY_PAUSE button while in STATE_PAUSED.
    387         sessionCallback.reset(1);
    388         setPlaybackState(PlaybackState.STATE_PAUSED);
    389         simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
    390         assertTrue(sessionCallback.await(TIME_OUT_MS));
    391         assertEquals(1, sessionCallback.mOnPlayCalledCount);
    392         assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo));
    393 
    394         // Next, simulate PLAY_PAUSE button while in STATE_PLAYING.
    395         sessionCallback.reset(1);
    396         setPlaybackState(PlaybackState.STATE_PLAYING);
    397         simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
    398         assertTrue(sessionCallback.await(TIME_OUT_MS));
    399         assertTrue(sessionCallback.mOnPauseCalled);
    400         assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo));
    401 
    402         // Double tap of PLAY_PAUSE is the next track instead of changing PLAY/PAUSE.
    403         sessionCallback.reset(2);
    404         setPlaybackState(PlaybackState.STATE_PLAYING);
    405         simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
    406         simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
    407         assertFalse(sessionCallback.await(WAIT_MS));
    408         assertTrue(sessionCallback.mOnSkipToNextCalled);
    409         assertEquals(0, sessionCallback.mOnPlayCalledCount);
    410         assertFalse(sessionCallback.mOnPauseCalled);
    411         assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo));
    412 
    413         // Test if PLAY_PAUSE double tap is considered as two single taps when another media
    414         // key is pressed.
    415         sessionCallback.reset(3);
    416         setPlaybackState(PlaybackState.STATE_PAUSED);
    417         simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
    418         simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_STOP);
    419         simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
    420         assertTrue(sessionCallback.await(TIME_OUT_MS));
    421         assertEquals(2, sessionCallback.mOnPlayCalledCount);
    422         assertTrue(sessionCallback.mOnStopCalled);
    423         assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo));
    424 
    425         // Test if media keys are handled in order.
    426         sessionCallback.reset(2);
    427         setPlaybackState(PlaybackState.STATE_PAUSED);
    428         simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
    429         simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_STOP);
    430         assertTrue(sessionCallback.await(TIME_OUT_MS));
    431         assertEquals(1, sessionCallback.mOnPlayCalledCount);
    432         assertTrue(sessionCallback.mOnStopCalled);
    433         assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo));
    434         synchronized (mWaitLock) {
    435             assertEquals(PlaybackState.STATE_STOPPED,
    436                     mSession.getController().getPlaybackState().getState());
    437         }
    438     }
    439 
    440     /**
    441      * Tests {@link MediaSession#setCallback} with {@code null}. No callbacks will be called
    442      * once {@code setCallback(null)} is done.
    443      */
    444     public void testSetCallbackWithNull() throws Exception {
    445         MediaSessionCallback sessionCallback = new MediaSessionCallback();
    446         mSession.setCallback(sessionCallback, mHandler);
    447         mSession.setFlags(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
    448         mSession.setActive(true);
    449 
    450         MediaController controller = mSession.getController();
    451         setPlaybackState(PlaybackState.STATE_PLAYING);
    452 
    453         sessionCallback.reset(1);
    454         mSession.setCallback(null, mHandler);
    455 
    456         controller.getTransportControls().pause();
    457         assertFalse(sessionCallback.await(WAIT_MS));
    458         assertFalse("Callback shouldn't be called.", sessionCallback.mOnPauseCalled);
    459     }
    460 
    461     private void setPlaybackState(int state) {
    462         final long allActions = PlaybackState.ACTION_PLAY | PlaybackState.ACTION_PAUSE
    463                 | PlaybackState.ACTION_PLAY_PAUSE | PlaybackState.ACTION_STOP
    464                 | PlaybackState.ACTION_SKIP_TO_NEXT | PlaybackState.ACTION_SKIP_TO_PREVIOUS
    465                 | PlaybackState.ACTION_FAST_FORWARD | PlaybackState.ACTION_REWIND;
    466         PlaybackState playbackState = new PlaybackState.Builder().setActions(allActions)
    467                 .setState(state, 0L, 0.0f).build();
    468         synchronized (mWaitLock) {
    469             mSession.setPlaybackState(playbackState);
    470         }
    471     }
    472 
    473     /**
    474      * Test {@link MediaSession#release} doesn't crash when multiple media sessions are in the app
    475      * which receives the media key events.
    476      * See: b/36669550
    477      */
    478     public void testReleaseNoCrashWithMultipleSessions() throws Exception {
    479         // Start a media playback for this app to receive media key events.
    480         Utils.assertMediaPlaybackStarted(getContext());
    481 
    482         MediaSession anotherSession = new MediaSession(getContext(), TEST_SESSION_TAG);
    483         mSession.release();
    484         anotherSession.release();
    485 
    486         // Try release with the different order.
    487         mSession = new MediaSession(getContext(), TEST_SESSION_TAG);
    488         anotherSession = new MediaSession(getContext(), TEST_SESSION_TAG);
    489         anotherSession.release();
    490         mSession.release();
    491     }
    492 
    493     // This uses public APIs to dispatch key events, so sessions would consider this as
    494     // 'media key event from this application'.
    495     private void simulateMediaKeyInput(int keyCode) {
    496         mAudioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
    497         mAudioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keyCode));
    498     }
    499 
    500     /**
    501      * Tests {@link MediaSession.QueueItem}.
    502      */
    503     public void testQueueItem() {
    504         MediaDescription.Builder descriptionBuilder = new MediaDescription.Builder()
    505                 .setMediaId("media-id")
    506                 .setTitle("title");
    507 
    508         QueueItem item = new QueueItem(descriptionBuilder.build(), TEST_QUEUE_ID);
    509         assertEquals(TEST_QUEUE_ID, item.getQueueId());
    510         assertEquals("media-id", item.getDescription().getMediaId());
    511         assertEquals("title", item.getDescription().getTitle());
    512         assertEquals(0, item.describeContents());
    513 
    514         QueueItem sameItem = new QueueItem(descriptionBuilder.build(), TEST_QUEUE_ID);
    515         assertTrue(item.equals(sameItem));
    516 
    517         QueueItem differentQueueId = new QueueItem(
    518             descriptionBuilder.build(), TEST_QUEUE_ID + 1);
    519         assertFalse(item.equals(differentQueueId));
    520 
    521         QueueItem differentDescription = new QueueItem(
    522             descriptionBuilder.setTitle("title2").build(), TEST_QUEUE_ID);
    523         assertFalse(item.equals(differentDescription));
    524 
    525         Parcel p = Parcel.obtain();
    526         item.writeToParcel(p, 0);
    527         p.setDataPosition(0);
    528         QueueItem other = QueueItem.CREATOR.createFromParcel(p);
    529         assertEquals(item.toString(), other.toString());
    530         p.recycle();
    531     }
    532 
    533     /**
    534      * Verifies that a new session hasn't had any configuration bits set yet.
    535      *
    536      * @param controller The controller for the session
    537      */
    538     private void verifyNewSession(MediaController controller) {
    539         assertEquals("New session has unexpected configuration", 0L, controller.getFlags());
    540         assertNull("New session has unexpected configuration", controller.getExtras());
    541         assertNull("New session has unexpected configuration", controller.getMetadata());
    542         assertEquals("New session has unexpected configuration",
    543                 getContext().getPackageName(), controller.getPackageName());
    544         assertNull("New session has unexpected configuration", controller.getPlaybackState());
    545         assertNull("New session has unexpected configuration", controller.getQueue());
    546         assertNull("New session has unexpected configuration", controller.getQueueTitle());
    547         assertEquals("New session has unexpected configuration", Rating.RATING_NONE,
    548                 controller.getRatingType());
    549         assertNull("New session has unexpected configuration", controller.getSessionActivity());
    550 
    551         assertNotNull(controller.getSessionToken());
    552         assertNotNull(controller.getTransportControls());
    553 
    554         MediaController.PlaybackInfo info = controller.getPlaybackInfo();
    555         assertNotNull(info);
    556         assertEquals(MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL, info.getPlaybackType());
    557         AudioAttributes attrs = info.getAudioAttributes();
    558         assertNotNull(attrs);
    559         assertEquals(AudioAttributes.USAGE_MEDIA, attrs.getUsage());
    560         assertEquals(mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC),
    561                 info.getCurrentVolume());
    562     }
    563 
    564     private class MediaControllerCallback extends MediaController.Callback {
    565         private volatile boolean mOnPlaybackStateChangedCalled;
    566         private volatile boolean mOnMetadataChangedCalled;
    567         private volatile boolean mOnQueueChangedCalled;
    568         private volatile boolean mOnQueueTitleChangedCalled;
    569         private volatile boolean mOnExtraChangedCalled;
    570         private volatile boolean mOnAudioInfoChangedCalled;
    571         private volatile boolean mOnSessionDestroyedCalled;
    572         private volatile boolean mOnSessionEventCalled;
    573 
    574         private volatile PlaybackState mPlaybackState;
    575         private volatile MediaMetadata mMediaMetadata;
    576         private volatile List<QueueItem> mQueue;
    577         private volatile CharSequence mTitle;
    578         private volatile String mEvent;
    579         private volatile Bundle mExtras;
    580         private volatile MediaController.PlaybackInfo mPlaybackInfo;
    581 
    582         public void resetLocked() {
    583             mOnPlaybackStateChangedCalled = false;
    584             mOnMetadataChangedCalled = false;
    585             mOnQueueChangedCalled = false;
    586             mOnQueueTitleChangedCalled = false;
    587             mOnExtraChangedCalled = false;
    588             mOnAudioInfoChangedCalled = false;
    589             mOnSessionDestroyedCalled = false;
    590             mOnSessionEventCalled = false;
    591 
    592             mPlaybackState = null;
    593             mMediaMetadata = null;
    594             mQueue = null;
    595             mTitle = null;
    596             mExtras = null;
    597             mPlaybackInfo = null;
    598         }
    599 
    600         @Override
    601         public void onPlaybackStateChanged(PlaybackState state) {
    602             synchronized (mWaitLock) {
    603                 mOnPlaybackStateChangedCalled = true;
    604                 mPlaybackState = state;
    605                 mWaitLock.notify();
    606             }
    607         }
    608 
    609         @Override
    610         public void onMetadataChanged(MediaMetadata metadata) {
    611             synchronized (mWaitLock) {
    612                 mOnMetadataChangedCalled = true;
    613                 mMediaMetadata = metadata;
    614                 mWaitLock.notify();
    615             }
    616         }
    617 
    618         @Override
    619         public void onQueueChanged(List<QueueItem> queue) {
    620             synchronized (mWaitLock) {
    621                 mOnQueueChangedCalled = true;
    622                 mQueue = queue;
    623                 mWaitLock.notify();
    624             }
    625         }
    626 
    627         @Override
    628         public void onQueueTitleChanged(CharSequence title) {
    629             synchronized (mWaitLock) {
    630                 mOnQueueTitleChangedCalled = true;
    631                 mTitle = title;
    632                 mWaitLock.notify();
    633             }
    634         }
    635 
    636         @Override
    637         public void onExtrasChanged(Bundle extras) {
    638             synchronized (mWaitLock) {
    639                 mOnExtraChangedCalled = true;
    640                 mExtras = extras;
    641                 mWaitLock.notify();
    642             }
    643         }
    644 
    645         @Override
    646         public void onAudioInfoChanged(MediaController.PlaybackInfo info) {
    647             synchronized (mWaitLock) {
    648                 mOnAudioInfoChangedCalled = true;
    649                 mPlaybackInfo = info;
    650                 mWaitLock.notify();
    651             }
    652         }
    653 
    654         @Override
    655         public void onSessionDestroyed() {
    656             synchronized (mWaitLock) {
    657                 mOnSessionDestroyedCalled = true;
    658                 mWaitLock.notify();
    659             }
    660         }
    661 
    662         @Override
    663         public void onSessionEvent(String event, Bundle extras) {
    664             synchronized (mWaitLock) {
    665                 mOnSessionEventCalled = true;
    666                 mEvent = event;
    667                 mExtras = (Bundle) extras.clone();
    668                 mWaitLock.notify();
    669             }
    670         }
    671     }
    672 
    673     private class MediaSessionCallback extends MediaSession.Callback {
    674         private CountDownLatch mLatch;
    675         private int mOnPlayCalledCount;
    676         private boolean mOnPauseCalled;
    677         private boolean mOnStopCalled;
    678         private boolean mOnFastForwardCalled;
    679         private boolean mOnRewindCalled;
    680         private boolean mOnSkipToPreviousCalled;
    681         private boolean mOnSkipToNextCalled;
    682         private RemoteUserInfo mCallerInfo;
    683 
    684         public void reset(int count) {
    685             mLatch = new CountDownLatch(count);
    686             mOnPlayCalledCount = 0;
    687             mOnPauseCalled = false;
    688             mOnStopCalled = false;
    689             mOnFastForwardCalled = false;
    690             mOnRewindCalled = false;
    691             mOnSkipToPreviousCalled = false;
    692             mOnSkipToNextCalled = false;
    693         }
    694 
    695         public boolean await(long waitMs) {
    696             try {
    697                 return mLatch.await(waitMs, TimeUnit.MILLISECONDS);
    698             } catch (InterruptedException e) {
    699                 return false;
    700             }
    701         }
    702 
    703         @Override
    704         public void onPlay() {
    705             mOnPlayCalledCount++;
    706             mCallerInfo = mSession.getCurrentControllerInfo();
    707             setPlaybackState(PlaybackState.STATE_PLAYING);
    708             mLatch.countDown();
    709         }
    710 
    711         @Override
    712         public void onPause() {
    713             mOnPauseCalled = true;
    714             mCallerInfo = mSession.getCurrentControllerInfo();
    715             setPlaybackState(PlaybackState.STATE_PAUSED);
    716             mLatch.countDown();
    717         }
    718 
    719         @Override
    720         public void onStop() {
    721             mOnStopCalled = true;
    722             mCallerInfo = mSession.getCurrentControllerInfo();
    723             setPlaybackState(PlaybackState.STATE_STOPPED);
    724             mLatch.countDown();
    725         }
    726 
    727         @Override
    728         public void onFastForward() {
    729             mOnFastForwardCalled = true;
    730             mCallerInfo = mSession.getCurrentControllerInfo();
    731             mLatch.countDown();
    732         }
    733 
    734         @Override
    735         public void onRewind() {
    736             mOnRewindCalled = true;
    737             mCallerInfo = mSession.getCurrentControllerInfo();
    738             mLatch.countDown();
    739         }
    740 
    741         @Override
    742         public void onSkipToPrevious() {
    743             mOnSkipToPreviousCalled = true;
    744             mCallerInfo = mSession.getCurrentControllerInfo();
    745             mLatch.countDown();
    746         }
    747 
    748         @Override
    749         public void onSkipToNext() {
    750             mOnSkipToNextCalled = true;
    751             mCallerInfo = mSession.getCurrentControllerInfo();
    752             mLatch.countDown();
    753         }
    754     }
    755 }
    756