• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2018 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 androidx.media;
17 
18 import static android.content.Context.KEYGUARD_SERVICE;
19 
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertFalse;
22 import static org.junit.Assert.assertTrue;
23 import static org.junit.Assert.fail;
24 
25 import android.app.Instrumentation;
26 import android.app.KeyguardManager;
27 import android.content.Context;
28 import android.content.res.AssetFileDescriptor;
29 import android.content.res.Resources;
30 import android.media.AudioManager;
31 import android.media.MediaTimestamp;
32 import android.media.SubtitleData;
33 import android.media.TimedMetaData;
34 import android.net.Uri;
35 import android.os.PersistableBundle;
36 import android.os.PowerManager;
37 import android.support.test.InstrumentationRegistry;
38 import android.support.test.rule.ActivityTestRule;
39 import android.view.SurfaceHolder;
40 import android.view.WindowManager;
41 
42 import androidx.annotation.CallSuper;
43 
44 import org.junit.After;
45 import org.junit.Before;
46 import org.junit.Rule;
47 
48 import java.io.IOException;
49 import java.net.HttpCookie;
50 import java.util.ArrayList;
51 import java.util.List;
52 import java.util.Map;
53 import java.util.Set;
54 import java.util.concurrent.ExecutorService;
55 import java.util.concurrent.Executors;
56 import java.util.logging.Logger;
57 
58 /**
59  * Base class for tests which use MediaPlayer2 to play audio or video.
60  */
61 public class MediaPlayer2TestBase {
62     private static final Logger LOG = Logger.getLogger(MediaPlayer2TestBase.class.getName());
63 
64     protected static final int SLEEP_TIME = 1000;
65     protected static final int LONG_SLEEP_TIME = 6000;
66     protected static final int STREAM_RETRIES = 20;
67 
68     protected Monitor mOnVideoSizeChangedCalled = new Monitor();
69     protected Monitor mOnVideoRenderingStartCalled = new Monitor();
70     protected Monitor mOnBufferingUpdateCalled = new Monitor();
71     protected Monitor mOnPrepareCalled = new Monitor();
72     protected Monitor mOnPlayCalled = new Monitor();
73     protected Monitor mOnDeselectTrackCalled = new Monitor();
74     protected Monitor mOnSeekCompleteCalled = new Monitor();
75     protected Monitor mOnCompletionCalled = new Monitor();
76     protected Monitor mOnInfoCalled = new Monitor();
77     protected Monitor mOnErrorCalled = new Monitor();
78     protected Monitor mOnMediaTimeDiscontinuityCalled = new Monitor();
79     protected int mCallStatus;
80 
81     protected Context mContext;
82     protected Resources mResources;
83 
84     protected ExecutorService mExecutor;
85 
86     protected MediaPlayer2 mPlayer = null;
87     protected MediaPlayer2 mPlayer2 = null;
88     protected MediaStubActivity mActivity;
89     protected Instrumentation mInstrumentation;
90 
91     protected final Object mEventCbLock = new Object();
92     protected List<MediaPlayer2.MediaPlayer2EventCallback> mEventCallbacks = new ArrayList<>();
93     protected final Object mEventCbLock2 = new Object();
94     protected List<MediaPlayer2.MediaPlayer2EventCallback> mEventCallbacks2 = new ArrayList<>();
95 
96     @Rule
97     public ActivityTestRule<MediaStubActivity> mActivityRule =
98             new ActivityTestRule<>(MediaStubActivity.class);
99     public PowerManager.WakeLock mScreenLock;
100     private KeyguardManager mKeyguardManager;
101 
102     // convenience functions to create MediaPlayer2
createMediaPlayer2(Context context, Uri uri)103     protected MediaPlayer2 createMediaPlayer2(Context context, Uri uri) {
104         return createMediaPlayer2(context, uri, null);
105     }
106 
createMediaPlayer2(Context context, Uri uri, SurfaceHolder holder)107     protected MediaPlayer2 createMediaPlayer2(Context context, Uri uri,
108             SurfaceHolder holder) {
109         AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
110         int s = am.generateAudioSessionId();
111         return createMediaPlayer2(context, uri, holder, null, s > 0 ? s : 0);
112     }
113 
createMediaPlayer2(Context context, Uri uri, SurfaceHolder holder, AudioAttributesCompat audioAttributes, int audioSessionId)114     protected MediaPlayer2 createMediaPlayer2(Context context, Uri uri, SurfaceHolder holder,
115             AudioAttributesCompat audioAttributes, int audioSessionId) {
116         try {
117             MediaPlayer2 mp = createMediaPlayer2OnUiThread();
118             final AudioAttributesCompat aa = audioAttributes != null ? audioAttributes :
119                     new AudioAttributesCompat.Builder().build();
120             mp.setAudioAttributes(aa);
121             mp.setAudioSessionId(audioSessionId);
122             mp.setDataSource(new DataSourceDesc.Builder()
123                     .setDataSource(context, uri)
124                     .build());
125             if (holder != null) {
126                 mp.setSurface(holder.getSurface());
127             }
128             final Monitor onPrepareCalled = new Monitor();
129             ExecutorService executor = Executors.newFixedThreadPool(1);
130             MediaPlayer2.MediaPlayer2EventCallback ecb =
131                     new MediaPlayer2.MediaPlayer2EventCallback() {
132                         @Override
133                         public void onInfo(
134                                 MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
135                             if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
136                                 onPrepareCalled.signal();
137                             }
138                         }
139                     };
140             mp.setMediaPlayer2EventCallback(executor, ecb);
141             mp.prepare();
142             onPrepareCalled.waitForSignal();
143             mp.clearMediaPlayer2EventCallback();
144             executor.shutdown();
145             return mp;
146         } catch (IllegalArgumentException ex) {
147             LOG.warning("create failed:" + ex);
148             // fall through
149         } catch (SecurityException ex) {
150             LOG.warning("create failed:" + ex);
151             // fall through
152         } catch (InterruptedException ex) {
153             LOG.warning("create failed:" + ex);
154             // fall through
155         }
156         return null;
157     }
158 
createMediaPlayer2(Context context, int resid)159     protected MediaPlayer2 createMediaPlayer2(Context context, int resid) {
160         AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
161         int s = am.generateAudioSessionId();
162         return createMediaPlayer2(context, resid, null, s > 0 ? s : 0);
163     }
164 
createMediaPlayer2(Context context, int resid, AudioAttributesCompat audioAttributes, int audioSessionId)165     protected MediaPlayer2 createMediaPlayer2(Context context, int resid,
166             AudioAttributesCompat audioAttributes, int audioSessionId) {
167         try {
168             AssetFileDescriptor afd = context.getResources().openRawResourceFd(resid);
169             if (afd == null) {
170                 return null;
171             }
172 
173             MediaPlayer2 mp = createMediaPlayer2OnUiThread();
174 
175             final AudioAttributesCompat aa = audioAttributes != null ? audioAttributes :
176                     new AudioAttributesCompat.Builder().build();
177             mp.setAudioAttributes(aa);
178             mp.setAudioSessionId(audioSessionId);
179 
180             mp.setDataSource(new DataSourceDesc.Builder()
181                     .setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength())
182                     .build());
183 
184             final Monitor onPrepareCalled = new Monitor();
185             ExecutorService executor = Executors.newFixedThreadPool(1);
186             MediaPlayer2.MediaPlayer2EventCallback ecb =
187                     new MediaPlayer2.MediaPlayer2EventCallback() {
188                         @Override
189                         public void onInfo(
190                                 MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
191                             if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
192                                 onPrepareCalled.signal();
193                             }
194                         }
195                     };
196             mp.setMediaPlayer2EventCallback(executor, ecb);
197             mp.prepare();
198             onPrepareCalled.waitForSignal();
199             mp.clearMediaPlayer2EventCallback();
200             afd.close();
201             executor.shutdown();
202             return mp;
203         } catch (IOException ex) {
204             LOG.warning("create failed:" + ex);
205             // fall through
206         } catch (IllegalArgumentException ex) {
207             LOG.warning("create failed:" + ex);
208             // fall through
209         } catch (SecurityException ex) {
210             LOG.warning("create failed:" + ex);
211             // fall through
212         } catch (InterruptedException ex) {
213             LOG.warning("create failed:" + ex);
214             // fall through
215         }
216         return null;
217     }
218 
createMediaPlayer2OnUiThread()219     private MediaPlayer2 createMediaPlayer2OnUiThread() {
220         final MediaPlayer2[] mp = new MediaPlayer2[1];
221         try {
222             mActivityRule.runOnUiThread(new Runnable() {
223                 public void run() {
224                     mp[0] = MediaPlayer2.create();
225                 }
226             });
227         } catch (Throwable throwable) {
228             fail("Failed to create MediaPlayer2 instance on UI thread.");
229         }
230         return mp[0];
231     }
232 
233     public static class Monitor {
234         private int mNumSignal;
235 
reset()236         public synchronized void reset() {
237             mNumSignal = 0;
238         }
239 
signal()240         public synchronized void signal() {
241             mNumSignal++;
242             notifyAll();
243         }
244 
waitForSignal()245         public synchronized boolean waitForSignal() throws InterruptedException {
246             return waitForCountedSignals(1) > 0;
247         }
248 
waitForCountedSignals(int targetCount)249         public synchronized int waitForCountedSignals(int targetCount) throws InterruptedException {
250             while (mNumSignal < targetCount) {
251                 wait();
252             }
253             return mNumSignal;
254         }
255 
waitForSignal(long timeoutMs)256         public synchronized boolean waitForSignal(long timeoutMs) throws InterruptedException {
257             return waitForCountedSignals(1, timeoutMs) > 0;
258         }
259 
waitForCountedSignals(int targetCount, long timeoutMs)260         public synchronized int waitForCountedSignals(int targetCount, long timeoutMs)
261                 throws InterruptedException {
262             if (timeoutMs == 0) {
263                 return waitForCountedSignals(targetCount);
264             }
265             long deadline = System.currentTimeMillis() + timeoutMs;
266             while (mNumSignal < targetCount) {
267                 long delay = deadline - System.currentTimeMillis();
268                 if (delay <= 0) {
269                     break;
270                 }
271                 wait(delay);
272             }
273             return mNumSignal;
274         }
275 
isSignalled()276         public synchronized boolean isSignalled() {
277             return mNumSignal >= 1;
278         }
279 
getNumSignal()280         public synchronized int getNumSignal() {
281             return mNumSignal;
282         }
283     }
284 
285     @Before
286     @CallSuper
setUp()287     public void setUp() throws Throwable {
288         mInstrumentation = InstrumentationRegistry.getInstrumentation();
289         mKeyguardManager = (KeyguardManager)
290                 mInstrumentation.getTargetContext().getSystemService(KEYGUARD_SERVICE);
291         mActivity = mActivityRule.getActivity();
292         mActivityRule.runOnUiThread(new Runnable() {
293             @Override
294             public void run() {
295                 // Keep screen on while testing.
296                 mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
297                 mActivity.setTurnScreenOn(true);
298                 mActivity.setShowWhenLocked(true);
299                 mKeyguardManager.requestDismissKeyguard(mActivity, null);
300             }
301         });
302         mInstrumentation.waitForIdleSync();
303 
304         try {
305             mActivityRule.runOnUiThread(new Runnable() {
306                 public void run() {
307                     mPlayer = MediaPlayer2.create();
308                     mPlayer2 = MediaPlayer2.create();
309                 }
310             });
311         } catch (Throwable e) {
312             e.printStackTrace();
313             fail();
314         }
315         mContext = mActivityRule.getActivity();
316         mResources = mContext.getResources();
317         mExecutor = Executors.newFixedThreadPool(1);
318 
319         setUpMP2ECb(mPlayer, mEventCbLock, mEventCallbacks);
320         setUpMP2ECb(mPlayer2, mEventCbLock2, mEventCallbacks2);
321     }
322 
323     @After
324     @CallSuper
tearDown()325     public void tearDown() throws Exception {
326         if (mPlayer != null) {
327             mPlayer.close();
328             mPlayer = null;
329         }
330         if (mPlayer2 != null) {
331             mPlayer2.close();
332             mPlayer2 = null;
333         }
334         mExecutor.shutdown();
335         mActivity = null;
336     }
337 
setUpMP2ECb(MediaPlayer2 mp, final Object cbLock, final List<MediaPlayer2.MediaPlayer2EventCallback> ecbs)338     protected void setUpMP2ECb(MediaPlayer2 mp, final Object cbLock,
339             final List<MediaPlayer2.MediaPlayer2EventCallback> ecbs) {
340         mp.setMediaPlayer2EventCallback(mExecutor, new MediaPlayer2.MediaPlayer2EventCallback() {
341             @Override
342             public void onVideoSizeChanged(MediaPlayer2 mp, DataSourceDesc dsd, int w, int h) {
343                 synchronized (cbLock) {
344                     for (MediaPlayer2.MediaPlayer2EventCallback ecb : ecbs) {
345                         ecb.onVideoSizeChanged(mp, dsd, w, h);
346                     }
347                 }
348             }
349 
350             @Override
351             public void onTimedMetaDataAvailable(MediaPlayer2 mp, DataSourceDesc dsd,
352                     TimedMetaData data) {
353                 synchronized (cbLock) {
354                     for (MediaPlayer2.MediaPlayer2EventCallback ecb : ecbs) {
355                         ecb.onTimedMetaDataAvailable(mp, dsd, data);
356                     }
357                 }
358             }
359 
360             @Override
361             public void onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
362                 synchronized (cbLock) {
363                     for (MediaPlayer2.MediaPlayer2EventCallback ecb : ecbs) {
364                         ecb.onError(mp, dsd, what, extra);
365                     }
366                 }
367             }
368 
369             @Override
370             public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
371                 synchronized (cbLock) {
372                     for (MediaPlayer2.MediaPlayer2EventCallback ecb : ecbs) {
373                         ecb.onInfo(mp, dsd, what, extra);
374                     }
375                 }
376             }
377 
378             @Override
379             public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
380                 synchronized (cbLock) {
381                     for (MediaPlayer2.MediaPlayer2EventCallback ecb : ecbs) {
382                         ecb.onCallCompleted(mp, dsd, what, status);
383                     }
384                 }
385             }
386 
387             @Override
388             public void onMediaTimeDiscontinuity(MediaPlayer2 mp, DataSourceDesc dsd,
389                     MediaTimestamp timestamp) {
390                 synchronized (cbLock) {
391                     for (MediaPlayer2.MediaPlayer2EventCallback ecb : ecbs) {
392                         ecb.onMediaTimeDiscontinuity(mp, dsd, timestamp);
393                     }
394                 }
395             }
396 
397             @Override
398             public void onCommandLabelReached(MediaPlayer2 mp, Object label) {
399                 synchronized (cbLock) {
400                     for (MediaPlayer2.MediaPlayer2EventCallback ecb : ecbs) {
401                         ecb.onCommandLabelReached(mp, label);
402                     }
403                 }
404             }
405             @Override
406             public  void onSubtitleData(MediaPlayer2 mp, DataSourceDesc dsd,
407                     final SubtitleData data) {
408                 synchronized (cbLock) {
409                     for (MediaPlayer2.MediaPlayer2EventCallback ecb : ecbs) {
410                         ecb.onSubtitleData(mp, dsd, data);
411                     }
412                 }
413             }
414         });
415     }
416 
417     // returns true on success
loadResource(int resid)418     protected boolean loadResource(int resid) throws Exception {
419         /* FIXME: ensure device has capability.
420         if (!MediaUtils.hasCodecsForResource(mContext, resid)) {
421             return false;
422         }
423         */
424 
425         AssetFileDescriptor afd = mResources.openRawResourceFd(resid);
426         try {
427             mPlayer.setDataSource(new DataSourceDesc.Builder()
428                     .setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength())
429                     .build());
430         } finally {
431             // TODO: close afd only after setDataSource is confirmed.
432             // afd.close();
433         }
434         return true;
435     }
436 
createDataSourceDesc(int resid)437     protected DataSourceDesc createDataSourceDesc(int resid) throws Exception {
438         /* FIXME: ensure device has capability.
439         if (!MediaUtils.hasCodecsForResource(mContext, resid)) {
440             return null;
441         }
442         */
443 
444         AssetFileDescriptor afd = mResources.openRawResourceFd(resid);
445         return new DataSourceDesc.Builder()
446                 .setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength())
447                 .build();
448     }
449 
checkLoadResource(int resid)450     protected boolean checkLoadResource(int resid) throws Exception {
451         return loadResource(resid);
452 
453         /* FIXME: ensure device has capability.
454         return MediaUtils.check(loadResource(resid), "no decoder found");
455         */
456     }
457 
playLiveVideoTest(String path, int playTime)458     protected void playLiveVideoTest(String path, int playTime) throws Exception {
459         playVideoWithRetries(path, null, null, playTime);
460     }
461 
playLiveAudioOnlyTest(String path, int playTime)462     protected void playLiveAudioOnlyTest(String path, int playTime) throws Exception {
463         playVideoWithRetries(path, -1, -1, playTime);
464     }
465 
playVideoTest(String path, int width, int height)466     protected void playVideoTest(String path, int width, int height) throws Exception {
467         playVideoWithRetries(path, width, height, 0);
468     }
469 
playVideoWithRetries(String path, Integer width, Integer height, int playTime)470     protected void playVideoWithRetries(String path, Integer width, Integer height, int playTime)
471             throws Exception {
472         boolean playedSuccessfully = false;
473         final Uri uri = Uri.parse(path);
474         for (int i = 0; i < STREAM_RETRIES; i++) {
475             try {
476                 mPlayer.setDataSource(new DataSourceDesc.Builder()
477                         .setDataSource(mContext, uri)
478                         .build());
479                 playLoadedVideo(width, height, playTime);
480                 playedSuccessfully = true;
481                 break;
482             } catch (PrepareFailedException e) {
483                 // prepare() can fail because of network issues, so try again
484                 LOG.warning("prepare() failed on try " + i + ", trying playback again");
485             }
486         }
487         assertTrue("Stream did not play successfully after all attempts", playedSuccessfully);
488     }
489 
playVideoTest(int resid, int width, int height)490     protected void playVideoTest(int resid, int width, int height) throws Exception {
491         if (!checkLoadResource(resid)) {
492             return; // skip
493         }
494 
495         playLoadedVideo(width, height, 0);
496     }
497 
playLiveVideoTest( Uri uri, Map<String, String> headers, List<HttpCookie> cookies, int playTime)498     protected void playLiveVideoTest(
499             Uri uri, Map<String, String> headers, List<HttpCookie> cookies,
500             int playTime) throws Exception {
501         playVideoWithRetries(uri, headers, cookies, null /* width */, null /* height */, playTime);
502     }
503 
playVideoWithRetries( Uri uri, Map<String, String> headers, List<HttpCookie> cookies, Integer width, Integer height, int playTime)504     protected void playVideoWithRetries(
505             Uri uri, Map<String, String> headers, List<HttpCookie> cookies,
506             Integer width, Integer height, int playTime) throws Exception {
507         boolean playedSuccessfully = false;
508         for (int i = 0; i < STREAM_RETRIES; i++) {
509             try {
510                 mPlayer.setDataSource(new DataSourceDesc.Builder()
511                         .setDataSource(mContext,
512                             uri, headers, cookies)
513                         .build());
514                 playLoadedVideo(width, height, playTime);
515                 playedSuccessfully = true;
516                 break;
517             } catch (PrepareFailedException e) {
518                 // prepare() can fail because of network issues, so try again
519                 // playLoadedVideo already has reset the player so we can try again safely.
520                 LOG.warning("prepare() failed on try " + i + ", trying playback again");
521             }
522         }
523         assertTrue("Stream did not play successfully after all attempts", playedSuccessfully);
524     }
525 
526     /**
527      * Play a video which has already been loaded with setDataSource().
528      *
529      * @param width width of the video to verify, or null to skip verification
530      * @param height height of the video to verify, or null to skip verification
531      * @param playTime length of time to play video, or 0 to play entire video.
532      * with a non-negative value, this method stops the playback after the length of
533      * time or the duration the video is elapsed. With a value of -1,
534      * this method simply starts the video and returns immediately without
535      * stoping the video playback.
536      */
playLoadedVideo(final Integer width, final Integer height, int playTime)537     protected void playLoadedVideo(final Integer width, final Integer height, int playTime)
538             throws Exception {
539         final float volume = 0.5f;
540 
541         boolean audioOnly = (width != null && width.intValue() == -1)
542                 || (height != null && height.intValue() == -1);
543         mPlayer.setSurface(mActivity.getSurfaceHolder().getSurface());
544 
545         synchronized (mEventCbLock) {
546             mEventCallbacks.add(new MediaPlayer2.MediaPlayer2EventCallback() {
547                 @Override
548                 public void onVideoSizeChanged(MediaPlayer2 mp, DataSourceDesc dsd, int w, int h) {
549                     if (w == 0 && h == 0) {
550                         // A size of 0x0 can be sent initially one time when using NuPlayer.
551                         assertFalse(mOnVideoSizeChangedCalled.isSignalled());
552                         return;
553                     }
554                     mOnVideoSizeChangedCalled.signal();
555                     if (width != null) {
556                         assertEquals(width.intValue(), w);
557                     }
558                     if (height != null) {
559                         assertEquals(height.intValue(), h);
560                     }
561                 }
562 
563                 @Override
564                 public void onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
565                     fail("Media player had error " + what + " playing video");
566                 }
567 
568                 @Override
569                 public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
570                     if (what == MediaPlayer2.MEDIA_INFO_VIDEO_RENDERING_START) {
571                         mOnVideoRenderingStartCalled.signal();
572                     } else if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
573                         mOnPrepareCalled.signal();
574                     }
575                 }
576 
577                 @Override
578                 public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd,
579                         int what, int status) {
580                     if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
581                         mOnPlayCalled.signal();
582                     }
583                 }
584             });
585         }
586         try {
587             mOnPrepareCalled.reset();
588             mPlayer.prepare();
589             mOnPrepareCalled.waitForSignal();
590         } catch (Exception e) {
591             mPlayer.reset();
592             throw new PrepareFailedException();
593         }
594 
595         mOnPlayCalled.reset();
596         mPlayer.play();
597         mOnPlayCalled.waitForSignal();
598         if (!audioOnly) {
599             mOnVideoSizeChangedCalled.waitForSignal();
600             mOnVideoRenderingStartCalled.waitForSignal();
601         }
602         mPlayer.setPlayerVolume(volume);
603 
604         // waiting to complete
605         if (playTime == -1) {
606             return;
607         } else if (playTime == 0) {
608             while (mPlayer.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING) {
609                 Thread.sleep(SLEEP_TIME);
610             }
611         } else {
612             Thread.sleep(playTime);
613         }
614 
615         // validate a few MediaMetrics.
616         PersistableBundle metrics = mPlayer.getMetrics();
617         if (metrics == null) {
618             fail("MediaPlayer.getMetrics() returned null metrics");
619         } else if (metrics.isEmpty()) {
620             fail("MediaPlayer.getMetrics() returned empty metrics");
621         } else {
622 
623             int size = metrics.size();
624             Set<String> keys = metrics.keySet();
625 
626             if (keys == null) {
627                 fail("MediaMetricsSet returned no keys");
628             } else if (keys.size() != size) {
629                 fail("MediaMetricsSet.keys().size() mismatch MediaMetricsSet.size()");
630             }
631 
632             // we played something; so one of these should be non-null
633             String vmime = metrics.getString(MediaPlayer2.MetricsConstants.MIME_TYPE_VIDEO, null);
634             String amime = metrics.getString(MediaPlayer2.MetricsConstants.MIME_TYPE_AUDIO, null);
635             if (vmime == null && amime == null) {
636                 fail("getMetrics() returned neither video nor audio mime value");
637             }
638 
639             long duration = metrics.getLong(MediaPlayer2.MetricsConstants.DURATION, -2);
640             if (duration == -2) {
641                 fail("getMetrics() didn't return a duration");
642             }
643             long playing = metrics.getLong(MediaPlayer2.MetricsConstants.PLAYING, -2);
644             if (playing == -2) {
645                 fail("getMetrics() didn't return a playing time");
646             }
647             if (!keys.contains(MediaPlayer2.MetricsConstants.PLAYING)) {
648                 fail("MediaMetricsSet.keys() missing: " + MediaPlayer2.MetricsConstants.PLAYING);
649             }
650         }
651         mPlayer.reset();
652     }
653 
654     private static class PrepareFailedException extends Exception {}
655 
setOnErrorListener()656     protected void setOnErrorListener() {
657         synchronized (mEventCbLock) {
658             mEventCallbacks.add(new MediaPlayer2.MediaPlayer2EventCallback() {
659                 @Override
660                 public void onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
661                     mOnErrorCalled.signal();
662                 }
663             });
664         }
665     }
666 }
667