Merge "Add transition latency test for activity switch in the same task"
This commit is contained in:
@@ -17,6 +17,10 @@
|
||||
package="com.android.perftests.wm">
|
||||
|
||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.READ_LOGS" />
|
||||
<!-- For perfetto trace files -->
|
||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<application>
|
||||
<uses-library android:name="android.test.runner" />
|
||||
@@ -26,6 +30,9 @@
|
||||
<action android:name="com.android.perftests.core.PERFTEST" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="android.wm.InTaskTransitionTest$TestActivity"
|
||||
android:process=":test" />
|
||||
</application>
|
||||
|
||||
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.wm;
|
||||
|
||||
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Looper;
|
||||
import android.os.RemoteCallback;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
import android.perftests.utils.ManualBenchmarkState;
|
||||
import android.perftests.utils.PerfManualStatusReporter;
|
||||
import android.perftests.utils.PerfTestActivity;
|
||||
import android.view.WindowManagerGlobal;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
/** Measure the performance of warm launch activity in the same task. */
|
||||
public class InTaskTransitionTest extends WindowManagerPerfTestBase
|
||||
implements RemoteCallback.OnResultListener {
|
||||
|
||||
private static final long TIMEOUT_MS = 5000;
|
||||
|
||||
@Rule
|
||||
public final PerfManualStatusReporter mPerfStatusReporter = new PerfManualStatusReporter();
|
||||
|
||||
private final TransitionMetricsReader mMetricsReader = new TransitionMetricsReader();
|
||||
|
||||
@Test
|
||||
@ManualBenchmarkState.ManualBenchmarkTest(
|
||||
targetTestDurationNs = 20 * TIME_1_S_IN_NS,
|
||||
statsReport = @ManualBenchmarkState.StatsReport(
|
||||
flags = ManualBenchmarkState.StatsReport.FLAG_ITERATION
|
||||
| ManualBenchmarkState.StatsReport.FLAG_MEAN
|
||||
| ManualBenchmarkState.StatsReport.FLAG_MAX))
|
||||
public void testStartActivityInSameTask() {
|
||||
final Context context = getInstrumentation().getContext();
|
||||
final Activity activity = getInstrumentation().startActivitySync(
|
||||
new Intent(context, PerfTestActivity.class)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
|
||||
final Intent next = new Intent(context, TestActivity.class);
|
||||
next.putExtra(TestActivity.CALLBACK, new RemoteCallback(this));
|
||||
|
||||
final ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
long measuredTimeNs = 0;
|
||||
|
||||
boolean readerStarted = false;
|
||||
while (state.keepRunning(measuredTimeNs)) {
|
||||
if (!readerStarted && !state.isWarmingUp()) {
|
||||
mMetricsReader.setCheckpoint();
|
||||
readerStarted = true;
|
||||
}
|
||||
final long startTime = SystemClock.elapsedRealtimeNanos();
|
||||
activity.startActivity(next);
|
||||
synchronized (mMetricsReader) {
|
||||
try {
|
||||
mMetricsReader.wait(TIMEOUT_MS);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
measuredTimeNs = SystemClock.elapsedRealtimeNanos() - startTime;
|
||||
}
|
||||
|
||||
for (TransitionMetricsReader.TransitionMetrics metrics : mMetricsReader.getMetrics()) {
|
||||
if (metrics.mTransitionDelayMs > 0) {
|
||||
state.addExtraResult("transitionDelayMs", metrics.mTransitionDelayMs);
|
||||
}
|
||||
if (metrics.mWindowsDrawnDelayMs > 0) {
|
||||
state.addExtraResult("windowsDrawnDelayMs", metrics.mWindowsDrawnDelayMs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResult(Bundle result) {
|
||||
// The test activity is destroyed.
|
||||
synchronized (mMetricsReader) {
|
||||
mMetricsReader.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
/** The test activity runs on a different process to trigger metrics logs. */
|
||||
public static class TestActivity extends Activity implements Runnable {
|
||||
static final String CALLBACK = "callback";
|
||||
|
||||
private RemoteCallback mCallback;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mCallback = getIntent().getParcelableExtra(CALLBACK, RemoteCallback.class);
|
||||
if (mCallback != null) {
|
||||
Looper.myLooper().getQueue().addIdleHandler(() -> {
|
||||
new Thread(this).start();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// Wait until transition animation is finished and then finish self.
|
||||
try {
|
||||
WindowManagerGlobal.getWindowManagerService()
|
||||
.syncInputTransactions(true /* waitForAnimations */);
|
||||
} catch (RemoteException e) {
|
||||
e.rethrowFromSystemServer();
|
||||
}
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (mCallback != null) {
|
||||
getMainThreadHandler().post(() -> mCallback.sendResult(null));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,10 +18,17 @@ package android.wm;
|
||||
|
||||
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||
|
||||
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION;
|
||||
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_DELAY_MS;
|
||||
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.metrics.LogMaker;
|
||||
import android.metrics.MetricsReader;
|
||||
import android.perftests.utils.PerfTestActivity;
|
||||
import android.perftests.utils.WindowPerfTestBase;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import androidx.test.runner.lifecycle.ActivityLifecycleCallback;
|
||||
import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
|
||||
@@ -31,6 +38,7 @@ import org.junit.runner.Description;
|
||||
import org.junit.runners.model.Statement;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class WindowManagerPerfTestBase extends WindowPerfTestBase {
|
||||
@@ -124,4 +132,42 @@ public class WindowManagerPerfTestBase extends WindowPerfTestBase {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class TransitionMetricsReader {
|
||||
final MetricsReader mMetricsReader = new MetricsReader();
|
||||
|
||||
static class TransitionMetrics {
|
||||
int mTransitionDelayMs;
|
||||
int mWindowsDrawnDelayMs;
|
||||
}
|
||||
|
||||
TransitionMetrics[] getMetrics() {
|
||||
mMetricsReader.read(0);
|
||||
final ArrayList<LogMaker> logs = new ArrayList<>();
|
||||
final LogMaker logTemplate = new LogMaker(APP_TRANSITION);
|
||||
while (mMetricsReader.hasNext()) {
|
||||
final LogMaker b = mMetricsReader.next();
|
||||
if (logTemplate.isSubsetOf(b)) {
|
||||
logs.add(b);
|
||||
}
|
||||
}
|
||||
|
||||
final TransitionMetrics[] infoArray = new TransitionMetrics[logs.size()];
|
||||
for (int i = 0; i < infoArray.length; i++) {
|
||||
final LogMaker log = logs.get(i);
|
||||
final SparseArray<Object> data = log.getEntries();
|
||||
final TransitionMetrics info = new TransitionMetrics();
|
||||
infoArray[i] = info;
|
||||
info.mTransitionDelayMs =
|
||||
(int) data.get(APP_TRANSITION_DELAY_MS, -1);
|
||||
info.mWindowsDrawnDelayMs =
|
||||
(int) data.get(APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS, -1);
|
||||
}
|
||||
return infoArray;
|
||||
}
|
||||
|
||||
void setCheckpoint() {
|
||||
mMetricsReader.checkpoint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user