1 /* 2 * Copyright (C) 2021 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 17 package android.device.stressmodes; 18 19 import android.content.ContentResolver; 20 import android.content.Context; 21 import android.content.res.AssetFileDescriptor; 22 import android.media.MediaCodec; 23 import android.media.MediaExtractor; 24 import android.media.MediaMuxer; 25 import android.net.Uri; 26 import android.util.Log; 27 import android.media.MediaFormat; 28 29 import androidx.test.InstrumentationRegistry; 30 import androidx.test.internal.runner.listener.InstrumentationRunListener; 31 32 import org.junit.runner.Description; 33 34 import java.io.File; 35 import java.nio.ByteBuffer; 36 import java.util.HashMap; 37 import java.util.concurrent.ExecutorService; 38 import java.util.concurrent.Executors; 39 40 /** 41 * This stressor test read file from a mp4 file and write to a new file to simulate the IO 42 * contention. 43 */ 44 public class IOContentionStressTestMode extends InstrumentationRunListener { 45 private static final String TAG = IOContentionStressTestMode.class.getSimpleName(); 46 private Context mContext; 47 private static final int MAX_SAMPLE_SIZE = 256 * 1024; 48 private ContentResolver mContentResolver; 49 private boolean mStop = false; 50 private ExecutorService mExecutorService = null; 51 IOContentionStressTestMode()52 public IOContentionStressTestMode() { 53 mContext = InstrumentationRegistry.getInstrumentation().getContext(); 54 InstrumentationRegistry.getInstrumentation() 55 .getUiAutomation() 56 .adoptShellPermissionIdentity("android.permission.WRITE_MEDIA_STORAGE"); 57 mContentResolver = mContext.getContentResolver(); 58 mExecutorService = Executors.newSingleThreadExecutor(); 59 } 60 doCopyFile()61 public final void doCopyFile() throws Exception { 62 Log.i(TAG, "doCopyFile"); 63 String path = "/data/local/tmp/testHevc.mp4"; 64 final File file = new File(path); 65 66 Log.i(TAG, "Copying file " + file); 67 68 // Create a file Uri: file:///data/user/0/android.media.cts/cache/HevcTranscode.mp4 69 Uri destinationUri = 70 Uri.parse( 71 ContentResolver.SCHEME_FILE 72 + "://" 73 + mContext.getCacheDir().getAbsolutePath() 74 + "/HevcTranscode.mp4"); 75 76 // Set up MediaExtractor to read from the source. 77 AssetFileDescriptor srcFd = 78 mContentResolver.openAssetFileDescriptor(Uri.fromFile(file), "r"); 79 AssetFileDescriptor dstFd = null; 80 MediaExtractor extractor = null; 81 MediaMuxer muxer = null; 82 83 try { 84 extractor = new MediaExtractor(); 85 extractor.setDataSource( 86 srcFd.getFileDescriptor(), srcFd.getStartOffset(), srcFd.getLength()); 87 88 int trackCount = extractor.getTrackCount(); 89 90 // Set up MediaMuxer for the destination. 91 muxer = 92 new MediaMuxer( 93 destinationUri.getPath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); 94 95 // Set up the tracks. 96 HashMap<Integer, Integer> indexMap = new HashMap<Integer, Integer>(trackCount); 97 for (int i = 0; i < trackCount; i++) { 98 extractor.selectTrack(i); 99 MediaFormat format = extractor.getTrackFormat(i); 100 int dstIndex = muxer.addTrack(format); 101 indexMap.put(i, dstIndex); 102 } 103 104 // Copy the samples from MediaExtractor to MediaMuxer. 105 boolean sawEOS = false; 106 int bufferSize = MAX_SAMPLE_SIZE; 107 int frameCount = 0; 108 int offset = 100; 109 110 ByteBuffer dstBuf = ByteBuffer.allocate(bufferSize); 111 MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); 112 113 muxer.start(); 114 while (!sawEOS && !mStop) { 115 bufferInfo.offset = offset; 116 bufferInfo.size = extractor.readSampleData(dstBuf, offset); 117 118 if (bufferInfo.size < 0) { 119 sawEOS = true; 120 bufferInfo.size = 0; 121 } else { 122 bufferInfo.presentationTimeUs = extractor.getSampleTime(); 123 bufferInfo.flags = extractor.getSampleFlags(); 124 int trackIndex = extractor.getSampleTrackIndex(); 125 126 muxer.writeSampleData(indexMap.get(trackIndex), dstBuf, bufferInfo); 127 extractor.advance(); 128 129 frameCount++; 130 } 131 } 132 } finally { 133 if (muxer != null) { 134 muxer.stop(); 135 muxer.release(); 136 } 137 if (extractor != null) { 138 extractor.release(); 139 } 140 if (srcFd != null) { 141 srcFd.close(); 142 } 143 } 144 } 145 146 @Override testStarted(Description description)147 public final void testStarted(Description description) throws Exception { 148 mExecutorService = Executors.newSingleThreadExecutor(); 149 mStop = false; 150 Log.i(TAG, "IOContentionStressTestMode stress started"); 151 mExecutorService.submit( 152 new Runnable() { 153 @Override 154 public void run() { 155 while (!mStop) { 156 try { 157 doCopyFile(); 158 } catch (Exception ex) { 159 Log.e(TAG, "Copy file get exception: " + ex); 160 } 161 } 162 } 163 }); 164 } 165 166 @Override testFinished(Description description)167 public final void testFinished(Description description) throws Exception { 168 Log.i(TAG, "IOContentionStressTestMode stress finished"); 169 mStop = true; 170 if (mExecutorService != null) { 171 mExecutorService.shutdown(); 172 } 173 } 174 } 175