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 android.app.PendingIntent;
19 import android.content.Context;
20 import android.content.Intent;
21 import android.media.AudioAttributes;
22 import android.media.AudioManager;
23 import android.media.MediaDescription;
24 import android.media.MediaMetadata;
25 import android.media.Rating;
26 import android.media.VolumeProvider;
27 import android.media.session.MediaController;
28 import android.media.session.MediaSession;
29 import android.media.session.MediaSession.QueueItem;
30 import android.media.session.PlaybackState;
31 import android.os.Bundle;
32 import android.os.Handler;
33 import android.os.Looper;
34 import android.os.Message;
35 import android.os.Parcel;
36 import android.test.AndroidTestCase;
37 
38 import java.util.ArrayList;
39 import java.util.List;
40 import java.util.Set;
41 
42 public class MediaSessionTest extends AndroidTestCase {
43     // The maximum time to wait for an operation.
44     private static final long TIME_OUT_MS = 3000L;
45     private static final int MAX_AUDIO_INFO_CHANGED_CALLBACK_COUNT = 10;
46     private static final String TEST_SESSION_TAG = "test-session-tag";
47     private static final String TEST_KEY = "test-key";
48     private static final String TEST_VALUE = "test-val";
49     private static final String TEST_SESSION_EVENT = "test-session-event";
50     private static final int TEST_CURRENT_VOLUME = 10;
51     private static final int TEST_MAX_VOLUME = 11;
52     private static final long TEST_QUEUE_ID = 12L;
53     private static final long TEST_ACTION = 55L;
54 
55     private AudioManager mAudioManager;
56     private Handler mHandler = new Handler(Looper.getMainLooper());
57     private Object mWaitLock = new Object();
58     private MediaControllerCallback mCallback = new MediaControllerCallback();
59     private MediaSession mSession;
60 
61     @Override
setUp()62     protected void setUp() throws Exception {
63         super.setUp();
64         mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
65         mSession = new MediaSession(getContext(), TEST_SESSION_TAG);
66     }
67 
68     @Override
tearDown()69     protected void tearDown() throws Exception {
70         // It is OK to call release() twice.
71         mSession.release();
72         super.tearDown();
73     }
74 
75     /**
76      * Tests that a session can be created and that all the fields are
77      * initialized correctly.
78      */
testCreateSession()79     public void testCreateSession() throws Exception {
80         assertNotNull(mSession.getSessionToken());
81         assertFalse("New session should not be active", mSession.isActive());
82 
83         // Verify by getting the controller and checking all its fields
84         MediaController controller = mSession.getController();
85         assertNotNull(controller);
86         verifyNewSession(controller, TEST_SESSION_TAG);
87     }
88 
89     /**
90      * Tests MediaSession.Token created in the constructor of MediaSession.
91      */
testSessionToken()92     public void testSessionToken() throws Exception {
93         MediaSession.Token sessionToken = mSession.getSessionToken();
94 
95         assertNotNull(sessionToken);
96         assertEquals(0, sessionToken.describeContents());
97 
98         // Test writeToParcel
99         Parcel p = Parcel.obtain();
100         sessionToken.writeToParcel(p, 0);
101         p.setDataPosition(0);
102         MediaSession.Token token = MediaSession.Token.CREATOR.createFromParcel(p);
103         assertEquals(token, sessionToken);
104         p.recycle();
105     }
106 
107     /**
108      * Tests that the various configuration bits on a session get passed to the
109      * controller.
110      */
testConfigureSession()111     public void testConfigureSession() throws Exception {
112         MediaController controller = mSession.getController();
113         controller.registerCallback(mCallback, mHandler);
114 
115         synchronized (mWaitLock) {
116             // test setExtras
117             mCallback.resetLocked();
118             final Bundle extras = new Bundle();
119             extras.putString(TEST_KEY, TEST_VALUE);
120             mSession.setExtras(extras);
121             mWaitLock.wait(TIME_OUT_MS);
122             assertTrue(mCallback.mOnExtraChangedCalled);
123 
124             Bundle extrasOut = mCallback.mExtras;
125             assertNotNull(extrasOut);
126             assertEquals(TEST_VALUE, extrasOut.get(TEST_KEY));
127 
128             extrasOut = controller.getExtras();
129             assertNotNull(extrasOut);
130             assertEquals(TEST_VALUE, extrasOut.get(TEST_KEY));
131 
132             // test setFlags
133             mSession.setFlags(5);
134             assertEquals(5, controller.getFlags());
135 
136             // test setMetadata
137             mCallback.resetLocked();
138             MediaMetadata metadata =
139                     new MediaMetadata.Builder().putString(TEST_KEY, TEST_VALUE).build();
140             mSession.setMetadata(metadata);
141             mWaitLock.wait(TIME_OUT_MS);
142             assertTrue(mCallback.mOnMetadataChangedCalled);
143 
144             MediaMetadata metadataOut = mCallback.mMediaMetadata;
145             assertNotNull(metadataOut);
146             assertEquals(TEST_VALUE, metadataOut.getString(TEST_KEY));
147 
148             metadataOut = controller.getMetadata();
149             assertNotNull(metadataOut);
150             assertEquals(TEST_VALUE, metadataOut.getString(TEST_KEY));
151 
152             // test setPlaybackState
153             mCallback.resetLocked();
154             PlaybackState state = new PlaybackState.Builder().setActions(TEST_ACTION).build();
155             mSession.setPlaybackState(state);
156             mWaitLock.wait(TIME_OUT_MS);
157             assertTrue(mCallback.mOnPlaybackStateChangedCalled);
158 
159             PlaybackState stateOut = mCallback.mPlaybackState;
160             assertNotNull(stateOut);
161             assertEquals(TEST_ACTION, stateOut.getActions());
162 
163             stateOut = controller.getPlaybackState();
164             assertNotNull(stateOut);
165             assertEquals(TEST_ACTION, stateOut.getActions());
166 
167             // test setQueue and setQueueTitle
168             mCallback.resetLocked();
169             List<QueueItem> queue = new ArrayList<>();
170             QueueItem item = new QueueItem(new MediaDescription.Builder()
171                     .setMediaId(TEST_VALUE).setTitle("title").build(), TEST_QUEUE_ID);
172             queue.add(item);
173             mSession.setQueue(queue);
174             mWaitLock.wait(TIME_OUT_MS);
175             assertTrue(mCallback.mOnQueueChangedCalled);
176 
177             mSession.setQueueTitle(TEST_VALUE);
178             mWaitLock.wait(TIME_OUT_MS);
179             assertTrue(mCallback.mOnQueueTitleChangedCalled);
180 
181             assertEquals(TEST_VALUE, mCallback.mTitle);
182             assertEquals(queue.size(), mCallback.mQueue.size());
183             assertEquals(TEST_QUEUE_ID, mCallback.mQueue.get(0).getQueueId());
184             assertEquals(TEST_VALUE, mCallback.mQueue.get(0).getDescription().getMediaId());
185 
186             assertEquals(TEST_VALUE, controller.getQueueTitle());
187             assertEquals(queue.size(), controller.getQueue().size());
188             assertEquals(TEST_QUEUE_ID, controller.getQueue().get(0).getQueueId());
189             assertEquals(TEST_VALUE, controller.getQueue().get(0).getDescription().getMediaId());
190 
191             mCallback.resetLocked();
192             mSession.setQueue(null);
193             mWaitLock.wait(TIME_OUT_MS);
194             assertTrue(mCallback.mOnQueueChangedCalled);
195 
196             mSession.setQueueTitle(null);
197             mWaitLock.wait(TIME_OUT_MS);
198             assertTrue(mCallback.mOnQueueTitleChangedCalled);
199 
200             assertNull(mCallback.mTitle);
201             assertNull(mCallback.mQueue);
202             assertNull(controller.getQueueTitle());
203             assertNull(controller.getQueue());
204 
205             // test setSessionActivity
206             Intent intent = new Intent("cts.MEDIA_SESSION_ACTION");
207             PendingIntent pi = PendingIntent.getActivity(getContext(), 555, intent, 0);
208             mSession.setSessionActivity(pi);
209             assertEquals(pi, controller.getSessionActivity());
210 
211             // test setActivity
212             mSession.setActive(true);
213             assertTrue(mSession.isActive());
214 
215             // test sendSessionEvent
216             mCallback.resetLocked();
217             mSession.sendSessionEvent(TEST_SESSION_EVENT, extras);
218             mWaitLock.wait(TIME_OUT_MS);
219 
220             assertTrue(mCallback.mOnSessionEventCalled);
221             assertEquals(TEST_SESSION_EVENT, mCallback.mEvent);
222             assertEquals(TEST_VALUE, mCallback.mExtras.getString(TEST_KEY));
223 
224             // test release
225             mCallback.resetLocked();
226             mSession.release();
227             mWaitLock.wait(TIME_OUT_MS);
228             assertTrue(mCallback.mOnSessionDestroyedCalled);
229         }
230     }
231 
232     /**
233      * Tests for setPlaybackToLocal and setPlaybackToRemote.
234      */
testPlaybackToLocalAndRemote()235     public void testPlaybackToLocalAndRemote() throws Exception {
236         MediaController controller = mSession.getController();
237         controller.registerCallback(mCallback, mHandler);
238 
239         synchronized (mWaitLock) {
240             // test setPlaybackToRemote, do this before testing setPlaybackToLocal
241             // to ensure it switches correctly.
242             mCallback.resetLocked();
243             try {
244                 mSession.setPlaybackToRemote(null);
245                 fail("Expected IAE for setPlaybackToRemote(null)");
246             } catch (IllegalArgumentException e) {
247                 // expected
248             }
249             VolumeProvider vp = new VolumeProvider(VolumeProvider.VOLUME_CONTROL_FIXED,
250                     TEST_MAX_VOLUME, TEST_CURRENT_VOLUME) {};
251             mSession.setPlaybackToRemote(vp);
252 
253             MediaController.PlaybackInfo info = null;
254             for (int i = 0; i < MAX_AUDIO_INFO_CHANGED_CALLBACK_COUNT; ++i) {
255                 mCallback.mOnAudioInfoChangedCalled = false;
256                 mWaitLock.wait(TIME_OUT_MS);
257                 assertTrue(mCallback.mOnAudioInfoChangedCalled);
258                 info = mCallback.mPlaybackInfo;
259                 if (info != null && info.getCurrentVolume() == TEST_CURRENT_VOLUME
260                         && info.getMaxVolume() == TEST_MAX_VOLUME
261                         && info.getVolumeControl() == VolumeProvider.VOLUME_CONTROL_FIXED
262                         && info.getPlaybackType()
263                                 == MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE) {
264                     break;
265                 }
266             }
267             assertNotNull(info);
268             assertEquals(MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE, info.getPlaybackType());
269             assertEquals(TEST_MAX_VOLUME, info.getMaxVolume());
270             assertEquals(TEST_CURRENT_VOLUME, info.getCurrentVolume());
271             assertEquals(VolumeProvider.VOLUME_CONTROL_FIXED, info.getVolumeControl());
272 
273             info = controller.getPlaybackInfo();
274             assertNotNull(info);
275             assertEquals(MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE, info.getPlaybackType());
276             assertEquals(TEST_MAX_VOLUME, info.getMaxVolume());
277             assertEquals(TEST_CURRENT_VOLUME, info.getCurrentVolume());
278             assertEquals(VolumeProvider.VOLUME_CONTROL_FIXED, info.getVolumeControl());
279 
280             // test setPlaybackToLocal
281             AudioAttributes attrs = new AudioAttributes.Builder().addTag(TEST_VALUE).build();
282             mSession.setPlaybackToLocal(attrs);
283 
284             info = controller.getPlaybackInfo();
285             assertNotNull(info);
286             assertEquals(MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL, info.getPlaybackType());
287             Set<String> tags = info.getAudioAttributes().getTags();
288             assertNotNull(tags);
289             assertTrue(tags.contains(TEST_VALUE));
290         }
291     }
292 
293     /**
294      * Tests MediaSession.QueueItem.
295      */
testQueueItem()296     public void testQueueItem() {
297         QueueItem item = new QueueItem(new MediaDescription.Builder()
298                 .setMediaId("media-id").setTitle("title").build(), TEST_QUEUE_ID);
299         assertEquals(TEST_QUEUE_ID, item.getQueueId());
300         assertEquals("media-id", item.getDescription().getMediaId());
301         assertEquals("title", item.getDescription().getTitle());
302 
303         Parcel p = Parcel.obtain();
304         item.writeToParcel(p, 0);
305         p.setDataPosition(0);
306         QueueItem other = QueueItem.CREATOR.createFromParcel(p);
307         assertEquals(item.toString(), other.toString());
308         p.recycle();
309     }
310 
311     /**
312      * Verifies that a new session hasn't had any configuration bits set yet.
313      *
314      * @param controller The controller for the session
315      */
verifyNewSession(MediaController controller, String tag)316     private void verifyNewSession(MediaController controller, String tag) {
317         assertEquals("New session has unexpected configuration", 0L, controller.getFlags());
318         assertNull("New session has unexpected configuration", controller.getExtras());
319         assertNull("New session has unexpected configuration", controller.getMetadata());
320         assertEquals("New session has unexpected configuration",
321                 getContext().getPackageName(), controller.getPackageName());
322         assertNull("New session has unexpected configuration", controller.getPlaybackState());
323         assertNull("New session has unexpected configuration", controller.getQueue());
324         assertNull("New session has unexpected configuration", controller.getQueueTitle());
325         assertEquals("New session has unexpected configuration", Rating.RATING_NONE,
326                 controller.getRatingType());
327         assertNull("New session has unexpected configuration", controller.getSessionActivity());
328 
329         assertNotNull(controller.getSessionToken());
330         assertNotNull(controller.getTransportControls());
331         assertEquals(tag, controller.getTag());
332 
333         MediaController.PlaybackInfo info = controller.getPlaybackInfo();
334         assertNotNull(info);
335         assertEquals(MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL, info.getPlaybackType());
336         AudioAttributes attrs = info.getAudioAttributes();
337         assertNotNull(attrs);
338         assertEquals(AudioAttributes.USAGE_MEDIA, attrs.getUsage());
339         assertEquals(mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC),
340                 info.getCurrentVolume());
341     }
342 
343     private class MediaControllerCallback extends MediaController.Callback {
344         private volatile boolean mOnPlaybackStateChangedCalled;
345         private volatile boolean mOnMetadataChangedCalled;
346         private volatile boolean mOnQueueChangedCalled;
347         private volatile boolean mOnQueueTitleChangedCalled;
348         private volatile boolean mOnExtraChangedCalled;
349         private volatile boolean mOnAudioInfoChangedCalled;
350         private volatile boolean mOnSessionDestroyedCalled;
351         private volatile boolean mOnSessionEventCalled;
352 
353         private volatile PlaybackState mPlaybackState;
354         private volatile MediaMetadata mMediaMetadata;
355         private volatile List<QueueItem> mQueue;
356         private volatile CharSequence mTitle;
357         private volatile String mEvent;
358         private volatile Bundle mExtras;
359         private volatile MediaController.PlaybackInfo mPlaybackInfo;
360 
resetLocked()361         public void resetLocked() {
362             mOnPlaybackStateChangedCalled = false;
363             mOnMetadataChangedCalled = false;
364             mOnQueueChangedCalled = false;
365             mOnQueueTitleChangedCalled = false;
366             mOnExtraChangedCalled = false;
367             mOnAudioInfoChangedCalled = false;
368             mOnSessionDestroyedCalled = false;
369             mOnSessionEventCalled = false;
370 
371             mPlaybackState = null;
372             mMediaMetadata = null;
373             mQueue = null;
374             mTitle = null;
375             mExtras = null;
376             mPlaybackInfo = null;
377         }
378 
379         @Override
onPlaybackStateChanged(PlaybackState state)380         public void onPlaybackStateChanged(PlaybackState state) {
381             synchronized (mWaitLock) {
382                 mOnPlaybackStateChangedCalled = true;
383                 mPlaybackState = state;
384                 mWaitLock.notify();
385             }
386         }
387 
388         @Override
onMetadataChanged(MediaMetadata metadata)389         public void onMetadataChanged(MediaMetadata metadata) {
390             synchronized (mWaitLock) {
391                 mOnMetadataChangedCalled = true;
392                 mMediaMetadata = metadata;
393                 mWaitLock.notify();
394             }
395         }
396 
397         @Override
onQueueChanged(List<QueueItem> queue)398         public void onQueueChanged(List<QueueItem> queue) {
399             synchronized (mWaitLock) {
400                 mOnQueueChangedCalled = true;
401                 mQueue = queue;
402                 mWaitLock.notify();
403             }
404         }
405 
406         @Override
onQueueTitleChanged(CharSequence title)407         public void onQueueTitleChanged(CharSequence title) {
408             synchronized (mWaitLock) {
409                 mOnQueueTitleChangedCalled = true;
410                 mTitle = title;
411                 mWaitLock.notify();
412             }
413         }
414 
415         @Override
onExtrasChanged(Bundle extras)416         public void onExtrasChanged(Bundle extras) {
417             synchronized (mWaitLock) {
418                 mOnExtraChangedCalled = true;
419                 mExtras = extras;
420                 mWaitLock.notify();
421             }
422         }
423 
424         @Override
onAudioInfoChanged(MediaController.PlaybackInfo info)425         public void onAudioInfoChanged(MediaController.PlaybackInfo info) {
426             synchronized (mWaitLock) {
427                 mOnAudioInfoChangedCalled = true;
428                 mPlaybackInfo = info;
429                 mWaitLock.notify();
430             }
431         }
432 
433         @Override
onSessionDestroyed()434         public void onSessionDestroyed() {
435             synchronized (mWaitLock) {
436                 mOnSessionDestroyedCalled = true;
437                 mWaitLock.notify();
438             }
439         }
440 
441         @Override
onSessionEvent(String event, Bundle extras)442         public void onSessionEvent(String event, Bundle extras) {
443             synchronized (mWaitLock) {
444                 mOnSessionEventCalled = true;
445                 mEvent = event;
446                 mExtras = (Bundle) extras.clone();
447                 mWaitLock.notify();
448             }
449         }
450     }
451 }
452