1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.wm;
18 
19 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
20 
21 import android.app.Activity;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.os.Bundle;
25 import android.os.Looper;
26 import android.os.RemoteCallback;
27 import android.os.RemoteException;
28 import android.os.SystemClock;
29 import android.perftests.utils.ManualBenchmarkState;
30 import android.perftests.utils.PerfManualStatusReporter;
31 import android.perftests.utils.PerfTestActivity;
32 import android.view.WindowManagerGlobal;
33 
34 import org.junit.Rule;
35 import org.junit.Test;
36 
37 /** Measure the performance of warm launch activity in the same task. */
38 public class InTaskTransitionTest extends WindowManagerPerfTestBase
39         implements RemoteCallback.OnResultListener {
40 
41     private static final long TIMEOUT_MS = 5000;
42 
43     @Rule
44     public final PerfManualStatusReporter mPerfStatusReporter = new PerfManualStatusReporter();
45 
46     private final TransitionMetricsReader mMetricsReader = new TransitionMetricsReader();
47 
48     @Test
49     @ManualBenchmarkState.ManualBenchmarkTest(
50             targetTestDurationNs = 20 * TIME_1_S_IN_NS,
51             statsReport = @ManualBenchmarkState.StatsReport(
52                     flags = ManualBenchmarkState.StatsReport.FLAG_ITERATION
53                             | ManualBenchmarkState.StatsReport.FLAG_MEAN
54                             | ManualBenchmarkState.StatsReport.FLAG_MAX))
testStartActivityInSameTask()55     public void testStartActivityInSameTask() {
56         final Context context = getInstrumentation().getContext();
57         final Activity activity = getInstrumentation().startActivitySync(
58                 new Intent(context, PerfTestActivity.class)
59                         .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
60         final Intent next = new Intent(context, TestActivity.class);
61         next.putExtra(TestActivity.CALLBACK, new RemoteCallback(this));
62 
63         final ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState();
64         long measuredTimeNs = 0;
65 
66         boolean readerStarted = false;
67         while (state.keepRunning(measuredTimeNs)) {
68             if (!readerStarted && !state.isWarmingUp()) {
69                 mMetricsReader.setCheckpoint();
70                 readerStarted = true;
71             }
72             final long startTime = SystemClock.elapsedRealtimeNanos();
73             activity.startActivity(next);
74             synchronized (mMetricsReader) {
75                 try {
76                     mMetricsReader.wait(TIMEOUT_MS);
77                 } catch (InterruptedException e) {
78                     throw new RuntimeException(e);
79                 }
80             }
81             measuredTimeNs = SystemClock.elapsedRealtimeNanos() - startTime;
82         }
83 
84         for (TransitionMetricsReader.TransitionMetrics metrics : mMetricsReader.getMetrics()) {
85             if (metrics.mTransitionDelayMs > 0) {
86                 state.addExtraResult("transitionDelayMs", metrics.mTransitionDelayMs);
87             }
88             if (metrics.mWindowsDrawnDelayMs > 0) {
89                 state.addExtraResult("windowsDrawnDelayMs", metrics.mWindowsDrawnDelayMs);
90             }
91         }
92     }
93 
94     @Override
onResult(Bundle result)95     public void onResult(Bundle result) {
96         // The test activity is destroyed.
97         synchronized (mMetricsReader) {
98             mMetricsReader.notifyAll();
99         }
100     }
101 
102     /** The test activity runs on a different process to trigger metrics logs. */
103     public static class TestActivity extends Activity implements Runnable {
104         static final String CALLBACK = "callback";
105 
106         private RemoteCallback mCallback;
107 
108         @Override
onCreate(Bundle savedInstanceState)109         protected void onCreate(Bundle savedInstanceState) {
110             super.onCreate(savedInstanceState);
111             mCallback = getIntent().getParcelableExtra(CALLBACK, RemoteCallback.class);
112             if (mCallback != null) {
113                 Looper.myLooper().getQueue().addIdleHandler(() -> {
114                     new Thread(this).start();
115                     return false;
116                 });
117             }
118         }
119 
120         @Override
run()121         public void run() {
122             // Wait until transition animation is finished and then finish self.
123             try {
124                 WindowManagerGlobal.getWindowManagerService()
125                         .syncInputTransactions(true /* waitForAnimations */);
126             } catch (RemoteException e) {
127                 e.rethrowFromSystemServer();
128             }
129             finish();
130         }
131 
132         @Override
onDestroy()133         protected void onDestroy() {
134             super.onDestroy();
135             if (mCallback != null) {
136                 getMainThreadHandler().post(() -> mCallback.sendResult(null));
137             }
138         }
139     }
140 }
141