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