Add performance test for BroadcastReceiver

This is intended to be the first of multiple performance tests around
ActivityManager.

This also refactors some of the existing performance utils so that a
BenchmarkState which accepts the elapsed time rather than measures it
can be added (ManualBenchmarkState).

This test measures the current time, sends a Broadcast, the target APK
receives it, measures the current time, and sends its measured time
back to the test APK.

Test: m ActivityManagerPerfTestsTestApp ActivityManagerPerfTests
Test: adb install $OUT/data/app/ActivityManagerPerfTestsTestApp/ActivityManagerPerfTestsTestApp.apk
Test: adb install $OUT/data/app/ActivityManagerPerfTests/ActivityManagerPerfTests.apk
Test: adb shell am instrument -w -e class \
com.android.frameworks.perftests.am.tests.BroadcastPerfTest \
com.android.frameworks.perftests.amtests/android.support.test.runner.AndroidJUnitRunner

Bug: 67460485

Change-Id: Ib1606ff60c6a845088bde5bd1a33294765b88b36
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java b/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
index bb9dc4a..da17818 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
@@ -25,7 +25,6 @@
 
 import java.io.File;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -78,10 +77,7 @@
 
     // Statistics. These values will be filled when the benchmark has finished.
     // The computation needs double precision, but long int is fine for final reporting.
-    private long mMedian = 0;
-    private double mMean = 0.0;
-    private double mStandardDeviation = 0.0;
-    private long mMin = 0;
+    private Stats mStats;
 
     // Individual duration in nano seconds.
     private ArrayList<Long> mResults = new ArrayList<>();
@@ -90,36 +86,6 @@
         return TimeUnit.MILLISECONDS.toNanos(ms);
     }
 
-    /**
-     * Calculates statistics.
-     */
-    private void calculateSatistics() {
-        final int size = mResults.size();
-        if (size <= 1) {
-            throw new IllegalStateException("At least two results are necessary.");
-        }
-
-        Collections.sort(mResults);
-        mMedian = size % 2 == 0 ? (mResults.get(size / 2) + mResults.get(size / 2 + 1)) / 2 :
-                mResults.get(size / 2);
-
-        mMin = mResults.get(0);
-        for (int i = 0; i < size; ++i) {
-            long result = mResults.get(i);
-            mMean += result;
-            if (result < mMin) {
-                mMin = result;
-            }
-        }
-        mMean /= (double) size;
-
-        for (int i = 0; i < size; ++i) {
-            final double tmp = mResults.get(i) - mMean;
-            mStandardDeviation += tmp * tmp;
-        }
-        mStandardDeviation = Math.sqrt(mStandardDeviation / (double) (size - 1));
-    }
-
     // Stops the benchmark timer.
     // This method can be called only when the timer is running.
     public void pauseTiming() {
@@ -173,7 +139,7 @@
             if (ENABLE_PROFILING) {
                 Debug.stopMethodTracing();
             }
-            calculateSatistics();
+            mStats = new Stats(mResults);
             mState = FINISHED;
             return false;
         }
@@ -224,28 +190,28 @@
         if (mState != FINISHED) {
             throw new IllegalStateException("The benchmark hasn't finished");
         }
-        return (long) mMean;
+        return (long) mStats.getMean();
     }
 
     private long median() {
         if (mState != FINISHED) {
             throw new IllegalStateException("The benchmark hasn't finished");
         }
-        return mMedian;
+        return mStats.getMedian();
     }
 
     private long min() {
         if (mState != FINISHED) {
             throw new IllegalStateException("The benchmark hasn't finished");
         }
-        return mMin;
+        return mStats.getMin();
     }
 
     private long standardDeviation() {
         if (mState != FINISHED) {
             throw new IllegalStateException("The benchmark hasn't finished");
         }
-        return (long) mStandardDeviation;
+        return (long) mStats.getStandardDeviation();
     }
 
     private String summaryLine() {
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java b/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java
new file mode 100644
index 0000000..2c84db1
--- /dev/null
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java
@@ -0,0 +1,157 @@
+/*
+ * 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 android.perftests.utils;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Provides a benchmark framework.
+ *
+ * This differs from BenchmarkState in that rather than the class measuring the the elapsed time,
+ * the test passes in the elapsed time.
+ *
+ * Example usage:
+ *
+ * public void sampleMethod() {
+ *     ManualBenchmarkState state = new ManualBenchmarkState();
+ *
+ *     int[] src = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ *     long elapsedTime = 0;
+ *     while (state.keepRunning(elapsedTime)) {
+ *         long startTime = System.nanoTime();
+ *         int[] dest = new int[src.length];
+ *         System.arraycopy(src, 0, dest, 0, src.length);
+ *         elapsedTime = System.nanoTime() - startTime;
+ *     }
+ *     System.out.println(state.summaryLine());
+ * }
+ *
+ * Or use the PerfManualStatusReporter TestRule.
+ *
+ * Make sure that the overhead of checking the clock does not noticeably affect the results.
+ */
+public final class ManualBenchmarkState {
+    private static final String TAG = ManualBenchmarkState.class.getSimpleName();
+
+    // TODO: Tune these values.
+    // warm-up for duration
+    private static final long WARMUP_DURATION_NS = TimeUnit.SECONDS.toNanos(5);
+    // minimum iterations to warm-up for
+    private static final int WARMUP_MIN_ITERATIONS = 8;
+
+    // target testing for duration
+    private static final long TARGET_TEST_DURATION_NS = TimeUnit.SECONDS.toNanos(16);
+    private static final int MAX_TEST_ITERATIONS = 1000000;
+    private static final int MIN_TEST_ITERATIONS = 10;
+
+    private static final int NOT_STARTED = 0;  // The benchmark has not started yet.
+    private static final int WARMUP = 1; // The benchmark is warming up.
+    private static final int RUNNING = 2;  // The benchmark is running.
+    private static final int FINISHED = 3;  // The benchmark has stopped.
+
+    private int mState = NOT_STARTED;  // Current benchmark state.
+
+    private long mWarmupStartTime = 0;
+    private int mWarmupIterations = 0;
+
+    private int mMaxIterations = 0;
+
+    // Individual duration in nano seconds.
+    private ArrayList<Long> mResults = new ArrayList<>();
+
+    // Statistics. These values will be filled when the benchmark has finished.
+    // The computation needs double precision, but long int is fine for final reporting.
+    private Stats mStats;
+
+    private void beginBenchmark(long warmupDuration, int iterations) {
+        mMaxIterations = (int) (TARGET_TEST_DURATION_NS / (warmupDuration / iterations));
+        mMaxIterations = Math.min(MAX_TEST_ITERATIONS,
+                Math.max(mMaxIterations, MIN_TEST_ITERATIONS));
+        mState = RUNNING;
+    }
+
+    /**
+     * Judges whether the benchmark needs more samples.
+     *
+     * For the usage, see class comment.
+     */
+    public boolean keepRunning(long duration) {
+        if (duration < 0) {
+            throw new RuntimeException("duration is negative: " + duration);
+        }
+        switch (mState) {
+            case NOT_STARTED:
+                mState = WARMUP;
+                mWarmupStartTime = System.nanoTime();
+                return true;
+            case WARMUP: {
+                final long timeSinceStartingWarmup = System.nanoTime() - mWarmupStartTime;
+                ++mWarmupIterations;
+                if (mWarmupIterations >= WARMUP_MIN_ITERATIONS
+                        && timeSinceStartingWarmup >= WARMUP_DURATION_NS) {
+                    beginBenchmark(timeSinceStartingWarmup, mWarmupIterations);
+                }
+                return true;
+            }
+            case RUNNING: {
+                mResults.add(duration);
+                final boolean keepRunning = mResults.size() < mMaxIterations;
+                if (!keepRunning) {
+                    mStats = new Stats(mResults);
+                    mState = FINISHED;
+                }
+                return keepRunning;
+            }
+            case FINISHED:
+                throw new IllegalStateException("The benchmark has finished.");
+            default:
+                throw new IllegalStateException("The benchmark is in an unknown state.");
+        }
+    }
+
+    private String summaryLine() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("Summary: ");
+        sb.append("median=").append(mStats.getMedian()).append("ns, ");
+        sb.append("mean=").append(mStats.getMean()).append("ns, ");
+        sb.append("min=").append(mStats.getMin()).append("ns, ");
+        sb.append("max=").append(mStats.getMax()).append("ns, ");
+        sb.append("sigma=").append(mStats.getStandardDeviation()).append(", ");
+        sb.append("iteration=").append(mResults.size()).append(", ");
+        sb.append("values=").append(mResults.toString());
+        return sb.toString();
+    }
+
+    public void sendFullStatusReport(Instrumentation instrumentation, String key) {
+        if (mState != FINISHED) {
+            throw new IllegalStateException("The benchmark hasn't finished");
+        }
+        Log.i(TAG, key + summaryLine());
+        final Bundle status = new Bundle();
+        status.putLong(key + "_median", mStats.getMedian());
+        status.putLong(key + "_mean", (long) mStats.getMean());
+        status.putLong(key + "_stddev", (long) mStats.getStandardDeviation());
+        instrumentation.sendStatus(Activity.RESULT_OK, status);
+    }
+}
+
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/PerfManualStatusReporter.java b/apct-tests/perftests/utils/src/android/perftests/utils/PerfManualStatusReporter.java
new file mode 100644
index 0000000..0de6f1d
--- /dev/null
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/PerfManualStatusReporter.java
@@ -0,0 +1,73 @@
+/*
+ * 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 android.perftests.utils;
+
+import android.support.test.InstrumentationRegistry;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * Use this rule to make sure we report the status after the test success.
+ *
+ * <code>
+ *
+ * @Rule public PerfManualStatusReporter mPerfStatusReporter = new PerfManualStatusReporter();
+ * @Test public void functionName() {
+ *     ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ *
+ *     int[] src = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ *     long elapsedTime = 0;
+ *     while (state.keepRunning(elapsedTime)) {
+ *         long startTime = System.nanoTime();
+ *         int[] dest = new int[src.length];
+ *         System.arraycopy(src, 0, dest, 0, src.length);
+ *         elapsedTime = System.nanoTime() - startTime;
+ *     }
+ * }
+ * </code>
+ *
+ * When test succeeded, the status report will use the key as
+ * "functionName_*"
+ */
+
+public class PerfManualStatusReporter implements TestRule {
+    private final ManualBenchmarkState mState;
+
+    public PerfManualStatusReporter() {
+        mState = new ManualBenchmarkState();
+    }
+
+    public ManualBenchmarkState getBenchmarkState() {
+        return mState;
+    }
+
+    @Override
+    public Statement apply(Statement base, Description description) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                base.evaluate();
+
+                mState.sendFullStatusReport(InstrumentationRegistry.getInstrumentation(),
+                        description.getMethodName());
+            }
+        };
+    }
+}
+
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/Stats.java b/apct-tests/perftests/utils/src/android/perftests/utils/Stats.java
new file mode 100644
index 0000000..acc44a8
--- /dev/null
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/Stats.java
@@ -0,0 +1,76 @@
+/*
+ * 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 android.perftests.utils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class Stats {
+    private long mMedian, mMin, mMax;
+    private double mMean, mStandardDeviation;
+
+    /* Calculate stats in constructor. */
+    public Stats(List<Long> values) {
+        // make a copy since we're modifying it
+        values = new ArrayList<>(values);
+        final int size = values.size();
+        if (size < 2) {
+            throw new IllegalArgumentException("At least two results are necessary.");
+        }
+
+        Collections.sort(values);
+
+        mMedian = size % 2 == 0 ? (values.get(size / 2) + values.get(size / 2 - 1)) / 2 :
+                values.get(size / 2);
+
+        mMin = values.get(0);
+        mMax = values.get(values.size() - 1);
+
+        for (int i = 0; i < size; ++i) {
+            long result = values.get(i);
+            mMean += result;
+        }
+        mMean /= (double) size;
+
+        for (int i = 0; i < size; ++i) {
+            final double tmp = values.get(i) - mMean;
+            mStandardDeviation += tmp * tmp;
+        }
+        mStandardDeviation = Math.sqrt(mStandardDeviation / (double) (size - 1));
+    }
+
+    public double getMean() {
+        return mMean;
+    }
+
+    public long getMedian() {
+        return mMedian;
+    }
+
+    public long getMax() {
+        return mMax;
+    }
+
+    public long getMin() {
+        return mMin;
+    }
+
+    public double getStandardDeviation() {
+        return mStandardDeviation;
+    }
+}
diff --git a/tests/ActivityManagerPerfTests/README.txt b/tests/ActivityManagerPerfTests/README.txt
new file mode 100644
index 0000000..77e0e90
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/README.txt
@@ -0,0 +1,34 @@
+ActivityManagerPerfTests
+
+Performance tests for various ActivityManager components, e.g. Services, Broadcasts
+
+Command to run tests (not working yet, atest seems buggy)
+* atest .../frameworks/base/tests/ActivityManagerPerfTests
+* m ActivityManagerPerfTests ActivityManagerPerfTestsTestApp && \
+  adb install $OUT/data/app/ActivityManagerPerfTests/ActivityManagerPerfTests.apk && \
+  adb install $OUT/data/app/ActivityManagerPerfTestsTestApp/ActivityManagerPerfTestsTestApp.apk && \
+  adb shell am instrument -w \
+  com.android.frameworks.perftests.amtests/android.support.test.runner.AndroidJUnitRunner
+
+Overview
+* The numbers we are trying to measure are end-to-end numbers
+  * For example, the time it takes from sending an Intent to start a Service
+    to the time the Service runs its callbacks
+* System.nanoTime() is monotonic and consistent between processes, so we use that for measuring time
+* To make sure the test app is running, we start an Activity
+* If the test app is involved, it will measure the time and send it back to the instrumentation test
+  * The time is sent back through a Binder interface in the Intent
+  * Each sent time is tagged with an id since there can be multiple events that send back a time
+    * For example, one is sent when the Activity is started, and another could be sent when a
+      Broadcast is received
+
+Structure
+* tests
+  * Instrumentation test which runs the various performance tests and reports the results
+
+* test-app
+  * Target package which contains the Services, BroadcastReceivers, etc. to test against
+  * Sends the time it measures back to the test package
+
+* utils
+  * Utilities that both the instrumentation test and test app can use
diff --git a/tests/ActivityManagerPerfTests/test-app/Android.mk b/tests/ActivityManagerPerfTests/test-app/Android.mk
new file mode 100644
index 0000000..b0a5db7
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/test-app/Android.mk
@@ -0,0 +1,28 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    ActivityManagerPerfTestsUtils
+
+LOCAL_PACKAGE_NAME := ActivityManagerPerfTestsTestApp
+
+include $(BUILD_PACKAGE)
diff --git a/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml b/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml
new file mode 100644
index 0000000..7145110
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.frameworks.perftests.amteststestapp">
+    <application android:name=".TestApplication">
+        <activity android:name=".TestActivity" android:exported="true"/>
+        <receiver
+            android:name=".TestBroadcastReceiver"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="com.android.frameworks.perftests.ACTION_BROADCAST_MANIFEST_RECEIVE" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </receiver>
+    </application>
+</manifest>
diff --git a/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestActivity.java b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestActivity.java
new file mode 100644
index 0000000..7ea9ba3
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestActivity.java
@@ -0,0 +1,32 @@
+/*
+ * 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.frameworks.perftests.amteststestapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import com.android.frameworks.perftests.am.util.Constants;
+import com.android.frameworks.perftests.am.util.Utils;
+
+public class TestActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Utils.sendTime(getIntent(), Constants.TYPE_ACTIVITY_CREATED);
+    }
+}
diff --git a/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestApplication.java b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestApplication.java
new file mode 100644
index 0000000..e26ffcd
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestApplication.java
@@ -0,0 +1,51 @@
+/*
+ * 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.frameworks.perftests.amteststestapp;
+
+import android.app.Application;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.util.Log;
+
+import com.android.frameworks.perftests.am.util.Constants;
+import com.android.frameworks.perftests.am.util.Utils;
+
+public class TestApplication extends Application {
+    private static final String TAG = TestApplication.class.getSimpleName();
+
+    @Override
+    public void onCreate() {
+        createRegisteredReceiver();
+
+        super.onCreate();
+    }
+
+    // Create registered BroadcastReceiver
+    private void createRegisteredReceiver() {
+        BroadcastReceiver registered = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                Log.i(TAG, "RegisteredReceiver.onReceive");
+                Utils.sendTime(intent, Constants.TYPE_BROADCAST_RECEIVE);
+            }
+        };
+        IntentFilter intentFilter = new IntentFilter(Constants.ACTION_BROADCAST_REGISTERED_RECEIVE);
+        registerReceiver(registered, intentFilter);
+    }
+}
diff --git a/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestBroadcastReceiver.java b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestBroadcastReceiver.java
new file mode 100644
index 0000000..336bf9d
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestBroadcastReceiver.java
@@ -0,0 +1,32 @@
+/*
+ * 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.frameworks.perftests.amteststestapp;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+import com.android.frameworks.perftests.am.util.Constants;
+import com.android.frameworks.perftests.am.util.Utils;
+
+public class TestBroadcastReceiver extends BroadcastReceiver {
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        Utils.sendTime(intent, Constants.TYPE_BROADCAST_RECEIVE);
+    }
+}
diff --git a/tests/ActivityManagerPerfTests/tests/Android.mk b/tests/ActivityManagerPerfTests/tests/Android.mk
new file mode 100644
index 0000000..daf603d
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/tests/Android.mk
@@ -0,0 +1,33 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    apct-perftests-utils \
+    ActivityManagerPerfTestsUtils
+
+LOCAL_PACKAGE_NAME := ActivityManagerPerfTests
+
+# For android.permission.FORCE_STOP_PACKAGES permission
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
diff --git a/tests/ActivityManagerPerfTests/tests/AndroidManifest.xml b/tests/ActivityManagerPerfTests/tests/AndroidManifest.xml
new file mode 100644
index 0000000..4e194c6
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/tests/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.frameworks.perftests.amtests">
+    <uses-permission android:name="android.permission.DUMP" />
+    <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.frameworks.perftests.amtests"/>
+</manifest>
diff --git a/tests/ActivityManagerPerfTests/tests/AndroidTest.xml b/tests/ActivityManagerPerfTests/tests/AndroidTest.xml
new file mode 100644
index 0000000..ffb5404
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/tests/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs ActivityManager Performance Tests">
+    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="test-file-name" value="ActivityManagerPerfTests.apk"/>
+        <option name="test-file-name" value="ActivityManagerPerfTestsTestApp.apk"/>
+        <option name="cleanup-apks" value="true"/>
+    </target_preparer>
+
+    <option name="test-suite-tag" value="apct"/>
+    <option name="test-tag" value="ActivityManagerPerfTests"/>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="com.android.frameworks.perftests.amtests"/>
+        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BasePerfTest.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BasePerfTest.java
new file mode 100644
index 0000000..661abe9
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BasePerfTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.frameworks.perftests.am.tests;
+
+import android.content.Context;
+import android.content.Intent;
+import android.perftests.utils.ManualBenchmarkState;
+import android.perftests.utils.PerfManualStatusReporter;
+import android.support.test.InstrumentationRegistry;
+
+import com.android.frameworks.perftests.am.util.TargetPackageUtils;
+import com.android.frameworks.perftests.am.util.TimeReceiver;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+
+import java.util.function.LongSupplier;
+
+public class BasePerfTest {
+    private static final String TAG = BasePerfTest.class.getSimpleName();
+
+    private TimeReceiver mTimeReceiver;
+
+    @Rule
+    public PerfManualStatusReporter mPerfManualStatusReporter = new PerfManualStatusReporter();
+
+    protected Context mContext;
+
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mTimeReceiver = new TimeReceiver();
+    }
+
+    @After
+    public void tearDown() {
+        TargetPackageUtils.killTargetPackage(mContext);
+    }
+
+    protected Intent createIntent(String action) {
+        final Intent intent = new Intent(action);
+        intent.addFlags(
+                Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND | Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
+        intent.putExtras(mTimeReceiver.createReceiveTimeExtraBinder());
+        return intent;
+    }
+
+    private void setUpIteration() {
+        mTimeReceiver.clear();
+        TargetPackageUtils.killTargetPackage(mContext);
+    }
+
+    protected void startTargetPackage() {
+        TargetPackageUtils.startTargetPackage(mContext, mTimeReceiver);
+    }
+
+    protected long getReceivedTimeNs(String type) {
+        return mTimeReceiver.getReceivedTimeNs(type);
+    }
+
+    protected void runPerfFunction(LongSupplier func) {
+        final ManualBenchmarkState benchmarkState = mPerfManualStatusReporter.getBenchmarkState();
+        long elapsedTimeNs = 0;
+        while (benchmarkState.keepRunning(elapsedTimeNs)) {
+            setUpIteration();
+            elapsedTimeNs = func.getAsLong();
+        }
+    }
+}
diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BroadcastPerfTest.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BroadcastPerfTest.java
new file mode 100644
index 0000000..795f498
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BroadcastPerfTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.frameworks.perftests.am.tests;
+
+import android.content.Intent;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.frameworks.perftests.am.util.Constants;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class BroadcastPerfTest extends BasePerfTest {
+    @Test
+    public void manifestBroadcastRunning() {
+        runPerfFunction(() -> {
+            startTargetPackage();
+
+            final Intent intent = createIntent(Constants.ACTION_BROADCAST_MANIFEST_RECEIVE);
+
+            final long startTime = System.nanoTime();
+
+            mContext.sendBroadcast(intent);
+
+            final long endTime = getReceivedTimeNs(Constants.TYPE_BROADCAST_RECEIVE);
+
+            return endTime - startTime;
+        });
+    }
+
+    @Test
+    public void manifestBroadcastNotRunning() {
+        runPerfFunction(() -> {
+            final Intent intent = createIntent(Constants.ACTION_BROADCAST_MANIFEST_RECEIVE);
+
+            final long startTime = System.nanoTime();
+
+            mContext.sendBroadcast(intent);
+
+            final long endTime = getReceivedTimeNs(Constants.TYPE_BROADCAST_RECEIVE);
+
+            return endTime - startTime;
+        });
+    }
+
+    @Test
+    public void registeredBroadcast() {
+        runPerfFunction(() -> {
+            startTargetPackage();
+
+            final Intent intent = createIntent(Constants.ACTION_BROADCAST_REGISTERED_RECEIVE);
+
+            final long startTime = System.nanoTime();
+
+            mContext.sendBroadcast(intent);
+
+            final long endTime = getReceivedTimeNs(Constants.TYPE_BROADCAST_RECEIVE);
+
+            return endTime - startTime;
+        });
+    }
+}
diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TargetPackageUtils.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TargetPackageUtils.java
new file mode 100644
index 0000000..c867141
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TargetPackageUtils.java
@@ -0,0 +1,93 @@
+/*
+ * 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.frameworks.perftests.am.util;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.SystemClock;
+
+public class TargetPackageUtils {
+    private static final String TAG = TargetPackageUtils.class.getSimpleName();
+
+    public static final String PACKAGE_NAME = "com.android.frameworks.perftests.amteststestapp";
+    public static final String ACTIVITY_NAME = PACKAGE_NAME + ".TestActivity";
+
+    private static final long WAIT_TIME_MS = 100L;
+
+    // Cache for test app's uid, so we only have to query it once.
+    private static int sTestAppUid = -1;
+
+    /**
+     * Kills the test package synchronously.
+     */
+    public static void killTargetPackage(Context context) {
+        ActivityManager activityManager = context.getSystemService(ActivityManager.class);
+        activityManager.forceStopPackage(PACKAGE_NAME);
+        while (targetPackageIsRunning(context)) {
+            sleep();
+        }
+
+        Utils.drainBroadcastQueue();
+    }
+
+    /**
+     * Starts the test package synchronously. It does so by starting an Activity.
+     */
+    public static void startTargetPackage(Context context, TimeReceiver timeReceiver) {
+        // "am start-activity -W PACKAGE_NAME/ACTIVITY_CLASS_NAME" still requires a sleep even
+        // though it should be synchronous, so just use Intent instead
+        final Intent intent = new Intent();
+        intent.putExtras(timeReceiver.createReceiveTimeExtraBinder());
+        intent.setClassName(PACKAGE_NAME, ACTIVITY_NAME);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        context.startActivity(intent);
+
+        while (!targetPackageIsRunning(context)) {
+            sleep();
+        }
+        // make sure Application has run
+        timeReceiver.getReceivedTimeNs(Constants.TYPE_ACTIVITY_CREATED);
+        Utils.drainBroadcastQueue();
+    }
+
+    private static boolean targetPackageIsRunning(Context context) {
+        final int uid = getTestAppUid(context);
+        final String result = Utils.runShellCommand(
+                String.format("cmd activity get-uid-state %d", uid));
+        return !result.contains("(NONEXISTENT)");
+    }
+
+    private static void sleep() {
+        SystemClock.sleep(WAIT_TIME_MS);
+    }
+
+    private static int getTestAppUid(Context context) {
+        if (sTestAppUid == -1) {
+            final PackageManager pm = context.getPackageManager();
+            try {
+                sTestAppUid = pm.getPackageUid(PACKAGE_NAME, 0);
+            } catch (PackageManager.NameNotFoundException e) {
+                throw new RuntimeException(e);
+            }
+        }
+        return sTestAppUid;
+    }
+
+}
+
diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TimeReceiver.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TimeReceiver.java
new file mode 100644
index 0000000..9cf6ee7
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TimeReceiver.java
@@ -0,0 +1,107 @@
+/*
+ * 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.frameworks.perftests.am.util;
+
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+
+/**
+ * TimeReceiver will listen for any messages containing a timestamp by starting a BroadcastReceiver
+ * which listens for Intents with the SendTime.ACTION_SEND_TIME action.
+ */
+public class TimeReceiver {
+    private static final String TAG = TimeReceiver.class.getSimpleName();
+    private static final long DEFAULT_RECEIVE_TIME_TIMEOUT_MILLIS = 10000L;
+
+    private BlockingQueue<ReceivedMessage> mQueue = new LinkedBlockingQueue<>();
+
+    private static class ReceivedMessage {
+        private final String mReceivedMessageType;
+        private final long mReceivedTimeNs;
+
+        public ReceivedMessage(String receivedMessageType, long receivedTimeNs) {
+            mReceivedMessageType = receivedMessageType;
+            mReceivedTimeNs = receivedTimeNs;
+        }
+    }
+
+    public Bundle createReceiveTimeExtraBinder() {
+        Bundle extras = new Bundle();
+        extras.putBinder(Constants.EXTRA_RECEIVER_CALLBACK, new ITimeReceiverCallback.Stub() {
+            @Override
+            public void sendTime(String type, long timeNs) throws RemoteException {
+                if (type == null) {
+                    throw new RuntimeException("receivedType is null");
+                }
+                if (timeNs < 0) {
+                    throw new RuntimeException(
+                            "receivedTime is negative/non-existant: " + timeNs);
+                }
+                Log.i(TAG, type + " " + timeNs);
+                mQueue.add(new ReceivedMessage(type, timeNs));
+            }
+        });
+        return extras;
+    }
+
+    public long getReceivedTimeNs(String type) {
+        return getReceivedTimeNs(type, DEFAULT_RECEIVE_TIME_TIMEOUT_MILLIS);
+    }
+
+    /**
+     * Returns a received timestamp with the given type tag. Will throw away any messages with a
+     * different type tag. If it times out, a RuntimeException is thrown.
+     */
+    public long getReceivedTimeNs(String type, long timeoutMs) {
+        ReceivedMessage message;
+        long endTimeNs = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeoutMs);
+        do {
+            long curTimeNs = System.nanoTime();
+            if (curTimeNs > endTimeNs) {
+                throw new RuntimeException("Timed out when listening for a time: " + type);
+            }
+            try {
+                Log.i(TAG, "waiting for message " + type);
+                message = mQueue.poll(endTimeNs - curTimeNs, TimeUnit.NANOSECONDS);
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+            if (message == null) {
+                throw new RuntimeException("Timed out when listening for a time: " + type);
+            }
+            Log.i(TAG, "got message " + message.mReceivedMessageType);
+            if (!type.equals(message.mReceivedMessageType)) {
+                Log.i(TAG, String.format("Expected type \"%s\", got \"%s\" (%d), skipping", type,
+                        message.mReceivedMessageType, message.mReceivedTimeNs));
+            }
+        } while (!type.equals(message.mReceivedMessageType));
+        return message.mReceivedTimeNs;
+    }
+
+    /**
+     * Clears the message queue.
+     */
+    public void clear() {
+        mQueue.clear();
+    }
+}
diff --git a/tests/ActivityManagerPerfTests/utils/Android.mk b/tests/ActivityManagerPerfTests/utils/Android.mk
new file mode 100644
index 0000000..7276e37
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/utils/Android.mk
@@ -0,0 +1,31 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under, src) \
+    src/com/android/frameworks/perftests/am/util/ITimeReceiverCallback.aidl
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    junit \
+    ub-uiautomator
+
+LOCAL_MODULE := ActivityManagerPerfTestsUtils
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Constants.java b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Constants.java
new file mode 100644
index 0000000..6528028
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Constants.java
@@ -0,0 +1,29 @@
+/*
+ * 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.frameworks.perftests.am.util;
+
+public class Constants {
+    public static final String TYPE_ACTIVITY_CREATED = "activity_create";
+    public static final String TYPE_BROADCAST_RECEIVE = "broadcast_receive";
+
+    public static final String ACTION_BROADCAST_MANIFEST_RECEIVE =
+            "com.android.frameworks.perftests.ACTION_BROADCAST_MANIFEST_RECEIVE";
+    public static final String ACTION_BROADCAST_REGISTERED_RECEIVE =
+            "com.android.frameworks.perftests.ACTION_BROADCAST_REGISTERED_RECEIVE";
+
+    public static final String EXTRA_RECEIVER_CALLBACK = "receiver_callback_binder";
+}
diff --git a/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/ITimeReceiverCallback.aidl b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/ITimeReceiverCallback.aidl
new file mode 100644
index 0000000..b43d49a
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/ITimeReceiverCallback.aidl
@@ -0,0 +1,21 @@
+/*
+ * 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.frameworks.perftests.am.util;
+
+interface ITimeReceiverCallback {
+    void sendTime(String type, long timeNs);
+}
\ No newline at end of file
diff --git a/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Utils.java b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Utils.java
new file mode 100644
index 0000000..493d8cd
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Utils.java
@@ -0,0 +1,59 @@
+/*
+ * 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.frameworks.perftests.am.util;
+
+import android.content.Intent;
+import android.os.RemoteException;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.uiautomator.UiDevice;
+import android.util.Log;
+
+import java.io.IOException;
+
+public class Utils {
+    private static final String TAG = "AmPerfTestsUtils";
+
+    public static void drainBroadcastQueue() {
+        runShellCommand("am wait-for-broadcast-idle");
+    }
+
+    /**
+     * Runs the command and returns the stdout.
+     */
+    public static String runShellCommand(String cmd) {
+        try {
+            return UiDevice.getInstance(
+                    InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Sends the current time in a message with the given type so TimeReceiver can receive it.
+     */
+    public static void sendTime(Intent intent, String type) {
+        final long time = System.nanoTime();
+        final ITimeReceiverCallback sendTimeBinder = ITimeReceiverCallback.Stub.asInterface(
+                intent.getExtras().getBinder(Constants.EXTRA_RECEIVER_CALLBACK));
+        try {
+            sendTimeBinder.sendTime(type, time);
+        } catch (RemoteException e) {
+            Log.e(TAG, e.getMessage());
+        }
+    }
+}