diff options
21 files changed, 1110 insertions, 40 deletions
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 bb9dc4ae562e..da17818bbda0 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 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() { 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 000000000000..2c84db18ce54 --- /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 000000000000..0de6f1d944ff --- /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 000000000000..acc44a8febfc --- /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 000000000000..77e0e90623fa --- /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 000000000000..b0a5db7a3134 --- /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 000000000000..71451101ac59 --- /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 000000000000..7ea9ba3d05ee --- /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 000000000000..e26ffcde7c07 --- /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 000000000000..336bf9d1fab6 --- /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 000000000000..daf603d6a63f --- /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 000000000000..4e194c66354e --- /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 000000000000..ffb5404d7d94 --- /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 000000000000..661abe91337f --- /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 000000000000..795f49866f84 --- /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 000000000000..c86714156c2b --- /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 000000000000..9cf6ee7c91d5 --- /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 000000000000..7276e37afc7a --- /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 000000000000..6528028ee1a1 --- /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 000000000000..b43d49a1b8e4 --- /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 000000000000..493d8cdd0803 --- /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()); + } + } +} |