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