blob: 9e17e940a06bf6a4cb1314f4ec325b5a81e2b06b [file] [log] [blame]
Riddle Hsu54a86c62019-07-10 21:37:08 +08001/*
2 * Copyright (C) 2019 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
17package android.wm;
18
19import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
20
Riddle Hsuf5622d22019-09-24 17:56:05 -060021import android.app.Activity;
Riddle Hsu54a86c62019-07-10 21:37:08 +080022import android.app.UiAutomation;
Riddle Hsu64d8bcc2020-02-06 21:39:27 +080023import android.content.Context;
Riddle Hsuf5622d22019-09-24 17:56:05 -060024import android.content.Intent;
Riddle Hsu64d8bcc2020-02-06 21:39:27 +080025import android.os.BatteryManager;
Riddle Hsu4a725002019-11-11 20:50:13 +080026import android.os.ParcelFileDescriptor;
Riddle Hsuf5622d22019-09-24 17:56:05 -060027import android.perftests.utils.PerfTestActivity;
Riddle Hsu64d8bcc2020-02-06 21:39:27 +080028import android.provider.Settings;
Riddle Hsu54a86c62019-07-10 21:37:08 +080029
Riddle Hsuf5622d22019-09-24 17:56:05 -060030import androidx.test.rule.ActivityTestRule;
31import androidx.test.runner.lifecycle.ActivityLifecycleCallback;
32import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
33import androidx.test.runner.lifecycle.Stage;
34
Riddle Hsu64d8bcc2020-02-06 21:39:27 +080035import org.junit.AfterClass;
Riddle Hsuf5622d22019-09-24 17:56:05 -060036import org.junit.BeforeClass;
37import org.junit.runner.Description;
38import org.junit.runners.model.Statement;
39
Riddle Hsu4a725002019-11-11 20:50:13 +080040import java.io.ByteArrayOutputStream;
41import java.io.File;
42import java.io.FileInputStream;
43import java.io.IOException;
Riddle Hsuf5622d22019-09-24 17:56:05 -060044import java.util.concurrent.TimeUnit;
Riddle Hsu54a86c62019-07-10 21:37:08 +080045
46public class WindowManagerPerfTestBase {
47 static final UiAutomation sUiAutomation = getInstrumentation().getUiAutomation();
48 static final long NANOS_PER_S = 1000L * 1000 * 1000;
Riddle Hsu5ef56dd62019-07-26 21:28:51 -060049 static final long TIME_1_S_IN_NS = 1 * NANOS_PER_S;
50 static final long TIME_5_S_IN_NS = 5 * NANOS_PER_S;
Riddle Hsu54a86c62019-07-10 21:37:08 +080051
Riddle Hsu4a725002019-11-11 20:50:13 +080052 /**
53 * The out directory matching the directory-keys of collector in AndroidTest.xml. The directory
54 * is in /data because while enabling method profling of system server, it cannot write the
55 * trace to external storage.
56 */
57 static final File BASE_OUT_PATH = new File("/data/local/CorePerfTests");
58
Riddle Hsu64d8bcc2020-02-06 21:39:27 +080059 private static int sOriginalStayOnWhilePluggedIn;
60
Riddle Hsuf5622d22019-09-24 17:56:05 -060061 @BeforeClass
62 public static void setUpOnce() {
Riddle Hsu64d8bcc2020-02-06 21:39:27 +080063 final Context context = getInstrumentation().getContext();
64 sOriginalStayOnWhilePluggedIn = Settings.Global.getInt(context.getContentResolver(),
65 Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
66 // Keep the device awake during testing.
67 setStayOnWhilePluggedIn(BatteryManager.BATTERY_PLUGGED_USB);
68
Riddle Hsu4a725002019-11-11 20:50:13 +080069 if (!BASE_OUT_PATH.exists()) {
70 executeShellCommand("mkdir -p " + BASE_OUT_PATH);
71 }
Riddle Hsu54a86c62019-07-10 21:37:08 +080072 // In order to be closer to the real use case.
Riddle Hsu4a725002019-11-11 20:50:13 +080073 executeShellCommand("input keyevent KEYCODE_WAKEUP");
74 executeShellCommand("wm dismiss-keyguard");
Riddle Hsu64d8bcc2020-02-06 21:39:27 +080075 context.startActivity(new Intent(Intent.ACTION_MAIN)
Riddle Hsuf5622d22019-09-24 17:56:05 -060076 .addCategory(Intent.CATEGORY_HOME).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
77 }
78
Riddle Hsu64d8bcc2020-02-06 21:39:27 +080079 @AfterClass
80 public static void tearDownOnce() {
81 setStayOnWhilePluggedIn(sOriginalStayOnWhilePluggedIn);
82 }
83
84 private static void setStayOnWhilePluggedIn(int value) {
85 executeShellCommand(String.format("settings put global %s %d",
86 Settings.Global.STAY_ON_WHILE_PLUGGED_IN, value));
87 }
88
Riddle Hsuf5622d22019-09-24 17:56:05 -060089 /**
Riddle Hsu4a725002019-11-11 20:50:13 +080090 * Executes shell command with reading the output. It may also used to block until the current
91 * command is completed.
92 */
93 static ByteArrayOutputStream executeShellCommand(String command) {
94 final ParcelFileDescriptor pfd = sUiAutomation.executeShellCommand(command);
95 final byte[] buf = new byte[512];
96 final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
97 int bytesRead;
98 try (FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
99 while ((bytesRead = fis.read(buf)) != -1) {
100 bytes.write(buf, 0, bytesRead);
101 }
102 } catch (IOException e) {
103 throw new RuntimeException(e);
104 }
105 return bytes;
106 }
107
108 /** Starts method tracing on system server. */
109 void startProfiling(String subPath) {
110 executeShellCommand("am profile start system " + new File(BASE_OUT_PATH, subPath));
111 }
112
113 void stopProfiling() {
114 executeShellCommand("am profile stop system");
115 }
116
117 /**
Riddle Hsuf5622d22019-09-24 17:56:05 -0600118 * Provides an activity that keeps screen on and is able to wait for a stable lifecycle stage.
119 */
120 static class PerfTestActivityRule extends ActivityTestRule<PerfTestActivity> {
121 private final Intent mStartIntent =
Riddle Hsu64d8bcc2020-02-06 21:39:27 +0800122 new Intent(getInstrumentation().getTargetContext(), PerfTestActivity.class);
Riddle Hsuf5622d22019-09-24 17:56:05 -0600123 private final LifecycleListener mLifecycleListener = new LifecycleListener();
124
125 PerfTestActivityRule() {
126 this(false /* launchActivity */);
127 }
128
129 PerfTestActivityRule(boolean launchActivity) {
130 super(PerfTestActivity.class, false /* initialTouchMode */, launchActivity);
131 }
132
133 @Override
134 public Statement apply(Statement base, Description description) {
135 final Statement wrappedStatement = new Statement() {
136 @Override
137 public void evaluate() throws Throwable {
138 ActivityLifecycleMonitorRegistry.getInstance()
139 .addLifecycleCallback(mLifecycleListener);
140 base.evaluate();
141 ActivityLifecycleMonitorRegistry.getInstance()
142 .removeLifecycleCallback(mLifecycleListener);
143 }
144 };
145 return super.apply(wrappedStatement, description);
146 }
147
148 @Override
149 protected Intent getActivityIntent() {
150 return mStartIntent;
151 }
152
153 @Override
154 public PerfTestActivity launchActivity(Intent intent) {
155 final PerfTestActivity activity = super.launchActivity(intent);
156 mLifecycleListener.setTargetActivity(activity);
157 return activity;
158 }
159
160 PerfTestActivity launchActivity() {
161 return launchActivity(mStartIntent);
162 }
163
164 void waitForIdleSync(Stage state) {
165 mLifecycleListener.waitForIdleSync(state);
166 }
167 }
168
169 static class LifecycleListener implements ActivityLifecycleCallback {
170 private Activity mTargetActivity;
171 private Stage mWaitingStage;
172 private Stage mReceivedStage;
173
174 void setTargetActivity(Activity activity) {
175 mTargetActivity = activity;
176 mReceivedStage = mWaitingStage = null;
177 }
178
179 void waitForIdleSync(Stage stage) {
180 synchronized (this) {
181 if (stage != mReceivedStage) {
182 mWaitingStage = stage;
183 try {
184 wait(TimeUnit.NANOSECONDS.toMillis(TIME_5_S_IN_NS));
185 } catch (InterruptedException impossible) { }
186 }
187 mWaitingStage = mReceivedStage = null;
188 }
189 getInstrumentation().waitForIdleSync();
190 }
191
192 @Override
193 public void onActivityLifecycleChanged(Activity activity, Stage stage) {
194 if (mTargetActivity != activity) {
195 return;
196 }
197
198 synchronized (this) {
199 mReceivedStage = stage;
200 if (mWaitingStage == mReceivedStage) {
201 notifyAll();
202 }
203 }
204 }
Riddle Hsu54a86c62019-07-10 21:37:08 +0800205 }
206}