Fix GfxMonitorImpl to match latest `dumpsys GfxInfo` output.

Change-Id: Iad0a1ac8ed5c9a6a52e1cdc45e44563f779a9c05
diff --git a/src/main/java/android/support/test/jank/internal/GfxMonitorImpl.java b/src/main/java/android/support/test/jank/internal/GfxMonitorImpl.java
index bc1abd5..15077ee 100644
--- a/src/main/java/android/support/test/jank/internal/GfxMonitorImpl.java
+++ b/src/main/java/android/support/test/jank/internal/GfxMonitorImpl.java
@@ -26,6 +26,8 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.EnumMap;
+import java.util.Map;
 import java.util.List;
 import java.util.regex.Pattern;
 import java.util.regex.Matcher;
@@ -41,46 +43,62 @@
 class GfxMonitorImpl implements JankMonitor {
 
     // Patterns used for parsing dumpsys gfxinfo output
-    private static final Pattern TOTAL_FRAMES_PATTERN =
-            Pattern.compile("\\s*Total frames rendered: (\\d+)");
-    private static final Pattern JANKY_FRAMES_PATTERN =
-            Pattern.compile("\\s*Janky frames: (\\d+) \\(.*\\)");
-    private static final Pattern MISSED_VSYNC_PATTERN =
-            Pattern.compile("\\s*Number Missed Vsync: (\\d+)");
-    private static final Pattern INPUT_LATENCY_PATTERN =
-            Pattern.compile("\\s*Number High input latency: (\\d+)");
-    private static final Pattern SLOW_UI_PATTERN =
-            Pattern.compile("\\s*Number Slow UI thread: (\\d+)");
-    private static final Pattern SLOW_BITMAP_PATTERN =
-            Pattern.compile("\\s*Number Slow bitmap uploads: (\\d+)");
-    private static final Pattern SLOW_DRAW_PATTERN =
-            Pattern.compile("\\s*Number Slow draw: (\\d+)");
-    private static final Pattern FRAME_TIME_90TH_PERCENTILE_PATTERN =
-            Pattern.compile("\\s*90th percentile: (\\d+)ms");
-    private static final Pattern FRAME_TIME_95TH_PERCENTILE_PATTERN =
-            Pattern.compile("\\s*95th percentile: (\\d+)ms");
-    private static final Pattern FRAME_TIME_99TH_PERCENTILE_PATTERN =
-            Pattern.compile("\\s*99th percentile: (\\d+)ms");
+    public enum JankStat {
+        TOTAL_FRAMES(Pattern.compile("\\s*Total frames rendered: (\\d+)"), 1),
+        NUM_JANKY(Pattern.compile("\\s*Janky frames: (\\d+) \\(.*\\)"), 1),
+        FRAME_TIME_90TH(Pattern.compile("\\s*90th percentile: (\\d+)ms"), 1),
+        FRAME_TIME_95TH(Pattern.compile("\\s*95th percentile: (\\d+)ms"), 1),
+        FRAME_TIME_99TH(Pattern.compile("\\s*99th percentile: (\\d+)ms"), 1),
+        NUM_MISSED_VSYNC(Pattern.compile("\\s*Number Missed Vsync: (\\d+)"), 1),
+        NUM_HIGH_INPUT_LATENCY(Pattern.compile("\\s*Number High input latency: (\\d+)"), 1),
+        NUM_SLOW_UI_THREAD(Pattern.compile("\\s*Number Slow UI thread: (\\d+)"), 1),
+        NUM_SLOW_BITMAP_UPLOADS(Pattern.compile("\\s*Number Slow bitmap uploads: (\\d+)"), 1),
+        NUM_SLOW_DRAW(Pattern.compile("\\s*Number Slow draw: (\\d+)"), 1);
+
+        private boolean mSuccessfulParse = false;
+        private Pattern mParsePattern;
+        private int mGroupIndex;
+
+        JankStat(Pattern pattern, int groupIndex) {
+            mParsePattern = pattern;
+            mGroupIndex = groupIndex;
+        }
+
+        String parse(String line) {
+            String ret = null;
+            Matcher matcher = mParsePattern.matcher(line);
+            if (matcher.matches()) {
+                ret = matcher.group(mGroupIndex);
+                mSuccessfulParse = true;
+            }
+            return ret;
+        }
+
+        boolean wasParsedSuccessfully() {
+            return mSuccessfulParse;
+        }
+
+        void reset() {
+            mSuccessfulParse = false;
+        }
+    }
+
+    // Metrics accumulated for each iteration
+    private Map<JankStat, List<Integer>> mAccumulatedStats =
+            new EnumMap<JankStat, List<Integer>>(JankStat.class);
 
     // Used to invoke dumpsys gfxinfo
     private UiAutomation mUiAutomation;
     private String mProcess;
 
-    // Metrics accumulated for each iteration
-    private List<Integer> jankyFrames = new ArrayList<Integer>();
-    private List<Integer> missedVsync = new ArrayList<Integer>();
-    private List<Integer> highInputLatency = new ArrayList<Integer>();
-    private List<Integer> slowUiThread = new ArrayList<Integer>();
-    private List<Integer> slowBitmapUploads = new ArrayList<Integer>();
-    private List<Integer> slowDraw = new ArrayList<Integer>();
-    private List<Integer> frameTime90thPercentile = new ArrayList<Integer>();
-    private List<Integer> frameTime95thPercentile = new ArrayList<Integer>();
-    private List<Integer> frameTime99thPercentile = new ArrayList<Integer>();
-
 
     public GfxMonitorImpl(UiAutomation automation, String process) {
         mUiAutomation = automation;
         mProcess = process;
+
+        for (JankStat stat : JankStat.values()) {
+            mAccumulatedStats.put(stat, new ArrayList<Integer>());
+        }
     }
 
     @Override
@@ -96,6 +114,7 @@
         }
     }
 
+
     @Override
     public int stopIteration() throws IOException {
         ParcelFileDescriptor stdout = mUiAutomation.executeShellCommand(
@@ -103,138 +122,91 @@
         BufferedReader stream = new BufferedReader(new InputStreamReader(
                 new ParcelFileDescriptor.AutoCloseInputStream(stdout)));
 
-        // Wait until we enter the frame stats section
+        // The frame stats section has the following output:
+        // Total frames rendered: ###
+        // Janky frames: ### (##.##%)
+        // 90th percentile: ##ms
+        // 95th percentile: ##ms
+        // 99th percentile: ##ms
+        // Number Missed Vsync: #
+        // Number High input latency: #
+        // Number Slow UI thread: #
+        // Number Slow bitmap uploads: #
+        // Number Slow draw: #
+
         String line;
         while ((line = stream.readLine()) != null) {
-            if (line.startsWith("Frame stats:")) {
-                break;
+
+            // Attempt to parse the line as a frame stat value
+            for (JankStat stat : JankStat.values()) {
+                String part;
+                if ((part = stat.parse(line)) != null) {
+                    // Parse was successful. Add the numeric value to the accumulated list of values
+                    // for that stat.
+                    mAccumulatedStats.get(stat).add(Integer.parseInt(part));
+                    break;
+                }
             }
         }
-        Assert.assertTrue("Failed to locate frame stats in gfxinfo output",
-                line != null && line.startsWith("Frame stats:"));
 
-        // The frame stats section has the following output:
-        // Frame stats:
-        //   Total frames rendered: ###
-        //   Janky frames: ### (##.##%)
-        //   90th percentile: ##ms
-        //   95th percentile: ##ms
-        //   99th percentile: ##ms
-        //    Number Missed Vsync: #
-        //    Number High input latency: #
-        //    Number Slow UI thread: #
-        //    Number Slow bitmap uploads: #
-        //    Number Slow draw: #
-
-        // Get Total Frames
-        String part;
-        if ((part = getMatchGroup(stream.readLine(), TOTAL_FRAMES_PATTERN, 1)) == null) {
-            Assert.fail("Failed to parse total frames");
+        // Make sure we found all the stats
+        for (JankStat stat : JankStat.values()) {
+            if (!stat.wasParsedSuccessfully()) {
+                Assert.fail(String.format("Failed to parse %s", stat.name()));
+            }
+            stat.reset();
         }
-        int totalFrames = Integer.parseInt(part);
 
-        // Get Num Janky
-        if ((part = getMatchGroup(stream.readLine(), JANKY_FRAMES_PATTERN, 1)) == null) {
-            Assert.fail("Failed to parse janky frames");
-        }
-        jankyFrames.add(Integer.parseInt(part));
+        List<Integer> totalFrames = mAccumulatedStats.get(JankStat.TOTAL_FRAMES);
+        return totalFrames.get(totalFrames.size()-1);
+    }
 
-        // Get 90th percentile
-        if ((part = getMatchGroup(stream.readLine(), FRAME_TIME_90TH_PERCENTILE_PATTERN, 1)) == null) {
-            Assert.fail("Failed to parse 90th percentile");
-        }
-        frameTime90thPercentile.add(Integer.parseInt(part));
+    private void putAvgMax(Bundle metrics, String averageKey, String maxKey,
+            List<Integer> values) {
 
-        // Get 95th percentile
-        if ((part = getMatchGroup(stream.readLine(), FRAME_TIME_95TH_PERCENTILE_PATTERN, 1)) == null) {
-            Assert.fail("Failed to parse 95th percentile");
-        }
-        frameTime95thPercentile.add(Integer.parseInt(part));
-
-        // Get 99th percentile
-        if ((part = getMatchGroup(stream.readLine(), FRAME_TIME_99TH_PERCENTILE_PATTERN, 1)) == null) {
-            Assert.fail("Failed to parse 99th percentile");
-        }
-        frameTime99thPercentile.add(Integer.parseInt(part));
-
-        // Get Missed Vsync
-        if ((part = getMatchGroup(stream.readLine(), MISSED_VSYNC_PATTERN, 1)) == null) {
-            Assert.fail("Failed to parse number missed vsync");
-        }
-        missedVsync.add(Integer.parseInt(part));
-
-        // Get High input latency
-        if ((part = getMatchGroup(stream.readLine(), INPUT_LATENCY_PATTERN, 1)) == null) {
-            Assert.fail("Failed to parse number high input latency");
-        }
-        highInputLatency.add(Integer.parseInt(part));
-
-        // Get Slow UI thread
-        if ((part = getMatchGroup(stream.readLine(), SLOW_UI_PATTERN, 1)) == null) {
-            Assert.fail("Failed to parse number slow ui thread");
-        }
-        slowUiThread.add(Integer.parseInt(part));
-
-        // Get Slow bitmap uploads
-        if ((part = getMatchGroup(stream.readLine(), SLOW_BITMAP_PATTERN, 1)) == null) {
-            Assert.fail("Failed to parse number slow bitmap uploads");
-        }
-        slowBitmapUploads.add(Integer.parseInt(part));
-
-        // Get Slow draw
-        if ((part = getMatchGroup(stream.readLine(), SLOW_DRAW_PATTERN, 1)) == null) {
-            Assert.fail("Failed to parse number slow draw");
-        }
-        slowDraw.add(Integer.parseInt(part));
-
-        return totalFrames;
+        metrics.putDouble(averageKey, MetricsHelper.computeAverageInt(values));
+        metrics.putInt(maxKey, Collections.max(values));
     }
 
     public Bundle getMetrics() {
         Bundle metrics = new Bundle();
 
         // Store average and max jank
-        metrics.putDouble(GfxMonitor.KEY_AVG_NUM_JANKY,
-                MetricsHelper.computeAverageInt(jankyFrames));
-        metrics.putInt(GfxMonitor.KEY_MAX_NUM_JANKY, Collections.max(jankyFrames));
+        putAvgMax(metrics, GfxMonitor.KEY_AVG_NUM_JANKY, GfxMonitor.KEY_MAX_NUM_JANKY,
+                mAccumulatedStats.get(JankStat.NUM_JANKY));
 
         // Store average and max percentile frame times
-        metrics.putDouble(GfxMonitor.KEY_AVG_FRAME_TIME_90TH_PERCENTILE,
-                MetricsHelper.computeAverageInt(frameTime90thPercentile));
-        metrics.putInt(GfxMonitor.KEY_MAX_FRAME_TIME_90TH_PERCENTILE,
-                Collections.max(frameTime90thPercentile));
-        metrics.putDouble(GfxMonitor.KEY_AVG_FRAME_TIME_95TH_PERCENTILE,
-                MetricsHelper.computeAverageInt(frameTime95thPercentile));
-        metrics.putInt(GfxMonitor.KEY_MAX_FRAME_TIME_95TH_PERCENTILE,
-                Collections.max(frameTime95thPercentile));
-        metrics.putDouble(GfxMonitor.KEY_AVG_FRAME_TIME_99TH_PERCENTILE,
-                MetricsHelper.computeAverageInt(frameTime99thPercentile));
-        metrics.putInt(GfxMonitor.KEY_MAX_FRAME_TIME_99TH_PERCENTILE,
-                Collections.max(frameTime99thPercentile));
+        putAvgMax(metrics, GfxMonitor.KEY_AVG_FRAME_TIME_90TH_PERCENTILE,
+                GfxMonitor.KEY_MAX_FRAME_TIME_90TH_PERCENTILE,
+                mAccumulatedStats.get(JankStat.FRAME_TIME_90TH));
+        putAvgMax(metrics, GfxMonitor.KEY_AVG_FRAME_TIME_95TH_PERCENTILE,
+                GfxMonitor.KEY_MAX_FRAME_TIME_95TH_PERCENTILE,
+                mAccumulatedStats.get(JankStat.FRAME_TIME_95TH));
+        putAvgMax(metrics, GfxMonitor.KEY_AVG_FRAME_TIME_99TH_PERCENTILE,
+                GfxMonitor.KEY_MAX_FRAME_TIME_99TH_PERCENTILE,
+                mAccumulatedStats.get(JankStat.FRAME_TIME_99TH));
 
         // Store average and max missed vsync
-        metrics.putDouble(GfxMonitor.KEY_AVG_MISSED_VSYNC,
-                MetricsHelper.computeAverageInt(missedVsync));
-        metrics.putInt(GfxMonitor.KEY_MAX_MISSED_VSYNC, Collections.max(missedVsync));
+        putAvgMax(metrics, GfxMonitor.KEY_AVG_MISSED_VSYNC, GfxMonitor.KEY_MAX_MISSED_VSYNC,
+                mAccumulatedStats.get(JankStat.NUM_MISSED_VSYNC));
 
         // Store average and max high input latency
-        metrics.putDouble(GfxMonitor.KEY_AVG_HIGH_INPUT_LATENCY,
-                MetricsHelper.computeAverageInt(highInputLatency));
-        metrics.putInt(GfxMonitor.KEY_MAX_HIGH_INPUT_LATENCY, Collections.max(highInputLatency));
+        putAvgMax(metrics, GfxMonitor.KEY_AVG_HIGH_INPUT_LATENCY,
+                GfxMonitor.KEY_MAX_HIGH_INPUT_LATENCY,
+                mAccumulatedStats.get(JankStat.NUM_HIGH_INPUT_LATENCY));
 
         // Store average and max slow ui thread
-        metrics.putDouble(GfxMonitor.KEY_AVG_SLOW_UI_THREAD,
-                MetricsHelper.computeAverageInt(slowUiThread));
-        metrics.putInt(GfxMonitor.KEY_MAX_SLOW_UI_THREAD, Collections.max(slowUiThread));
+        putAvgMax(metrics, GfxMonitor.KEY_AVG_SLOW_UI_THREAD, GfxMonitor.KEY_MAX_SLOW_UI_THREAD,
+                mAccumulatedStats.get(JankStat.NUM_SLOW_UI_THREAD));
 
         // Store average and max slow bitmap uploads
-        metrics.putDouble(GfxMonitor.KEY_AVG_SLOW_BITMAP_UPLOADS,
-                MetricsHelper.computeAverageInt(slowUiThread));
-        metrics.putInt(GfxMonitor.KEY_MAX_SLOW_BITMAP_UPLOADS, Collections.max(slowUiThread));
+        putAvgMax(metrics, GfxMonitor.KEY_AVG_SLOW_BITMAP_UPLOADS,
+                GfxMonitor.KEY_MAX_SLOW_BITMAP_UPLOADS,
+                mAccumulatedStats.get(JankStat.NUM_SLOW_BITMAP_UPLOADS));
 
         // Store average and max slow draw
-        metrics.putDouble(GfxMonitor.KEY_AVG_SLOW_DRAW, MetricsHelper.computeAverageInt(slowDraw));
-        metrics.putInt(GfxMonitor.KEY_MAX_SLOW_DRAW, Collections.max(slowDraw));
+        putAvgMax(metrics, GfxMonitor.KEY_AVG_SLOW_DRAW, GfxMonitor.KEY_MAX_SLOW_DRAW,
+                mAccumulatedStats.get(JankStat.NUM_SLOW_DRAW));
 
         return metrics;
     }