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
This commit is contained in:
Arthur Eubanks
2017-12-18 13:46:59 -08:00
parent 8da513e8bf
commit 263d674d59
21 changed files with 1110 additions and 40 deletions

View File

@@ -25,7 +25,6 @@ import android.util.Log;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
/**
@@ -78,10 +77,7 @@ public final class BenchmarkState {
// 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 @@ public final class BenchmarkState {
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 @@ public final class BenchmarkState {
if (ENABLE_PROFILING) {
Debug.stopMethodTracing();
}
calculateSatistics();
mStats = new Stats(mResults);
mState = FINISHED;
return false;
}
@@ -224,28 +190,28 @@ public final class BenchmarkState {
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() {

View File

@@ -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);
}
}

View File

@@ -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());
}
};
}
}

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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)

View File

@@ -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>

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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)

View File

@@ -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>

View File

@@ -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>

View File

@@ -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();
}
}
}

View File

@@ -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;
});
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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)

View File

@@ -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";
}

View File

@@ -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);
}

View File

@@ -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());
}
}
}