Extract NN API Benchmarks into an app that can easily be run by dogfooders. am: f7302c14e6
am: 6ee2a2b58c
Change-Id: Ife8df985f2c254caa145dbc79eaa029c3bc03b0d
diff --git a/dogfood/Android.mk b/dogfood/Android.mk
new file mode 100644
index 0000000..b7fef4d
--- /dev/null
+++ b/dogfood/Android.mk
@@ -0,0 +1,47 @@
+#
+# Copyright (C) 2019 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_STATIC_JAVA_LIBRARIES := android-common androidx.test.rules androidx.appcompat_appcompat androidx-constraintlayout_constraintlayout
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_COMPATIBILITY_SUITE += device-tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+ $(call all-java-files-under, ../src/com/android/nn/benchmark/core) \
+ $(call all-java-files-under, ../src/com/android/nn/benchmark/evaluators) \
+ $(call all-java-files-under, ../src/com/android/nn/benchmark/imageprocessors) \
+ $(call all-java-files-under, ../src/com/android/nn/benchmark/util)
+LOCAL_JNI_SHARED_LIBRARIES := libnnbenchmark_jni
+
+LOCAL_SDK_VERSION := 27
+LOCAL_ASSET_DIR := $(LOCAL_PATH)/../../models/assets
+
+GOOGLE_TEST_MODELS_DIR := vendor/google/tests/mlts/models/assets
+ifneq ($(wildcard $(GOOGLE_TEST_MODELS_DIR)),)
+LOCAL_ASSET_DIR += $(GOOGLE_TEST_MODELS_DIR)
+endif
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res $(LOCAL_PATH)/../res
+
+LOCAL_PACKAGE_NAME := NeuralNetworksApiDogfood
+include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/dogfood/AndroidManifest.xml b/dogfood/AndroidManifest.xml
new file mode 100644
index 0000000..5573e52
--- /dev/null
+++ b/dogfood/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.nn.dogfood"
+ android:versionCode="2"
+ android:versionName="2">
+
+ <application
+ android:allowBackup="true"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:roundIcon="@mipmap/ic_launcher_round"
+ android:supportsRtl="true"
+ android:theme="@style/AppTheme">
+ <activity android:name=".MainActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <service
+ android:name=".BenchmarkJobService"
+ android:label="Benchmark Service"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+
+ </service>
+ </application>
+
+</manifest>
diff --git a/dogfood/res/drawable-v24/ic_launcher_foreground.xml b/dogfood/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..1f6bb29
--- /dev/null
+++ b/dogfood/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,34 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108">
+ <path
+ android:fillType="evenOdd"
+ android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
+ android:strokeWidth="1"
+ android:strokeColor="#00000000">
+ <aapt:attr name="android:fillColor">
+ <gradient
+ android:endX="78.5885"
+ android:endY="90.9159"
+ android:startX="48.7653"
+ android:startY="61.0927"
+ android:type="linear">
+ <item
+ android:color="#44000000"
+ android:offset="0.0" />
+ <item
+ android:color="#00000000"
+ android:offset="1.0" />
+ </gradient>
+ </aapt:attr>
+ </path>
+ <path
+ android:fillColor="#FFFFFF"
+ android:fillType="nonZero"
+ android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
+ android:strokeWidth="1"
+ android:strokeColor="#00000000" />
+</vector>
diff --git a/dogfood/res/drawable/ic_launcher_background.xml b/dogfood/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..0d025f9
--- /dev/null
+++ b/dogfood/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108">
+ <path
+ android:fillColor="#008577"
+ android:pathData="M0,0h108v108h-108z" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M9,0L9,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,0L19,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M29,0L29,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M39,0L39,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M49,0L49,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M59,0L59,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M69,0L69,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M79,0L79,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M89,0L89,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M99,0L99,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,9L108,9"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,19L108,19"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,29L108,29"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,39L108,39"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,49L108,49"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,59L108,59"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,69L108,69"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,79L108,79"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,89L108,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,99L108,99"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,29L89,29"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,39L89,39"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,49L89,49"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,59L89,59"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,69L89,69"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,79L89,79"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M29,19L29,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M39,19L39,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M49,19L49,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M59,19L59,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M69,19L69,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M79,19L79,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+</vector>
diff --git a/dogfood/res/layout/activity_main.xml b/dogfood/res/layout/activity_main.xml
new file mode 100644
index 0000000..afc57af
--- /dev/null
+++ b/dogfood/res/layout/activity_main.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".dogfood.MainActivity">
+
+ <Button
+ android:id="@+id/start_stop_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="36dp"
+ android:layout_marginLeft="36dp"
+ android:onClick="startStopTestClicked"
+ android:text="Start NN API Test"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ />
+
+ <TextView
+ android:id="@+id/message"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="8dp"
+ android:layout_marginTop="16dp"
+ android:text=""
+ app:layout_constraintStart_toStartOf="@+id/start_button"
+ app:layout_constraintTop_toBottomOf="@+id/start_stop_button" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/dogfood/res/mipmap-anydpi-v26/ic_launcher.xml b/dogfood/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/dogfood/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/ic_launcher_background" />
+ <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>
\ No newline at end of file
diff --git a/dogfood/res/mipmap-anydpi-v26/ic_launcher_round.xml b/dogfood/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/dogfood/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/ic_launcher_background" />
+ <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>
\ No newline at end of file
diff --git a/dogfood/res/mipmap-hdpi/ic_launcher.png b/dogfood/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..898f3ed
--- /dev/null
+++ b/dogfood/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/dogfood/res/mipmap-hdpi/ic_launcher_round.png b/dogfood/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..dffca36
--- /dev/null
+++ b/dogfood/res/mipmap-hdpi/ic_launcher_round.png
Binary files differ
diff --git a/dogfood/res/mipmap-mdpi/ic_launcher.png b/dogfood/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..64ba76f
--- /dev/null
+++ b/dogfood/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/dogfood/res/mipmap-mdpi/ic_launcher_round.png b/dogfood/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..dae5e08
--- /dev/null
+++ b/dogfood/res/mipmap-mdpi/ic_launcher_round.png
Binary files differ
diff --git a/dogfood/res/mipmap-xhdpi/ic_launcher.png b/dogfood/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..e5ed465
--- /dev/null
+++ b/dogfood/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/dogfood/res/mipmap-xhdpi/ic_launcher_round.png b/dogfood/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..14ed0af
--- /dev/null
+++ b/dogfood/res/mipmap-xhdpi/ic_launcher_round.png
Binary files differ
diff --git a/dogfood/res/mipmap-xxhdpi/ic_launcher.png b/dogfood/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..b0907ca
--- /dev/null
+++ b/dogfood/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/dogfood/res/mipmap-xxhdpi/ic_launcher_round.png b/dogfood/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..d8ae031
--- /dev/null
+++ b/dogfood/res/mipmap-xxhdpi/ic_launcher_round.png
Binary files differ
diff --git a/dogfood/res/mipmap-xxxhdpi/ic_launcher.png b/dogfood/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..2c18de9
--- /dev/null
+++ b/dogfood/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/dogfood/res/mipmap-xxxhdpi/ic_launcher_round.png b/dogfood/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..beed3cd
--- /dev/null
+++ b/dogfood/res/mipmap-xxxhdpi/ic_launcher_round.png
Binary files differ
diff --git a/dogfood/res/values/colors.xml b/dogfood/res/values/colors.xml
new file mode 100644
index 0000000..69b2233
--- /dev/null
+++ b/dogfood/res/values/colors.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="colorPrimary">#008577</color>
+ <color name="colorPrimaryDark">#00574B</color>
+ <color name="colorAccent">#D81B60</color>
+</resources>
diff --git a/dogfood/res/values/strings.xml b/dogfood/res/values/strings.xml
new file mode 100644
index 0000000..729c261
--- /dev/null
+++ b/dogfood/res/values/strings.xml
@@ -0,0 +1,5 @@
+<resources>
+ <string name="app_name">NN API Dogfood</string>
+ <string name="start_button_text">Start test</string>
+ <string name="stop_button_text">Stop test</string>
+</resources>
diff --git a/dogfood/res/values/styles.xml b/dogfood/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/dogfood/res/values/styles.xml
@@ -0,0 +1,11 @@
+<resources>
+
+ <!-- Base application theme. -->
+ <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+ <!-- Customize your theme here. -->
+ <item name="colorPrimary">@color/colorPrimary</item>
+ <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
+ <item name="colorAccent">@color/colorAccent</item>
+ </style>
+
+</resources>
diff --git a/dogfood/src/com/android/nn/dogfood/BenchmarkJobService.java b/dogfood/src/com/android/nn/dogfood/BenchmarkJobService.java
new file mode 100644
index 0000000..222f8be
--- /dev/null
+++ b/dogfood/src/com/android/nn/dogfood/BenchmarkJobService.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2019 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.nn.dogfood;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.SharedPreferences;
+import android.util.Log;
+
+import androidx.core.app.NotificationCompat;
+import androidx.core.app.NotificationManagerCompat;
+
+import com.android.nn.benchmark.core.BenchmarkResult;
+import com.android.nn.benchmark.core.NNTestBase;
+import com.android.nn.benchmark.core.Processor;
+import com.android.nn.benchmark.core.TestModels;
+
+import java.util.List;
+import java.util.Random;
+
+/** Regularly runs a random selection of the NN API benchmark models */
+public class BenchmarkJobService extends JobService implements Processor.Callback {
+
+ private static final String TAG = "NN_BENCHMARK";
+ private static final String CHANNEL_ID = "default";
+ private static final int NOTIFICATION_ID = 999;
+ public static final int JOB_ID = 1;
+ private NotificationManagerCompat mNotificationManager;
+ private NotificationCompat.Builder mNotification;
+ private boolean mJobStopped = false;
+ private static final int NUM_RUNS = 10;
+ private Processor mProcessor;
+ private JobParameters mJobParameters;
+ private static final String NN_API_DOGFOOD_PREF = "nn_api_dogfood";
+
+ private static int DOGFOOD_MODELS_PER_RUN = 20;
+ private BenchmarkResult mTestResults[];
+
+
+ @Override
+ public boolean onStartJob(JobParameters jobParameters) {
+ mJobParameters = jobParameters;
+ incrementNumRuns();
+ Log.d(TAG, String.format("NN API Benchmarking job %d/%d started", getNumRuns(), NUM_RUNS));
+ showNotification();
+ doBenchmark();
+
+ return true;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters jobParameters) {
+ Log.d(TAG, String.format("NN API Benchmarking job %d/%d stopped", getNumRuns(), NUM_RUNS));
+ mJobStopped = true;
+
+ return false;
+ }
+
+ public void doBenchmark() {
+
+ mProcessor = new Processor(this, this, randomModelList());
+ mProcessor.setUseNNApi(true);
+ mProcessor.setToggleLong(true);
+ mProcessor.start();
+ }
+
+ public void onBenchmarkFinish(boolean ok) {
+ mProcessor.exit();
+ if (getNumRuns() >= NUM_RUNS) {
+ mNotification
+ .setProgress(0, 0, false)
+ .setContentText(
+ "Benchmarking done! please upload a bug report via BetterBug under"
+ + " Android > Android OS & > Apps Runtime > Machine Learning")
+ .setOngoing(false);
+ mNotificationManager.notify(NOTIFICATION_ID, mNotification.build());
+ JobScheduler jobScheduler = getSystemService(JobScheduler.class);
+ jobScheduler.cancel(JOB_ID);
+ resetNumRuns();
+ } else {
+ mNotification
+ .setProgress(0, 0, false)
+ .setContentText(
+ String.format(
+ "Background test %d of %d is complete", getNumRuns(), NUM_RUNS))
+ .setOngoing(false);
+ mNotificationManager.notify(NOTIFICATION_ID, mNotification.build());
+ }
+
+ Log.d(TAG, "NN API Benchmarking job finished");
+ jobFinished(mJobParameters, false);
+ }
+
+ public void onStatusUpdate(int testNumber, int numTests, String modelName) {
+ Log.d(
+ TAG,
+ String.format("Benchmark progress %d of %d - %s", testNumber, numTests, modelName));
+ mNotification.setProgress(numTests, testNumber, false);
+ mNotificationManager.notify(NOTIFICATION_ID, mNotification.build());
+ }
+
+ private void showNotification() {
+ mNotificationManager = NotificationManagerCompat.from(this);
+ NotificationChannel channel =
+ new NotificationChannel(CHANNEL_ID, "Default", NotificationManager.IMPORTANCE_LOW);
+ // mNotificationManager.createNotificationChannel(channel);
+ mNotificationManager = NotificationManagerCompat.from(this);
+ String title = "NN API Dogfood";
+ String msg = String.format("Background test %d of %d is running", getNumRuns(), NUM_RUNS);
+
+ mNotification =
+ new NotificationCompat.Builder(this, CHANNEL_ID)
+ .setSmallIcon(R.mipmap.ic_launcher)
+ .setContentTitle(title)
+ .setContentText("NN API Benchmarking Job")
+ .setPriority(NotificationCompat.PRIORITY_MAX)
+ .setOngoing(true);
+ mNotificationManager.notify(NOTIFICATION_ID, mNotification.build());
+ }
+
+ private int[] randomModelList() {
+ long seed = System.currentTimeMillis();
+ List<TestModels.TestModelEntry> testList = TestModels.modelsList();
+
+ Log.v(TAG, "Dogfood run seed " + seed);
+ Random random = new Random(seed);
+ int numModelsToSelect = Math.min(DOGFOOD_MODELS_PER_RUN, testList.size());
+ int[] randomModelIndices = new int[numModelsToSelect];
+
+ for (int i = 0; i < numModelsToSelect; i++) {
+ randomModelIndices[i] = random.nextInt(testList.size());
+ }
+
+ return randomModelIndices;
+ }
+
+ private void incrementNumRuns() {
+ SharedPreferences.Editor editor =
+ getSharedPreferences(NN_API_DOGFOOD_PREF, MODE_PRIVATE).edit();
+ editor.putInt("num_runs", getNumRuns() + 1);
+ editor.apply();
+ }
+
+ private void resetNumRuns() {
+ SharedPreferences.Editor editor =
+ getSharedPreferences(NN_API_DOGFOOD_PREF, MODE_PRIVATE).edit();
+ editor.putInt("num_runs", 0);
+ editor.apply();
+ }
+
+ private int getNumRuns() {
+ SharedPreferences prefs = getSharedPreferences(NN_API_DOGFOOD_PREF, MODE_PRIVATE);
+ return prefs.getInt("num_runs", 0);
+ }
+}
diff --git a/dogfood/src/com/android/nn/dogfood/MainActivity.java b/dogfood/src/com/android/nn/dogfood/MainActivity.java
new file mode 100644
index 0000000..0c259be
--- /dev/null
+++ b/dogfood/src/com/android/nn/dogfood/MainActivity.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2019 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.nn.dogfood;
+
+import android.app.job.JobInfo;
+import android.app.job.JobScheduler;
+import android.content.ComponentName;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.android.nn.benchmark.core.TestModelsListLoader;
+import com.android.nn.benchmark.util.TestExternalStorageActivity;
+
+import java.io.IOException;
+
+public class MainActivity extends AppCompatActivity {
+
+ private static final String TAG = "NN_BENCHMARK";
+ private static final int JOB_FREQUENCY_MILLIS = 15 * 60 * 1000; // 15 minutes
+ private Button mStartStopButton;
+ private TextView mMessage;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ TestExternalStorageActivity.testWriteExternalStorage(this, true);
+
+ mStartStopButton = (Button) findViewById(R.id.start_stop_button);
+ mMessage = (TextView) findViewById(R.id.message);
+ try {
+ TestModelsListLoader.parseFromAssets(getAssets());
+ } catch (IOException e) {
+ Log.e(TAG, "Could not load models", e);
+ }
+ }
+
+ public void startStopTestClicked(View v) {
+
+ JobScheduler jobScheduler = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE);
+ if (jobScheduler.getPendingJob(BenchmarkJobService.JOB_ID) == null) {
+ // no job is currently scheduled
+ ComponentName componentName = new ComponentName(this, BenchmarkJobService.class);
+ JobInfo jobInfo =
+ new JobInfo.Builder(BenchmarkJobService.JOB_ID, componentName)
+ .setPeriodic(JOB_FREQUENCY_MILLIS)
+ .build();
+ jobScheduler.schedule(jobInfo);
+ mMessage.setText("Benchmark job scheduled, you can leave this app");
+ mStartStopButton.setText(R.string.stop_button_text);
+ } else {
+ jobScheduler.cancel(BenchmarkJobService.JOB_ID);
+ mMessage.setText("Benchmark job cancelled");
+ mStartStopButton.setText(R.string.start_button_text);
+ }
+ }
+}
diff --git a/src/com/android/nn/benchmark/app/NNBenchmark.java b/src/com/android/nn/benchmark/app/NNBenchmark.java
index c29fef8..a45e043 100644
--- a/src/com/android/nn/benchmark/app/NNBenchmark.java
+++ b/src/com/android/nn/benchmark/app/NNBenchmark.java
@@ -19,24 +19,13 @@
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
-import android.os.Trace;
-import android.util.Log;
-import android.util.Pair;
import android.view.WindowManager;
import android.widget.TextView;
-import com.android.nn.benchmark.core.BenchmarkException;
import com.android.nn.benchmark.core.BenchmarkResult;
-import com.android.nn.benchmark.core.InferenceInOutSequence;
-import com.android.nn.benchmark.core.InferenceResult;
-import com.android.nn.benchmark.core.NNTestBase;
-import com.android.nn.benchmark.core.TestModels;
-import com.android.nn.benchmark.core.UnsupportedSdkException;
+import com.android.nn.benchmark.core.Processor;
-import java.util.List;
-import java.io.IOException;
-
-public class NNBenchmark extends Activity {
+public class NNBenchmark extends Activity implements Processor.Callback {
protected static final String TAG = "NN_BENCHMARK";
public static final String EXTRA_ENABLE_LONG = "enable long";
@@ -52,211 +41,25 @@
private BenchmarkResult mTestResults[];
private TextView mTextView;
- private boolean mToggleLong;
- private boolean mTogglePause;
-
- private boolean mUseNNApi;
- private boolean mCompleteInputSet;
-
- protected void setUseNNApi(boolean useNNApi) {
- mUseNNApi = useNNApi;
- }
-
- protected void setCompleteInputSet(boolean completeInputSet) {
- mCompleteInputSet = completeInputSet;
- }
// Initialize the parameters for Instrumentation tests.
protected void prepareInstrumentationTest() {
mTestList = new int[1];
mTestResults = new BenchmarkResult[1];
- mProcessor = new Processor();
+ mProcessor = new Processor(this, this, mTestList);
}
- /////////////////////////////////////////////////////////////////////////
- // Processor is a helper thread for running the work without
- // blocking the UI thread.
- class Processor extends Thread {
- private float mLastResult;
- private boolean mRun = true;
- private boolean mDoingBenchmark;
- private NNTestBase mTest;
-
- // Method to retrieve benchmark results for instrumentation tests.
- BenchmarkResult getInstrumentationResult(
- TestModels.TestModelEntry t, float warmupTimeSeconds, float runTimeSeconds)
- throws IOException {
- mTest = changeTest(mTest, t);
- return getBenchmark(warmupTimeSeconds, runTimeSeconds);
- }
-
- // Run one loop of kernels for at least the specified minimum time.
- // The function returns the average time in ms for the test run
- private BenchmarkResult runBenchmarkLoop(float minTime, boolean completeInputSet)
- throws IOException {
- try {
- // Run the kernel
- Pair<List<InferenceInOutSequence>, List<InferenceResult>> results;
- if (minTime > 0.f) {
- if (completeInputSet) {
- results = mTest.runBenchmarkCompleteInputSet(1, minTime);
- } else {
- results = mTest.runBenchmark(minTime);
- }
- } else {
- results = mTest.runInferenceOnce();
- }
- return BenchmarkResult.fromInferenceResults(
- mTest.getTestInfo(),
- mUseNNApi ? BenchmarkResult.BACKEND_TFLITE_NNAPI
- : BenchmarkResult.BACKEND_TFLITE_CPU,
- results.first, results.second, mTest.getEvaluator());
- } catch (BenchmarkException e) {
- return new BenchmarkResult(e.getMessage());
- }
- }
-
-
- // Get a benchmark result for a specific test
- private BenchmarkResult getBenchmark(float warmupTimeSeconds, float runTimeSeconds)
- throws IOException {
- try {
- mTest.checkSdkVersion();
- } catch (UnsupportedSdkException e) {
- BenchmarkResult r = new BenchmarkResult(e.getMessage());
- Log.v(TAG, "Test: " + r.toString());
- return r;
- }
-
- mDoingBenchmark = true;
-
- long result = 0;
-
- // We run a short bit of work before starting the actual test
- // this is to let any power management do its job and respond.
- // For NNAPI systrace usage documentation, see
- // frameworks/ml/nn/common/include/Tracing.h.
- try {
- final String traceName = "[NN_LA_PWU]runBenchmarkLoop";
- Trace.beginSection(traceName);
- runBenchmarkLoop(warmupTimeSeconds, false);
- } finally {
- Trace.endSection();
- }
-
- // Run the actual benchmark
- BenchmarkResult r;
- try {
- final String traceName = "[NN_LA_PBM]runBenchmarkLoop";
- Trace.beginSection(traceName);
- r = runBenchmarkLoop(runTimeSeconds, mCompleteInputSet);
- } finally {
- Trace.endSection();
- }
-
- Log.v(TAG, "Test: " + r.toString());
-
- mDoingBenchmark = false;
- return r;
- }
-
- @Override
- public void run() {
- while (mRun) {
- // Our loop for launching tests or benchmarks
- synchronized (this) {
- // We may have been asked to exit while waiting
- if (!mRun) return;
- }
-
- try {
- // Loop over the tests we want to benchmark
- for (int ct = 0; (ct < mTestList.length) && mRun; ct++) {
-
- // For reproducibility we wait a short time for any sporadic work
- // created by the user touching the screen to launch the test to pass.
- // Also allows for things to settle after the test changes.
- try {
- sleep(250);
- } catch (InterruptedException e) {
- }
-
- TestModels.TestModelEntry testModel =
- TestModels.modelsList().get(mTestList[ct]);
- int testNumber = ct + 1;
- runOnUiThread(() -> {
- mTextView.setText(
- String.format(
- "Running test %d of %d: %s",
- testNumber,
- mTestList.length,
- testModel.toString()));
- });
-
- // Select the next test
- mTest = changeTest(mTest, testModel);
-
- // If the user selected the "long pause" option, wait
- if (mTogglePause) {
- for (int i = 0; (i < 100) && mRun; i++) {
- try {
- sleep(100);
- } catch (InterruptedException e) {
- }
- }
- }
-
- // Run the test
- float warmupTime = 0.3f;
- float runTime = 1.f;
- if (mToggleLong) {
- warmupTime = 2.f;
- runTime = 10.f;
- }
- mTestResults[ct] = getBenchmark(warmupTime, runTime);
- }
- onBenchmarkFinish(mRun);
- } catch (IOException e) {
- Log.e(TAG, "Exception during benchmark run", e);
- break;
- }
- }
- }
-
- public void exit() {
- mRun = false;
-
- synchronized (this) {
- notifyAll();
- }
-
- try {
- this.join();
- } catch (InterruptedException e) {
- }
-
- if (mTest != null) {
- mTest.destroy();
- mTest = null;
- }
- }
+ public void setUseNNApi(boolean useNNApi) {
+ mProcessor.setUseNNApi(useNNApi);
}
+ public void setCompleteInputSet(boolean completeInputSet) {
+ mProcessor.setCompleteInputSet(completeInputSet);
+ }
private boolean mDoingBenchmark;
public Processor mProcessor;
- NNTestBase changeTest(NNTestBase oldTestBase, TestModels.TestModelEntry t) {
- if (oldTestBase != null) {
- // Make sure we don't leak memory.
- oldTestBase.destroy();
- }
- NNTestBase tb = t.createNNTestBase(mUseNNApi,
- false /* enableIntermediateTensorsDump */);
- tb.setupModel(this);
- return tb;
- }
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -279,7 +82,7 @@
if (ok) {
Intent intent = new Intent();
intent.putExtra(EXTRA_RESULTS_TESTS, mTestList);
- intent.putExtra(EXTRA_RESULTS_RESULTS, mTestResults);
+ intent.putExtra(EXTRA_RESULTS_RESULTS, mProcessor.getTestResults());
setResult(RESULT_OK, intent);
} else {
setResult(RESULT_CANCELED);
@@ -287,18 +90,25 @@
finish();
}
+ public void onStatusUpdate(int testNumber, int numTests, String modelName) {
+ runOnUiThread(
+ () -> {
+ mTextView.setText(
+ String.format(
+ "Running test %d of %d: %s", testNumber, numTests, modelName));
+ });
+ }
+
@Override
protected void onResume() {
super.onResume();
Intent i = getIntent();
mTestList = i.getIntArrayExtra(EXTRA_TESTS);
- mToggleLong = i.getBooleanExtra(EXTRA_ENABLE_LONG, false);
- mTogglePause = i.getBooleanExtra(EXTRA_ENABLE_PAUSE, false);
- setUseNNApi(!i.getBooleanExtra(EXTRA_DISABLE_NNAPI, false));
-
+ mProcessor = new Processor(this, this, mTestList);
+ mProcessor.setToggleLong(i.getBooleanExtra(EXTRA_ENABLE_LONG, false));
+ mProcessor.setTogglePause(i.getBooleanExtra(EXTRA_ENABLE_PAUSE, false));
+ mProcessor.setUseNNApi(!i.getBooleanExtra(EXTRA_DISABLE_NNAPI, false));
if (mTestList != null) {
- mTestResults = new BenchmarkResult[mTestList.length];
- mProcessor = new Processor();
mProcessor.start();
}
}
diff --git a/src/com/android/nn/benchmark/core/NNTestBase.java b/src/com/android/nn/benchmark/core/NNTestBase.java
index d497396..63f7bc8 100644
--- a/src/com/android/nn/benchmark/core/NNTestBase.java
+++ b/src/com/android/nn/benchmark/core/NNTestBase.java
@@ -16,7 +16,7 @@
package com.android.nn.benchmark.core;
-import android.app.Activity;
+import android.content.Context;
import android.content.res.AssetManager;
import android.os.Build;
import android.util.Log;
@@ -73,7 +73,7 @@
String dumpPath,
List<InferenceInOutSequence> inOutList);
- protected Activity mActivity;
+ protected Context mContext;
protected TextView mText;
private String mModelName;
private String mModelFile;
@@ -135,8 +135,8 @@
mNNApiDeviceName = Optional.ofNullable(value);
}
- public final boolean setupModel(Activity ipact) {
- mActivity = ipact;
+ public final boolean setupModel(Context ipcxt) {
+ mContext = ipcxt;
String modelFileName = copyAssetToFile();
if (modelFileName != null) {
mModelHandle = initModel(
@@ -149,7 +149,7 @@
resizeInputTensors(mModelHandle, mInputShape);
}
if (mEvaluatorConfig != null) {
- mEvaluator = mEvaluatorConfig.createEvaluator(mActivity.getAssets());
+ mEvaluator = mEvaluatorConfig.createEvaluator(mContext.getAssets());
}
return true;
}
@@ -174,13 +174,13 @@
List<InferenceInOutSequence> inOutList = new ArrayList<>();
if (mInputOutputAssets != null) {
for (InferenceInOutSequence.FromAssets ioAsset : mInputOutputAssets) {
- inOutList.add(ioAsset.readAssets(mActivity.getAssets()));
+ inOutList.add(ioAsset.readAssets(mContext.getAssets()));
}
}
if (mInputOutputDatasets != null) {
for (InferenceInOutSequence.FromDataset dataset : mInputOutputDatasets) {
- inOutList.addAll(dataset.readDataset(mActivity.getAssets(),
- mActivity.getCacheDir()));
+ inOutList.addAll(dataset.readDataset(mContext.getAssets(),
+ mContext.getCacheDir()));
}
}
@@ -297,11 +297,11 @@
private String copyAssetToFile() {
String outFileName;
String modelAssetName = mModelFile + ".tflite";
- AssetManager assetManager = mActivity.getAssets();
+ AssetManager assetManager = mContext.getAssets();
try {
InputStream in = assetManager.open(modelAssetName);
- outFileName = mActivity.getCacheDir().getAbsolutePath() + "/" + modelAssetName;
+ outFileName = mContext.getCacheDir().getAbsolutePath() + "/" + modelAssetName;
File outFile = new File(outFileName);
OutputStream out = new FileOutputStream(outFile);
diff --git a/src/com/android/nn/benchmark/core/Processor.java b/src/com/android/nn/benchmark/core/Processor.java
new file mode 100644
index 0000000..1aa6008
--- /dev/null
+++ b/src/com/android/nn/benchmark/core/Processor.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2019 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.nn.benchmark.core;
+
+import android.content.Context;
+import android.os.Trace;
+import android.util.Log;
+import android.util.Pair;
+
+import java.io.IOException;
+import java.util.List;
+
+/** Processor is a helper thread for running the work without blocking the UI thread. */
+public class Processor extends Thread {
+
+ public interface Callback {
+ public void onBenchmarkFinish(boolean ok);
+
+ public void onStatusUpdate(int testNumber, int numTests, String modelName);
+ }
+
+ protected static final String TAG = "NN_BENCHMARK";
+ private Context mContext;
+
+ private float mLastResult;
+ private boolean mRun = true;
+ private boolean mDoingBenchmark;
+ private NNTestBase mTest;
+ private int mTestList[];
+ private BenchmarkResult mTestResults[];
+
+ private Processor.Callback mCallback;
+
+ private boolean mUseNNApi;
+ private boolean mCompleteInputSet;
+ private boolean mToggleLong;
+ private boolean mTogglePause;
+
+ public Processor(Context context, Processor.Callback callback, int[] testList) {
+ mContext = context;
+ mCallback = callback;
+ mTestList = testList;
+ if (mTestList != null) {
+ mTestResults = new BenchmarkResult[mTestList.length];
+ }
+ }
+
+ public void setUseNNApi(boolean useNNApi) {
+ mUseNNApi = useNNApi;
+ }
+
+ public void setCompleteInputSet(boolean completeInputSet) {
+ mCompleteInputSet = completeInputSet;
+ }
+
+ public void setToggleLong(boolean toggleLong) {
+ mToggleLong = toggleLong;
+ }
+
+ public void setTogglePause(boolean togglePause) {
+ mTogglePause = togglePause;
+ }
+
+ // Method to retrieve benchmark results for instrumentation tests.
+ public BenchmarkResult getInstrumentationResult(
+ TestModels.TestModelEntry t, float warmupTimeSeconds, float runTimeSeconds)
+ throws IOException {
+ mTest = changeTest(mTest, t);
+ return getBenchmark(warmupTimeSeconds, runTimeSeconds);
+ }
+
+ private NNTestBase changeTest(NNTestBase oldTestBase, TestModels.TestModelEntry t) {
+ if (oldTestBase != null) {
+ // Make sure we don't leak memory.
+ oldTestBase.destroy();
+ }
+ NNTestBase tb = t.createNNTestBase(mUseNNApi, false /* enableIntermediateTensorsDump */);
+ tb.setupModel(mContext);
+ return tb;
+ }
+
+ // Run one loop of kernels for at least the specified minimum time.
+ // The function returns the average time in ms for the test run
+ private BenchmarkResult runBenchmarkLoop(float minTime, boolean completeInputSet)
+ throws IOException {
+ try {
+ // Run the kernel
+ Pair<List<InferenceInOutSequence>, List<InferenceResult>> results;
+ if (minTime > 0.f) {
+ if (completeInputSet) {
+ results = mTest.runBenchmarkCompleteInputSet(1, minTime);
+ } else {
+ results = mTest.runBenchmark(minTime);
+ }
+ } else {
+ results = mTest.runInferenceOnce();
+ }
+ return BenchmarkResult.fromInferenceResults(
+ mTest.getTestInfo(),
+ mUseNNApi
+ ? BenchmarkResult.BACKEND_TFLITE_NNAPI
+ : BenchmarkResult.BACKEND_TFLITE_CPU,
+ results.first,
+ results.second,
+ mTest.getEvaluator());
+ } catch (BenchmarkException e) {
+ return new BenchmarkResult(e.getMessage());
+ }
+ }
+
+ public BenchmarkResult[] getTestResults() {
+ return mTestResults;
+ }
+
+ // Get a benchmark result for a specific test
+ private BenchmarkResult getBenchmark(float warmupTimeSeconds, float runTimeSeconds)
+ throws IOException {
+ try {
+ mTest.checkSdkVersion();
+ } catch (UnsupportedSdkException e) {
+ BenchmarkResult r = new BenchmarkResult(e.getMessage());
+ Log.v(TAG, "Test: " + r.toString());
+ return r;
+ }
+
+ mDoingBenchmark = true;
+
+ long result = 0;
+
+ // We run a short bit of work before starting the actual test
+ // this is to let any power management do its job and respond.
+ // For NNAPI systrace usage documentation, see
+ // frameworks/ml/nn/common/include/Tracing.h.
+ try {
+ final String traceName = "[NN_LA_PWU]runBenchmarkLoop";
+ Trace.beginSection(traceName);
+ runBenchmarkLoop(warmupTimeSeconds, false);
+ } finally {
+ Trace.endSection();
+ }
+
+ // Run the actual benchmark
+ BenchmarkResult r;
+ try {
+ final String traceName = "[NN_LA_PBM]runBenchmarkLoop";
+ Trace.beginSection(traceName);
+ r = runBenchmarkLoop(runTimeSeconds, mCompleteInputSet);
+ } finally {
+ Trace.endSection();
+ }
+
+ Log.v(TAG, "Test: " + r.toString());
+
+ mDoingBenchmark = false;
+ return r;
+ }
+
+ @Override
+ public void run() {
+ while (mRun) {
+ // Our loop for launching tests or benchmarks
+ synchronized (this) {
+ // We may have been asked to exit while waiting
+ if (!mRun) return;
+ }
+
+ try {
+ // Loop over the tests we want to benchmark
+ for (int ct = 0; (ct < mTestList.length) && mRun; ct++) {
+
+ // For reproducibility we wait a short time for any sporadic work
+ // created by the user touching the screen to launch the test to pass.
+ // Also allows for things to settle after the test changes.
+ try {
+ sleep(250);
+ } catch (InterruptedException e) {
+ }
+
+ TestModels.TestModelEntry testModel =
+ TestModels.modelsList().get(mTestList[ct]);
+ int testNumber = ct + 1;
+ mCallback.onStatusUpdate(testNumber, mTestList.length, testModel.toString());
+
+ // Select the next test
+ mTest = changeTest(mTest, testModel);
+
+ // If the user selected the "long pause" option, wait
+ if (mTogglePause) {
+ for (int i = 0; (i < 100) && mRun; i++) {
+ try {
+ sleep(100);
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+
+ // Run the test
+ float warmupTime = 0.3f;
+ float runTime = 1.f;
+ if (mToggleLong) {
+ warmupTime = 2.f;
+ runTime = 10.f;
+ }
+ mTestResults[ct] = getBenchmark(warmupTime, runTime);
+ }
+ mCallback.onBenchmarkFinish(mRun);
+ } catch (IOException e) {
+ Log.e(TAG, "Exception during benchmark run", e);
+ break;
+ }
+ }
+ }
+
+ public void exit() {
+ mRun = false;
+
+ synchronized (this) {
+ notifyAll();
+ }
+ // exit() is called on same thread when run via dogfood BenchmarkJobService
+ if (this != Thread.currentThread()) {
+ try {
+ this.join();
+ } catch (InterruptedException e) {
+ }
+ }
+
+ if (mTest != null) {
+ mTest.destroy();
+ mTest = null;
+ }
+ }
+}