1 /* 2 * Copyright (C) 2012 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.content.res.AssetFileDescriptor; 19 import android.content.res.Resources; 20 import android.media.MediaRecorder; 21 import android.media.MediaPlayer; 22 import android.os.Environment; 23 import android.platform.test.annotations.AppModeFull; 24 import android.test.ActivityInstrumentationTestCase2; 25 import android.util.Log; 26 import android.view.SurfaceHolder; 27 28 import java.util.Random; 29 30 /** 31 * Tests for the MediaPlayer.java and MediaRecorder.java APIs 32 * 33 * These testcases make randomized calls to the public APIs available, and 34 * the focus is on whether the randomized calls can lead to crash in 35 * mediaserver process and/or ANRs. 36 * 37 * The files in res/raw used by testLocalVideo* are (c) copyright 2008, 38 * Blender Foundation / www.bigbuckbunny.org, and are licensed under the Creative Commons 39 * Attribution 3.0 License at http://creativecommons.org/licenses/by/3.0/us/. 40 */ 41 @NonMediaMainlineTest 42 @MediaHeavyPresubmitTest 43 @AppModeFull(reason = "TODO: evaluate and port to instant") 44 public class MediaRandomTest extends ActivityInstrumentationTestCase2<MediaStubActivity> { 45 private static final String TAG = "MediaRandomTest"; 46 47 private static final String OUTPUT_FILE = 48 Environment.getExternalStorageDirectory().toString() + "/record.3gp"; 49 50 private static final int NUMBER_OF_RECORDER_RANDOM_ACTIONS = 100000; 51 private static final int NUMBER_OF_PLAYER_RANDOM_ACTIONS = 100000; 52 53 private MediaRecorder mRecorder; 54 private MediaPlayer mPlayer; 55 private SurfaceHolder mSurfaceHolder; 56 private Resources mResources; 57 58 // Modified across multiple threads 59 private volatile boolean mMediaServerDied; 60 private volatile int mAction; 61 private volatile int mParam; 62 63 @Override setUp()64 protected void setUp() throws Exception { 65 super.setUp(); 66 getInstrumentation().waitForIdleSync(); 67 mMediaServerDied = false; 68 mSurfaceHolder = getActivity().getSurfaceHolder(); 69 mResources = getInstrumentation().getTargetContext().getResources(); 70 try { 71 // Running this on UI thread make sure that 72 // onError callback can be received. 73 runTestOnUiThread(new Runnable() { 74 public void run() { 75 mRecorder = new MediaRecorder(); 76 mPlayer = new MediaPlayer(); 77 } 78 }); 79 } catch (Throwable e) { 80 e.printStackTrace(); 81 fail(); 82 } 83 } 84 85 @Override tearDown()86 protected void tearDown() throws Exception { 87 if (mRecorder != null) { 88 mRecorder.release(); 89 mRecorder = null; 90 } 91 if (mPlayer != null) { 92 mPlayer.release(); 93 mPlayer = null; 94 } 95 super.tearDown(); 96 } 97 98 /** 99 * This is a watchdog used to stop the process if it hasn't been pinged 100 * for more than specified milli-seconds. It is used like: 101 * 102 * Watchdog w = new Watchdog(10000); // 10 seconds. 103 * w.start(); // start the watchdog. 104 * ... 105 * w.ping(); 106 * ... 107 * w.ping(); 108 * ... 109 * w.end(); // ask the watchdog to stop. 110 * w.join(); // join the thread. 111 */ 112 class Watchdog extends Thread { 113 private final long mTimeoutMs; 114 private boolean mWatchdogStop; 115 private boolean mWatchdogPinged; 116 Watchdog(long timeoutMs)117 public Watchdog(long timeoutMs) { 118 mTimeoutMs = timeoutMs; 119 mWatchdogStop = false; 120 mWatchdogPinged = false; 121 } 122 run()123 public synchronized void run() { 124 while (true) { 125 // avoid early termination by "spurious" waitup. 126 final long startTimeMs = System.currentTimeMillis(); 127 long remainingWaitTimeMs = mTimeoutMs; 128 do { 129 try { 130 wait(remainingWaitTimeMs); 131 } catch (InterruptedException ex) { 132 // ignore. 133 } 134 remainingWaitTimeMs = mTimeoutMs - (System.currentTimeMillis() - startTimeMs); 135 } while (remainingWaitTimeMs > 0); 136 137 if (mWatchdogStop) { 138 break; 139 } 140 141 if (!mWatchdogPinged) { 142 fail("Action " + mAction + " Param " + mParam 143 + " waited over " + (mTimeoutMs - remainingWaitTimeMs) + " ms"); 144 return; 145 } 146 mWatchdogPinged = false; 147 } 148 } 149 ping()150 public synchronized void ping() { 151 mWatchdogPinged = true; 152 this.notify(); 153 } 154 end()155 public synchronized void end() { 156 mWatchdogStop = true; 157 this.notify(); 158 } 159 } 160 MediaRandomTest()161 public MediaRandomTest() { 162 super("android.media.cts", MediaStubActivity.class); 163 } 164 loadSource(int resid)165 private void loadSource(int resid) throws Exception { 166 AssetFileDescriptor afd = mResources.openRawResourceFd(resid); 167 try { 168 mPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), 169 afd.getLength()); 170 } finally { 171 afd.close(); 172 } 173 } testPlayerRandomActionAV1()174 public void testPlayerRandomActionAV1() throws Exception { 175 testPlayerRandomAction(R.raw.video_480x360_webm_av1_400kbps_30fps_vorbis_stereo_128kbps_48000hz); 176 } testPlayerRandomActionH264()177 public void testPlayerRandomActionH264() throws Exception { 178 testPlayerRandomAction(R.raw.video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz); 179 } testPlayerRandomActionHEVC()180 public void testPlayerRandomActionHEVC() throws Exception { 181 testPlayerRandomAction(R.raw.video_480x360_mp4_hevc_650kbps_30fps_aac_stereo_128kbps_48000hz); 182 } testPlayerRandomActionMpeg2()183 public void testPlayerRandomActionMpeg2() throws Exception { 184 testPlayerRandomAction(R.raw.video_480x360_mp4_mpeg2_1500kbps_30fps_aac_stereo_128kbps_48000hz); 185 } testPlayerRandomAction(int resid)186 private void testPlayerRandomAction(int resid) throws Exception { 187 Watchdog watchdog = new Watchdog(5000); 188 try { 189 mPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() { 190 @Override 191 public boolean onError(MediaPlayer mp, int what, int extra) { 192 if (mPlayer == mp && 193 what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) { 194 Log.e(TAG, "mediaserver process died"); 195 mMediaServerDied = true; 196 } 197 return true; 198 } 199 }); 200 loadSource(resid); 201 mPlayer.setDisplay(mSurfaceHolder); 202 mPlayer.prepare(); 203 mPlayer.start(); 204 205 long seed = System.currentTimeMillis(); 206 Log.v(TAG, "seed = " + seed); 207 Random r = new Random(seed); 208 209 watchdog.start(); 210 for (int i = 0; i < NUMBER_OF_PLAYER_RANDOM_ACTIONS; i++){ 211 watchdog.ping(); 212 assertTrue(!mMediaServerDied); 213 214 mAction = (int)(r.nextInt() % 12); 215 mParam = (int)(r.nextInt() % 1000000); 216 try { 217 switch (mAction) { 218 case 0: 219 mPlayer.getCurrentPosition(); 220 break; 221 case 1: 222 mPlayer.getDuration(); 223 break; 224 case 2: 225 mPlayer.getVideoHeight(); 226 break; 227 case 3: 228 mPlayer.getVideoWidth(); 229 break; 230 case 4: 231 mPlayer.isPlaying(); 232 break; 233 case 5: 234 mPlayer.pause(); 235 break; 236 case 6: 237 // Don't add mPlayer.prepare() call here for two reasons: 238 // 1. calling prepare() is a bad idea since it is a blocking call, and 239 // 2. when prepare() is in progress, mediaserver died message will not be sent to apps 240 mPlayer.prepareAsync(); 241 break; 242 case 7: 243 mPlayer.seekTo((int)(mParam)); 244 break; 245 case 8: 246 mPlayer.setLooping(mParam % 2 == 0); 247 break; 248 case 9: 249 mPlayer.setVolume((mParam % 1000) / 500.0f, 250 (mParam / 1000) / 500.0f); 251 break; 252 case 10: 253 mPlayer.start(); 254 break; 255 case 11: 256 Thread.sleep(mParam % 20); 257 break; 258 } 259 } catch (Exception e) { 260 } 261 } 262 mPlayer.stop(); 263 } catch (Exception e) { 264 Log.v(TAG, e.toString()); 265 } finally { 266 watchdog.end(); 267 watchdog.join(); 268 } 269 } 270 testRecorderRandomAction()271 public void testRecorderRandomAction() throws Exception { 272 Watchdog watchdog = new Watchdog(5000); 273 try { 274 long seed = System.currentTimeMillis(); 275 Log.v(TAG, "seed = " + seed); 276 Random r = new Random(seed); 277 278 mMediaServerDied = false; 279 mRecorder.setOnErrorListener(new MediaRecorder.OnErrorListener() { 280 @Override 281 public void onError(MediaRecorder recorder, int what, int extra) { 282 if (mRecorder == recorder && 283 what == MediaRecorder.MEDIA_ERROR_SERVER_DIED) { 284 Log.e(TAG, "mediaserver process died"); 285 mMediaServerDied = true; 286 } 287 } 288 }); 289 290 final int[] width = {176, 352, 320, 640, 1280, 1920}; 291 final int[] height = {144, 288, 240, 480, 720, 1080}; 292 final int[] audioSource = { 293 MediaRecorder.AudioSource.DEFAULT, 294 MediaRecorder.AudioSource.MIC, 295 MediaRecorder.AudioSource.CAMCORDER, 296 }; 297 298 watchdog.start(); 299 for (int i = 0; i < NUMBER_OF_RECORDER_RANDOM_ACTIONS; i++) { 300 watchdog.ping(); 301 assertTrue(!mMediaServerDied); 302 303 mAction = (int)(r.nextInt(14)); 304 mParam = (int)(r.nextInt(1000000)); 305 try { 306 switch (mAction) { 307 case 0: { 308 // We restrict the audio sources because setting some sources 309 // may cause 2+ second delays because the input device may 310 // retry - loop (e.g. VOICE_UPLINK for voice call to be initiated). 311 final int index = mParam % audioSource.length; 312 mRecorder.setAudioSource(audioSource[index]); 313 break; 314 } 315 case 1: 316 // XXX: 317 // Fix gralloc source and change 318 // mRecorder.setVideoSource(mParam % 3); 319 mRecorder.setVideoSource(mParam % 2); 320 break; 321 case 2: 322 mRecorder.setOutputFormat(mParam % 5); 323 break; 324 case 3: 325 mRecorder.setAudioEncoder(mParam % 3); 326 break; 327 case 4: 328 mRecorder.setVideoEncoder(mParam % 5); 329 break; 330 case 5: 331 mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface()); 332 break; 333 case 6: 334 int index = mParam % width.length; 335 mRecorder.setVideoSize(width[index], height[index]); 336 break; 337 case 7: 338 mRecorder.setVideoFrameRate(mParam % 40 - 5); 339 break; 340 case 8: 341 mRecorder.setOutputFile(OUTPUT_FILE); 342 break; 343 case 9: 344 mRecorder.prepare(); 345 break; 346 case 10: 347 mRecorder.start(); 348 break; 349 case 11: 350 Thread.sleep(mParam % 20); 351 break; 352 case 12: 353 mRecorder.stop(); 354 break; 355 case 13: 356 mRecorder.reset(); 357 break; 358 default: 359 break; 360 } 361 } catch (Exception e) { 362 } 363 } 364 } catch (Exception e) { 365 Log.v(TAG, e.toString()); 366 } finally { 367 watchdog.end(); 368 watchdog.join(); 369 } 370 } 371 } 372