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 
17 package android.media.misc.cts;
18 
19 import static org.junit.Assert.assertTrue;
20 
21 import android.content.res.AssetFileDescriptor;
22 import android.media.MediaExtractor;
23 import android.media.cts.MediaTestBase;
24 import android.media.cts.TestUtils.Monitor;
25 import android.os.Build;
26 import android.os.ParcelFileDescriptor;
27 import android.platform.test.annotations.AppModeFull;
28 import android.platform.test.annotations.Presubmit;
29 import android.platform.test.annotations.RequiresDevice;
30 import android.util.Log;
31 import android.webkit.cts.CtsTestServer;
32 
33 import androidx.test.ext.junit.runners.AndroidJUnit4;
34 import androidx.test.filters.SmallTest;
35 
36 import com.android.compatibility.common.util.ApiLevelUtil;
37 import com.android.compatibility.common.util.Preconditions;
38 
39 import org.apache.http.impl.DefaultHttpServerConnection;
40 import org.apache.http.impl.io.SocketOutputBuffer;
41 import org.apache.http.io.SessionOutputBuffer;
42 import org.apache.http.params.HttpParams;
43 import org.apache.http.util.CharArrayBuffer;
44 import org.junit.After;
45 import org.junit.Before;
46 import org.junit.Test;
47 import org.junit.runner.RunWith;
48 
49 import java.io.File;
50 import java.io.FileNotFoundException;
51 import java.io.IOException;
52 import java.net.Socket;
53 import java.util.Map;
54 import java.util.Set;
55 import java.util.UUID;
56 import java.util.concurrent.CountDownLatch;
57 import java.util.concurrent.TimeUnit;
58 
59 @SmallTest
60 @RequiresDevice
61 @AppModeFull(reason = "TODO: evaluate and port to instant")
62 @RunWith(AndroidJUnit4.class)
63 public class NativeDecoderTest extends MediaTestBase {
64     private static final String TAG = "DecoderTest";
65 
66     private static final boolean sIsAtLeastS = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.S);
67 
68     static final String mInpPrefix = WorkDir.getMediaDirString();
69     short[] mMasterBuffer;
70 
71     static {
72         // Load jni on initialization.
73         Log.i("@@@", "before loadlibrary");
74         System.loadLibrary("ctsmediamisc_jni");
75         Log.i("@@@", "after loadlibrary");
76     }
77 
78     @Before
79     @Override
setUp()80     public void setUp() throws Throwable {
81         super.setUp();
82     }
83 
84     @After
85     @Override
tearDown()86     public void tearDown() {
87         super.tearDown();
88     }
89 
getAssetFileDescriptorFor(final String res)90     protected static AssetFileDescriptor getAssetFileDescriptorFor(final String res)
91             throws FileNotFoundException {
92         Preconditions.assertTestFileExists(mInpPrefix + res);
93         File inpFile = new File(mInpPrefix + res);
94         ParcelFileDescriptor parcelFD =
95                 ParcelFileDescriptor.open(inpFile, ParcelFileDescriptor.MODE_READ_ONLY);
96         return new AssetFileDescriptor(parcelFD, 0, parcelFD.getStatSize());
97     }
98 
99     @Presubmit
100     @Test
testFormat()101     public void testFormat() throws Exception {
102         assertTrue("media format fail, see log for details", testFormatNative());
103     }
104 
testFormatNative()105     private static native boolean testFormatNative();
106 
107     @Presubmit
108     @Test
testPssh()109     public void testPssh() throws Exception {
110         testPssh("psshtest.mp4");
111     }
112 
testPssh(final String res)113     private void testPssh(final String res) throws Exception {
114         AssetFileDescriptor fd = getAssetFileDescriptorFor(res);
115 
116         MediaExtractor ex = new MediaExtractor();
117         ex.setDataSource(fd.getParcelFileDescriptor().getFileDescriptor(),
118                 fd.getStartOffset(), fd.getLength());
119         testPssh(ex);
120         ex.release();
121 
122         boolean ret = testPsshNative(
123                 fd.getParcelFileDescriptor().getFd(), fd.getStartOffset(), fd.getLength());
124         assertTrue("native pssh error", ret);
125     }
126 
testPssh(MediaExtractor ex)127     private static void testPssh(MediaExtractor ex) {
128         Map<UUID, byte[]> map = ex.getPsshInfo();
129         Set<UUID> keys = map.keySet();
130         for (UUID uuid: keys) {
131             Log.i("@@@", "uuid: " + uuid + ", data size " +
132                     map.get(uuid).length);
133         }
134     }
135 
testPsshNative(int fd, long offset, long size)136     private static native boolean testPsshNative(int fd, long offset, long size);
137 
138     @Test
testCryptoInfo()139     public void testCryptoInfo() throws Exception {
140         assertTrue("native cryptoinfo failed, see log for details", testCryptoInfoNative());
141     }
142 
testCryptoInfoNative()143     private static native boolean testCryptoInfoNative();
144 
145     @Presubmit
146     @Test
testMediaFormat()147     public void testMediaFormat() throws Exception {
148         assertTrue("native mediaformat failed, see log for details", testMediaFormatNative());
149     }
150 
testMediaFormatNative()151     private static native boolean testMediaFormatNative();
152 
153     @Presubmit
154     @Test
testAMediaDataSourceClose()155     public void testAMediaDataSourceClose() throws Throwable {
156 
157         final CtsTestServer slowServer = new SlowCtsTestServer();
158         final String url = slowServer.getAssetUrl("noiseandchirps.ogg");
159         final long ds = createAMediaDataSource(url);
160         final long ex = createAMediaExtractor();
161 
162         try {
163             setAMediaExtractorDataSourceAndFailIfAnr(ex, ds);
164         } finally {
165             slowServer.shutdown();
166             deleteAMediaExtractor(ex);
167             deleteAMediaDataSource(ds);
168         }
169 
170     }
171 
setAMediaExtractorDataSourceAndFailIfAnr(final long ex, final long ds)172     private void setAMediaExtractorDataSourceAndFailIfAnr(final long ex, final long ds)
173             throws Throwable {
174         final Monitor setAMediaExtractorDataSourceDone = new Monitor();
175         final int HEAD_START_MILLIS = 1000;
176         final int ANR_TIMEOUT_MILLIS = 2500;
177         final int JOIN_TIMEOUT_MILLIS = 1500;
178 
179         Thread setAMediaExtractorDataSourceThread = new Thread() {
180             public void run() {
181                 setAMediaExtractorDataSource(ex, ds);
182                 setAMediaExtractorDataSourceDone.signal();
183             }
184         };
185 
186         try {
187             setAMediaExtractorDataSourceThread.start();
188             Thread.sleep(HEAD_START_MILLIS);
189             closeAMediaDataSource(ds);
190             boolean closed = setAMediaExtractorDataSourceDone.waitForSignal(ANR_TIMEOUT_MILLIS);
191             assertTrue("close took longer than " + ANR_TIMEOUT_MILLIS, closed);
192         } finally {
193             setAMediaExtractorDataSourceThread.join(JOIN_TIMEOUT_MILLIS);
194         }
195 
196     }
197 
198     private class SlowCtsTestServer extends CtsTestServer {
199 
200         private static final int SERVER_DELAY_MILLIS = 5000;
201         private final CountDownLatch mDisconnected = new CountDownLatch(1);
202 
SlowCtsTestServer()203         SlowCtsTestServer() throws Exception {
204             super(mContext);
205         }
206 
207         @Override
createHttpServerConnection()208         protected DefaultHttpServerConnection createHttpServerConnection() {
209             return new SlowHttpServerConnection(mDisconnected, SERVER_DELAY_MILLIS);
210         }
211 
212         @Override
shutdown()213         public void shutdown() {
214             mDisconnected.countDown();
215             super.shutdown();
216         }
217     }
218 
219     private static class SlowHttpServerConnection extends DefaultHttpServerConnection {
220 
221         private final CountDownLatch mDisconnected;
222         private final int mDelayMillis;
223 
SlowHttpServerConnection(CountDownLatch disconnected, int delayMillis)224         public SlowHttpServerConnection(CountDownLatch disconnected, int delayMillis) {
225             mDisconnected = disconnected;
226             mDelayMillis = delayMillis;
227         }
228 
229         @Override
createHttpDataTransmitter( Socket socket, int buffersize, HttpParams params)230         protected SessionOutputBuffer createHttpDataTransmitter(
231                 Socket socket, int buffersize, HttpParams params) throws IOException {
232             return createSessionOutputBuffer(socket, buffersize, params);
233         }
234 
createSessionOutputBuffer( Socket socket, int buffersize, HttpParams params)235         SessionOutputBuffer createSessionOutputBuffer(
236                 Socket socket, int buffersize, HttpParams params) throws IOException {
237             return new SocketOutputBuffer(socket, buffersize, params) {
238                 @Override
239                 public void write(byte[] b) throws IOException {
240                     write(b, 0, b.length);
241                 }
242 
243                 @Override
244                 public void write(byte[] b, int off, int len) throws IOException {
245                     while (len-- > 0) {
246                         write(b[off++]);
247                     }
248                 }
249 
250                 @Override
251                 public void writeLine(String s) throws IOException {
252                     delay();
253                     super.writeLine(s);
254                 }
255 
256                 @Override
257                 public void writeLine(CharArrayBuffer buffer) throws IOException {
258                     delay();
259                     super.writeLine(buffer);
260                 }
261 
262                 @Override
263                 public void write(int b) throws IOException {
264                     delay();
265                     super.write(b);
266                 }
267 
268                 private void delay() throws IOException {
269                     try {
270                         mDisconnected.await(mDelayMillis, TimeUnit.MILLISECONDS);
271                     } catch (InterruptedException e) {
272                         // Ignored
273                     }
274                 }
275 
276             };
277         }
278     }
279 
280     private static native long createAMediaExtractor();
281     private static native long createAMediaDataSource(String url);
282     private static native int  setAMediaExtractorDataSource(long ex, long ds);
283     private static native void closeAMediaDataSource(long ds);
284     private static native void deleteAMediaExtractor(long ex);
285     private static native void deleteAMediaDataSource(long ds);
286 
287 }
288 
289