1 /*
2  * Copyright (C) 2021 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 com.android.performance.tests;
18 
19 import com.android.tradefed.device.DeviceNotAvailableException;
20 import com.android.tradefed.device.IManagedTestDevice;
21 import com.android.tradefed.device.ITestDevice;
22 import com.android.tradefed.invoker.TestInformation;
23 import com.android.tradefed.log.LogUtil;
24 import com.android.tradefed.result.ITestInvocationListener;
25 import com.android.tradefed.targetprep.BaseEmulatorPreparer;
26 import com.android.tradefed.testtype.AndroidJUnitTest;
27 import com.android.tradefed.util.RunUtil;
28 
29 import java.io.IOException;
30 import java.nio.file.Path;
31 import java.util.List;
32 
33 /** A performance test that does repeated emulator launches and measures timings. */
34 public class EmulatorStartupPerfTest extends BaseEmulatorPerfTest {
35 
36     public static class EmulatorLauncher extends BaseEmulatorPreparer {
37 
38         /**
39          * Launches emulator with args provided via Configuration.
40          *
41          * <p>We want to launch emulator directly rather than going through
42          * DeviceManager#launchEmulator in order to measure timing accurately: DeviceManager
43          * performs several time consuming steps before returning
44          */
launchEmulator(ITestDevice device)45         public void launchEmulator(ITestDevice device) throws IOException {
46             List<String> args = buildEmulatorLaunchArgs();
47 
48             args.add("-read-only");
49             String port = device.getSerialNumber().replace("emulator-", "");
50             args.add("-port");
51             args.add(port);
52 
53             RunUtil runUtil = buildRunUtilForEmulatorLaunch();
54 
55             Process p = runUtil.runCmdInBackground(args);
56             ((IManagedTestDevice) device).setEmulatorProcess(p);
57         }
58     }
59 
60     @Override
performIteration( TestInformation testInfo, BaseEmulatorPreparer baseEmulatorPreparer, AndroidJUnitTest delegateTest, Path apkPath, DataRecorder timingsRecorder, ITestInvocationListener listener)61     protected void performIteration(
62             TestInformation testInfo,
63             BaseEmulatorPreparer baseEmulatorPreparer,
64             AndroidJUnitTest delegateTest,
65             Path apkPath,
66             DataRecorder timingsRecorder,
67             ITestInvocationListener listener)
68             throws Exception {
69 
70         EmulatorLauncher emulatorLauncher = (EmulatorLauncher) baseEmulatorPreparer;
71 
72         ITestDevice device = testInfo.getDevice();
73         long startTimeMs = System.currentTimeMillis();
74         timingsRecorder.captureTime(
75                 "online_time",
76                 () -> {
77                     emulatorLauncher.launchEmulator(device);
78                     device.waitForDeviceOnline(1 * 60 * 1000);
79                     return null;
80                 });
81 
82         timingsRecorder.captureTime(
83                 "boot_time",
84                 () -> {
85                     waitForBootComplete(device, startTimeMs + 3 * 60 * 1000);
86                     return null;
87                 });
88 
89         timingsRecorder.captureTime(
90                 "install_time",
91                 () -> {
92                     // direcly install package instead of using
93                     // ITestDevice.installPackage
94                     // to avoid overhead of the expensive aapt parsing checks it
95                     // does every time
96                     String result = device.executeAdbCommand("install", apkPath.toString());
97                     LogUtil.CLog.i("Install returned " + result);
98                     return null;
99                 });
100 
101         timingsRecorder.captureTime(
102                 "test_time",
103                 () -> {
104                     delegateTest.run(testInfo, listener);
105                     return null;
106                 });
107 
108         timingsRecorder.recordMetric("total_time", System.currentTimeMillis() - startTimeMs);
109 
110         LogUtil.CLog.i("Metrics: %s", timingsRecorder.toString());
111     }
112 
waitForBootComplete(ITestDevice device, long quitAfterTime)113     private void waitForBootComplete(ITestDevice device, long quitAfterTime)
114             throws DeviceNotAvailableException {
115         // we don't want to use  waitForDeviceAvailable, as that has a 3 second sleep
116         // so directly query for boot complete
117         while (System.currentTimeMillis() < quitAfterTime) {
118             String result = device.executeShellCommand("getprop dev.bootcomplete");
119             if (result.trim().equals("1")) {
120                 return;
121             }
122             RunUtil.getDefault().sleep(50);
123         }
124     }
125 }
126