Merge "Convert device side to write proto rather than csv"
diff --git a/README b/README
index 3c21fcc..3bb78d4 100644
--- a/README
+++ b/README
@@ -1,4 +1,3 @@
-
 Note: env setup requires slightly more than a normal Android build,
 so that the path gets adjusted for tradefed.sh:
 
@@ -9,8 +8,9 @@
 lunch 2				# generic aosp arm64 eng build
 mmma tools/tradefederation/core tools/test/graphicsbenchmark
 
-
 Incremental build + run, from tools/test/graphicsbenchmark dir:
 
 mma
-tradefed.sh run commandAndExit AndroidTest.xml --alt-dir $OUT --apk-dir $PATH_TO_BENCHMARK_APK_DIR
+tradefed.sh run commandAndExit AndroidTest.xml --alt-dir $OUT --apk-dir $OUT/data/app
+
+Contact a member of the team for alternative test apks.
diff --git a/benchmark_libs/Android.bp b/benchmark_libs/Android.bp
index dd61923..0657698 100644
--- a/benchmark_libs/Android.bp
+++ b/benchmark_libs/Android.bp
@@ -19,5 +19,6 @@
     srcs: [
         "android_benchmark.cpp"
     ],
+    export_include_dirs: ["."],
     shared_libs: ["liblog"],
 }
diff --git a/hostside/res/com/android/graphics/benchmark/apk-info.xml b/hostside/res/com/android/graphics/benchmark/apk-info.xml
index ef141ff..b923d6d 100644
--- a/hostside/res/com/android/graphics/benchmark/apk-info.xml
+++ b/hostside/res/com/android/graphics/benchmark/apk-info.xml
@@ -1,15 +1,9 @@
 <?xml version="1.0"?>
 <apk-info>
     <apk>
-        <name>sniper3d</name>
-        <fileName>Sniper3D_3242.apk</fileName>
-        <packageName>com.fungames.sniper3d</packageName>
-        <layerName>SurfaceView - com.fungames.sniper3d/com.tfg.libs.billing.unity.BillingActivity#0</layerName>
-    </apk>
-    <apk>
-        <name>afterpulse</name>
-        <fileName>afterpulse-v1.9.0.apk</fileName>
-        <packageName>com.dle.afterpulse</packageName>
-        <layerName>com.dle.afterpulse/com.dle.application.obbdownloader.OBBDownloaderActivity#0</layerName>
+        <name>sample</name>
+        <fileName>GraphicsBenchmarkSampleApp.apk</fileName>
+        <packageName>com.android.graphics.benchmark.example</packageName>
+        <layerName>SurfaceView - com.android.graphics.benchmark.example/com.android.graphics.benchmark.example.SampleActivity#0</layerName>
     </apk>
 </apk-info>
\ No newline at end of file
diff --git a/hostside/src/com/android/graphics/benchmark/metric/GraphicsBenchmarkMetricCollector.java b/hostside/src/com/android/graphics/benchmark/metric/GraphicsBenchmarkMetricCollector.java
index c795634..76d84fa 100644
--- a/hostside/src/com/android/graphics/benchmark/metric/GraphicsBenchmarkMetricCollector.java
+++ b/hostside/src/com/android/graphics/benchmark/metric/GraphicsBenchmarkMetricCollector.java
@@ -176,7 +176,10 @@
             return true;
         }
         else {
-            mElapsedTimes.add(timeStamp - mLatestSeen);
+            // Ignore the first timestamp.
+            if (mLatestSeen != 0) {
+                mElapsedTimes.add(timeStamp - mLatestSeen);
+            }
             mLatestSeen = timeStamp;
             return false;
         }
@@ -186,7 +189,9 @@
     private void onStart(DeviceMetricData runData) {}
 
     private void onEnd(DeviceMetricData runData) {
-        double minFPS = Double.MAX_VALUE, maxFPS = 0.0, avgFPS = 0.0;
+        double minFPS = Double.MAX_VALUE;
+        double maxFPS = 0.0;
+        long totalTimeNs = 0;
 
         // TODO: correlate with mDeviceResultData to exclude loading period, etc.
 
@@ -200,19 +205,23 @@
                 double currentFPS = 1.0e9/time;
                 minFPS = (currentFPS < minFPS ? currentFPS : minFPS);
                 maxFPS = (currentFPS > maxFPS ? currentFPS : maxFPS);
-                avgFPS += currentFPS;
+                totalTimeNs += time;
 
                 outputFile.write(currentFPS + "\n");
             }
 
             outputFile.write("\nSTATS\n");
 
-            avgFPS = avgFPS / mElapsedTimes.size();
+            double avgFPS = mElapsedTimes.size() * 1.0e9 / totalTimeNs;
 
             outputFile.write("min FPS = " + minFPS + "\n");
             outputFile.write("max FPS = " + maxFPS + "\n");
             outputFile.write("avg FPS = " + avgFPS + "\n");
 
+            runData.addStringMetric("min_fps", Double.toString(minFPS));
+            runData.addStringMetric("max_fps", Double.toString(minFPS));
+            runData.addStringMetric("fps", Double.toString(avgFPS));
+
             outputFile.write("\n");
         } catch (IOException e) {
             throw new RuntimeException(e);
diff --git a/hostside/src/com/android/graphics/benchmark/testtype/GraphicsBenchmarkHostsideController.java b/hostside/src/com/android/graphics/benchmark/testtype/GraphicsBenchmarkHostsideController.java
index 5046ba1..f7a54c9 100644
--- a/hostside/src/com/android/graphics/benchmark/testtype/GraphicsBenchmarkHostsideController.java
+++ b/hostside/src/com/android/graphics/benchmark/testtype/GraphicsBenchmarkHostsideController.java
@@ -34,6 +34,7 @@
 import com.android.tradefed.testtype.IShardableTest;
 
 import com.google.common.io.ByteStreams;
+import com.google.common.io.Files;
 
 import org.xml.sax.SAXException;
 
@@ -116,7 +117,8 @@
         getDevice().pushFile(mApkInfoFile, ApkInfo.APK_LIST_LOCATION);
 
         for (ApkInfo apk : mApks) {
-            getDevice().installPackage(new File(mApkDir, apk.getFileName()), true);
+            File apkFile = findApk(apk.getFileName());
+            getDevice().installPackage(apkFile, true);
             GraphicsBenchmarkMetricCollector.setAppLayerName(apk);
 
             // Might seem counter-intuitive, but the easiest way to get per-package results is
@@ -130,7 +132,18 @@
             // TODO: Populate metrics
 
             listener.testStarted(identifier);
-            runDeviceTests(PACKAGE, CLASS, "run[" + apk.getName() + "]");
+
+            if (apkFile == null) {
+                listener.testFailed(
+                        identifier,
+                        String.format(
+                                "Missing APK.  Unable to find %s in %s.",
+                                apk.getFileName(),
+                                mApkDir));
+            } else {
+                runDeviceTests(PACKAGE, CLASS, "run[" + apk.getName() + "]");
+            }
+
             listener.testEnded(identifier, testMetrics);
 
             ResultDataProto.Result resultData = retrieveResultData();
@@ -147,11 +160,25 @@
             try (InputStream inputStream = new FileInputStream(resultFile)) {
                 ResultDataProto.Result data = ResultDataProto.Result.parseFrom(inputStream);
                 return data;
-            } catch(IOException e) {
+            } catch (IOException e) {
                 throw new RuntimeException(e);
             }
         }
+        return null;
+    }
 
+    /** Find an apk in the apk-dir directory */
+    private File findApk(String filename) {
+        File file = new File(mApkDir, filename);
+        if (file.exists()) {
+            return file;
+        }
+        // If a default sample app is named Sample.apk, it is outputted to
+        // $ANDROID_PRODUCT_OUT/data/app/Sample/Sample.apk.
+        file = new File(mApkDir, Files.getNameWithoutExtension(filename) + "/" + filename);
+        if (file.exists()) {
+            return file;
+        }
         return null;
     }
 
@@ -159,21 +186,30 @@
         if (mApks != null) {
             return;
         }
+
+        // Find an apk info file.  The priorities are:
+        // 1. Use the specified apk-info if available.
+        // 2. Use 'apk-info.xml' if there is one in the apk-dir directory.
+        // 3. Use the default apk-info.xml in res.
         if (mApkInfoFileName != null) {
             mApkInfoFile = new File(mApkInfoFileName);
         } else {
-            String resource = "/com/android/graphics/benchmark/apk-info.xml";
-            try(InputStream inputStream = ApkInfo.class.getResourceAsStream(resource)) {
-                if (inputStream == null) {
-                    throw new FileNotFoundException("Unable to find resource: " + resource);
+            mApkInfoFile = new File(mApkDir, "apk-info.xml");
+
+            if (!mApkInfoFile.exists()) {
+                String resource = "/com/android/graphics/benchmark/apk-info.xml";
+                try(InputStream inputStream = ApkInfo.class.getResourceAsStream(resource)) {
+                    if (inputStream == null) {
+                        throw new FileNotFoundException("Unable to find resource: " + resource);
+                    }
+                    mApkInfoFile = File.createTempFile("apk-info", ".xml");
+                    try (OutputStream ostream = new FileOutputStream(mApkInfoFile)) {
+                        ByteStreams.copy(inputStream, ostream);
+                    }
+                    mApkInfoFile.deleteOnExit();
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
                 }
-                mApkInfoFile = File.createTempFile("apk-info", ".xml");
-                try (OutputStream ostream = new FileOutputStream(mApkInfoFile)) {
-                    ByteStreams.copy(inputStream, ostream);
-                }
-                mApkInfoFile.deleteOnExit();
-            } catch (IOException e) {
-                throw new RuntimeException(e);
             }
         }
         ApkListXmlParser parser = new ApkListXmlParser();
diff --git a/sample_app/Android.mk b/sample_app/Android.mk
new file mode 100644
index 0000000..a2e0eb7
--- /dev/null
+++ b/sample_app/Android.mk
@@ -0,0 +1,38 @@
+# Copyright 2018, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SDK_VERSION := 26  # Oreo
+LOCAL_MODULE := libsample
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := src/cpp/sample_activity.cpp
+LOCAL_SHARED_LIBRARIES := libagbench
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_SDK_VERSION := 26  # Oreo
+LOCAL_PACKAGE_NAME := GraphicsBenchmarkSampleApp
+LOCAL_MODULE_TAGS := tests
+LOCAL_JNI_SHARED_LIBRARIES := libagbench libsample
+LOCAL_COMPATIBILITY_SUITE := device-tests
+LOCAL_SRC_FILES := $(call all-java-files-under, src/java)
+
+include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/sample_app/AndroidManifest.xml b/sample_app/AndroidManifest.xml
new file mode 100644
index 0000000..bd77eb3
--- /dev/null
+++ b/sample_app/AndroidManifest.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.graphics.benchmark.example">
+
+  <application
+      android:allowBackup="false"
+      android:label="SampleBenchmark">
+    <activity android:name=".SampleActivity">
+      <intent-filter>
+        <action android:name="android.intent.action.MAIN"/>
+
+        <category android:name="android.intent.category.LAUNCHER"/>
+      </intent-filter>
+    </activity>
+  </application>
+</manifest>
diff --git a/sample_app/src/cpp/sample_activity.cpp b/sample_app/src/cpp/sample_activity.cpp
new file mode 100644
index 0000000..5504c3a
--- /dev/null
+++ b/sample_app/src/cpp/sample_activity.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <jni.h>
+#include <android_benchmark.h>
+
+extern "C"
+JNIEXPORT void JNICALL
+Java_com_android_graphics_benchmark_example_SampleActivity_broadcastIntent(JNIEnv*, jobject instance) {
+    android::AndroidGraphicsBenchmark benchmark;
+    benchmark.startBenchmark(instance);
+}
diff --git a/sample_app/src/java/com/android/graphics/benchmark/example/MyGLRenderer.java b/sample_app/src/java/com/android/graphics/benchmark/example/MyGLRenderer.java
new file mode 100644
index 0000000..adaaeba
--- /dev/null
+++ b/sample_app/src/java/com/android/graphics/benchmark/example/MyGLRenderer.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.graphics.benchmark.example;
+
+import android.opengl.GLES20;
+import android.opengl.GLSurfaceView;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+public class MyGLRenderer implements GLSurfaceView.Renderer {
+
+    public void onSurfaceCreated(GL10 unused, EGLConfig config) {
+        // Set the background frame color
+        GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
+    }
+
+    public void onDrawFrame(GL10 unused) {
+        // Redraw background color
+        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+    }
+
+    public void onSurfaceChanged(GL10 unused, int width, int height) {
+        GLES20.glViewport(0, 0, width, height);
+    }
+}
diff --git a/sample_app/src/java/com/android/graphics/benchmark/example/MyGLSurfaceView.java b/sample_app/src/java/com/android/graphics/benchmark/example/MyGLSurfaceView.java
new file mode 100644
index 0000000..c0897e7
--- /dev/null
+++ b/sample_app/src/java/com/android/graphics/benchmark/example/MyGLSurfaceView.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.graphics.benchmark.example;
+
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+
+class MyGLSurfaceView extends GLSurfaceView {
+
+    private final MyGLRenderer mRenderer;
+
+    public MyGLSurfaceView(Context context){
+        super(context);
+
+        // Create an OpenGL ES 2.0 context
+        setEGLContextClientVersion(2);
+
+        mRenderer = new MyGLRenderer();
+
+        // Set the Renderer for drawing on the GLSurfaceView
+        setRenderer(mRenderer);
+    }
+}
diff --git a/sample_app/src/java/com/android/graphics/benchmark/example/SampleActivity.java b/sample_app/src/java/com/android/graphics/benchmark/example/SampleActivity.java
new file mode 100644
index 0000000..b0207a5
--- /dev/null
+++ b/sample_app/src/java/com/android/graphics/benchmark/example/SampleActivity.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.graphics.benchmark.example;
+
+import android.app.Activity;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+
+public class SampleActivity extends Activity {
+    private GLSurfaceView mGLView;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // Create a GLSurfaceView instance and set it
+        // as the ContentView for this Activity.
+        mGLView = new MyGLSurfaceView(this);
+        setContentView(mGLView);
+
+        // Loop every 5s.
+        Handler handler = new Handler();
+        Runnable task = new Runnable() {
+            @Override
+            public void run() {
+                broadcastIntent();
+                handler.postDelayed(this, 5000);
+            }
+        };
+        handler.postDelayed(task, 5000);
+    }
+
+    public native void broadcastIntent();
+
+    static {
+        System.loadLibrary("sample");
+    }
+}