Merge changes I1e21ab46,Ic4f298d7,I8d1c0738,I84b93d5a into qt-dev am: 68e06d0995 am: 1fc1637595

Original change: https://googleplex-android-review.googlesource.com/c/platform/cts/+/12630906

Change-Id: I7ebdd53f8cc44386ec528dd633d3cea52ad097b1
diff --git a/tests/tests/security/res/raw/cve_2018_9412.mp3 b/tests/tests/security/res/raw/cve_2018_9412.mp3
new file mode 100644
index 0000000..23391b3
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2018_9412.mp3
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_1989_h264.mp4 b/tests/tests/security/res/raw/cve_2019_1989_h264.mp4
new file mode 100644
index 0000000..bc6f1ed
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_1989_h264.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_1989_info.mp4 b/tests/tests/security/res/raw/cve_2019_1989_info.mp4
new file mode 100644
index 0000000..57b59b5
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_1989_info.mp4
@@ -0,0 +1,46 @@
+1 706
+1 27
+1 8
+0 59463
+0 8879
+0 1702
+0 1196
+0 967
+0 6219
+0 1727
+0 542
+0 600
+0 9607
+0 2226
+0 763
+0 752
+0 10565
+0 2636
+0 966
+0 1030
+0 11734
+0 3270
+0 1056
+0 1003
+0 11993
+0 3253
+0 1066
+0 1091
+0 12431
+0 3425
+0 1118
+0 1149
+0 5523
+0 6016
+0 5999
+0 5897
+0 12690
+0 3997
+0 1127
+1 27
+1 7
+0 39989
+0 2702
+0 4955
+0 4868
+0 5516
diff --git a/tests/tests/security/res/raw/cve_2019_2222_framelen.mp4 b/tests/tests/security/res/raw/cve_2019_2222_framelen.mp4
new file mode 100644
index 0000000..a4bc980
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_2222_framelen.mp4
@@ -0,0 +1,8 @@
+35
+28
+12
+6
+10
+31
+8
+130
diff --git a/tests/tests/security/res/raw/cve_2019_2222_hevc.mp4 b/tests/tests/security/res/raw/cve_2019_2222_hevc.mp4
new file mode 100644
index 0000000..26b8007
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_2222_hevc.mp4
Binary files differ
diff --git a/tests/tests/security/src/android/security/cts/StagefrightTest.java b/tests/tests/security/src/android/security/cts/StagefrightTest.java
index 6b3bb26..157b98a 100644
--- a/tests/tests/security/src/android/security/cts/StagefrightTest.java
+++ b/tests/tests/security/src/android/security/cts/StagefrightTest.java
@@ -1376,6 +1376,12 @@
     }
 
     @Test
+    @SecurityTest(minPatchLevel = "2018-07")
+    public void testStagefright_cve_2018_9412() throws Exception {
+        doStagefrightTest(R.raw.cve_2018_9412, 180000);
+    }
+
+    @Test
     @SecurityTest(minPatchLevel = "Unknown")
     public void testStagefright_bug_142641801() throws Exception {
         assumeFalse(ModuleDetector.moduleIsPlayManaged(
@@ -1582,10 +1588,37 @@
         doStagefrightTest(R.raw.cve_2016_3879, new CrashUtils.Config().checkMinAddress(false));
     }
 
+    /***********************************************************
+     to prevent merge conflicts, add P tests below this comment,
+     before any existing test methods
+     ***********************************************************/
+
+    @Test
+    @SecurityTest(minPatchLevel = "2019-12")
+    public void testStagefright_cve_2019_2222() throws Exception {
+        int[] frameSizes = getFrameSizes(R.raw.cve_2019_2222_framelen);
+        doStagefrightTestRawBlob(R.raw.cve_2019_2222_hevc, "video/hevc", 320, 240, frameSizes);
+    }
+
     private void doStagefrightTest(final int rid) throws Exception {
         doStagefrightTest(rid, null);
     }
 
+    /***********************************************************
+     to prevent merge conflicts, add Q tests below this comment,
+     before any existing test methods
+     ***********************************************************/
+
+    @Test
+    @SecurityTest(minPatchLevel = "2019-03")
+    public void testStagefright_cve_2019_1989() throws Exception {
+        Object obj[] = getFrameInfo(R.raw.cve_2019_1989_info);
+        int[] isHeader = (int[])obj [0];
+        int[] frameSizes = (int[])obj [1];
+        doStagefrightTestRawBlob(R.raw.cve_2019_1989_h264, "video/avc",
+                1920, 1080, frameSizes, isHeader, new CrashUtils.Config());
+    }
+
     private void doStagefrightTest(final int rid, CrashUtils.Config config) throws Exception {
         NetworkSecurityPolicy policy = NetworkSecurityPolicy.getInstance();
         policy.setCleartextTrafficPermitted(true);
@@ -2374,6 +2407,25 @@
         return frameSizes;
     }
 
+    private Object[] getFrameInfo(int rid) throws IOException {
+        final Context context = getInstrumentation().getContext();
+        final Resources resources = context.getResources();
+        AssetFileDescriptor fd = resources.openRawResourceFd(rid);
+        FileInputStream fis = fd.createInputStream();
+        byte[] frameInfo = new byte[(int) fd.getLength()];
+        fis.read(frameInfo);
+        fis.close();
+        String[] lines = new String(frameInfo).trim().split("\\r?\\n");
+        int isHeader[] = new int[lines.length];
+        int frameSizes[] = new int[lines.length];
+        for (int i = 0; i < lines.length; i++) {
+            String[] values = lines[i].trim().split("\\s+");
+            isHeader[i] = Integer.parseInt(values[0]);
+            frameSizes[i] = Integer.parseInt(values[1]);
+        }
+        return new Object[] {isHeader, frameSizes};
+    }
+
     private void runWithTimeout(Runnable runner, int timeout) {
         Thread t = new Thread(runner);
         t.start();
@@ -2658,6 +2710,136 @@
         thr.join();
     }
 
+    private void doStagefrightTestRawBlob(int rid, String mime, int initWidth, int initHeight,
+            int frameSizes[], int isHeader[], CrashUtils.Config config) throws Exception {
+
+        final MediaPlayerCrashListener mpcl = new MediaPlayerCrashListener(config);
+        final Context context = getInstrumentation().getContext();
+        final Resources resources = context.getResources();
+        LooperThread thr = new LooperThread(new Runnable() {
+            @Override
+            public void run() {
+                MediaPlayer mp = new MediaPlayer();
+                mp.setOnErrorListener(mpcl);
+                AssetFileDescriptor fd = null;
+                try {
+                    fd = resources.openRawResourceFd(R.raw.good);
+                    // the onErrorListener won't receive MEDIA_ERROR_SERVER_DIED until
+                    // setDataSource has been called
+                    mp.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
+                    fd.close();
+                } catch (Exception e) {
+                    // this is a known-good file, so no failure should occur
+                    fail("setDataSource of known-good file failed");
+                }
+                synchronized (mpcl) {
+                    mpcl.notify();
+                }
+                Looper.loop();
+                mp.release();
+            }
+        });
+        thr.start();
+        // wait until the thread has initialized the MediaPlayer
+        synchronized (mpcl) {
+            mpcl.wait();
+        }
+
+        AssetFileDescriptor fd = resources.openRawResourceFd(rid);
+        byte[] blob = new byte[(int) fd.getLength()];
+        FileInputStream fis = fd.createInputStream();
+        int numRead = fis.read(blob);
+        fis.close();
+
+        // find all the available decoders for this format
+        ArrayList<String> matchingCodecs = new ArrayList<String>();
+        int numCodecs = MediaCodecList.getCodecCount();
+        for (int i = 0; i < numCodecs; i++) {
+            MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
+            if (info.isEncoder()) {
+                continue;
+            }
+            try {
+                MediaCodecInfo.CodecCapabilities caps = info.getCapabilitiesForType(mime);
+                if (caps != null) {
+                    matchingCodecs.add(info.getName());
+                }
+            } catch (IllegalArgumentException e) {
+                // type is not supported
+            }
+        }
+
+        if (matchingCodecs.size() == 0) {
+            Log.w(TAG, "no codecs for mime type " + mime);
+        }
+        String rname = resources.getResourceEntryName(rid);
+        // decode this blob once with each matching codec
+        for (String codecName : matchingCodecs) {
+            Log.i(TAG, "Decoding blob " + rname + " using codec " + codecName);
+            MediaCodec codec = MediaCodec.createByCodecName(codecName);
+            MediaFormat format = MediaFormat.createVideoFormat(mime, initWidth, initHeight);
+            try {
+                codec.configure(format, null, null, 0);
+                codec.start();
+            } catch (Exception e) {
+                Log.i(TAG, "Exception from codec " + codecName);
+                releaseCodec(codec);
+                continue;
+            }
+            try {
+                MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+                ByteBuffer[] inputBuffers = codec.getInputBuffers();
+                int numFrames = 0;
+                if (frameSizes != null) {
+                    numFrames = frameSizes.length;
+                }
+                if (0 == numFrames) {
+                    fail("Improper picture length file");
+                }
+                int offset = 0;
+                int j = 0;
+                while (j < numFrames) {
+                    int flags = 0;
+                    int bufidx = codec.dequeueInputBuffer(5000);
+                    if (bufidx >= 0) {
+                        inputBuffers[bufidx].rewind();
+                        Log.i(TAG, "Got buffer index " + bufidx + " with length "
+                                + inputBuffers[bufidx].capacity());
+                        if (isHeader[j] == 1) {
+                            flags = MediaCodec.BUFFER_FLAG_CODEC_CONFIG;
+                        }
+                        if (j == (numFrames - 1)) {
+                            flags = MediaCodec.BUFFER_FLAG_END_OF_STREAM;
+                        }
+                        Log.i(TAG, "Feeding frame " + j + " with framelen " + frameSizes[j]
+                                + " offset " + offset + " and flags " + flags);
+                        inputBuffers[bufidx].put(blob, offset, frameSizes[j]);
+                        codec.queueInputBuffer(bufidx, 0, frameSizes[j], 0, flags);
+                        offset = offset + frameSizes[j];
+                        j++;
+                    } else {
+                        Log.i(TAG, "no input buffer");
+                    }
+                    bufidx = codec.dequeueOutputBuffer(info, 5000);
+                    if (bufidx >= 0) {
+                        codec.releaseOutputBuffer(bufidx, false);
+                    } else {
+                        Log.i(TAG, "no output buffer");
+                    }
+                }
+            } catch (Exception e) {
+                // ignore, not a security issue
+            } finally {
+                releaseCodec(codec);
+            }
+        }
+        String cve = rname.replace("_", "-").toUpperCase();
+        assertFalse("Device *IS* vulnerable to " + cve,
+                mpcl.waitForError() == MediaPlayer.MEDIA_ERROR_SERVER_DIED);
+        thr.stopLooper();
+        thr.join();
+    }
+
     private void doStagefrightTestMediaPlayerANR(final int rid, final String uri) throws Exception {
         doStagefrightTestMediaPlayerANR(rid, uri, null);
     }