1 /*
2  * Copyright (C) 2018 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 package com.android.tradefed.targetprep.adb;
17 
18 import com.android.annotations.VisibleForTesting;
19 import com.android.tradefed.build.IBuildInfo;
20 import com.android.tradefed.config.GlobalConfiguration;
21 import com.android.tradefed.config.Option;
22 import com.android.tradefed.config.OptionClass;
23 import com.android.tradefed.device.DeviceNotAvailableException;
24 import com.android.tradefed.device.IDeviceManager;
25 import com.android.tradefed.invoker.ExecutionFiles.FilesKey;
26 import com.android.tradefed.invoker.TestInformation;
27 import com.android.tradefed.log.LogUtil.CLog;
28 import com.android.tradefed.targetprep.BaseTargetPreparer;
29 import com.android.tradefed.targetprep.BuildError;
30 import com.android.tradefed.targetprep.SemaphoreTokenTargetPreparer;
31 import com.android.tradefed.targetprep.TargetSetupError;
32 import com.android.tradefed.util.CommandResult;
33 import com.android.tradefed.util.CommandStatus;
34 import com.android.tradefed.util.FileUtil;
35 import com.android.tradefed.util.IRunUtil;
36 import com.android.tradefed.util.RunUtil;
37 
38 import java.io.File;
39 import java.io.IOException;
40 
41 /**
42  * Target preparer to stop adb server on the host before and after running adb tests.
43  *
44  * <p>This preparer should be used with care as it stops and restart adb on the hosts. It should
45  * usually be tight with {@link SemaphoreTokenTargetPreparer} to avoid other tests from running at
46  * the same time.
47  */
48 @OptionClass(alias = "adb-stop-server-preparer")
49 public class AdbStopServerPreparer extends BaseTargetPreparer {
50 
51     public static final String ADB_BINARY_KEY = "adb_path";
52 
53     @Option(
54         name = "restart-new-adb-version",
55         description = "Whether or not to restart adb with the new version after stopping it."
56     )
57     private boolean mRestartNewVersion = true;
58 
59     private static final long CMD_TIMEOUT = 60000L;
60     private static final String ANDROID_HOST_OUT = "ANDROID_HOST_OUT";
61 
62     private IRunUtil mRunUtil;
63     private File mTmpDir;
64 
65     /** {@inheritDoc} */
66     @Override
setUp(TestInformation testInfo)67     public void setUp(TestInformation testInfo)
68             throws TargetSetupError, BuildError, DeviceNotAvailableException {
69         IBuildInfo buildInfo = testInfo.getBuildInfo();
70         getDeviceManager().stopAdbBridge();
71 
72         // Kill the default adb server
73         callAdbServerAndLog("kill-server", "Tradefed: AdbStopServerPreparer called kill-server\n");
74 
75         // Let the adb process finish
76         getRunUtil().sleep(2000);
77 
78         if (!mRestartNewVersion) {
79             CLog.d("Skipping restarting of new adb version.");
80             return;
81         }
82 
83         File adb = null;
84         if (getEnvironment(ANDROID_HOST_OUT) != null) {
85             String hostOut = getEnvironment(ANDROID_HOST_OUT);
86             adb = new File(hostOut, "bin/adb");
87             if (adb.exists()) {
88                 adb.setExecutable(true);
89             } else {
90                 adb = null;
91             }
92         }
93 
94         if (adb == null && buildInfo.getFile("adb") != null) {
95             adb = buildInfo.getFile("adb");
96             adb = renameAdbBinary(adb);
97             // Track the updated adb file.
98             testInfo.executionFiles().put(FilesKey.ADB_BINARY, adb);
99         }
100 
101         if (adb != null) {
102             CLog.d("Restarting adb from %s", adb.getAbsolutePath());
103             IRunUtil restartAdb = createRunUtil();
104             CommandResult result =
105                     restartAdb.runTimedCmd(CMD_TIMEOUT, adb.getAbsolutePath(), "start-server");
106             if (!CommandStatus.SUCCESS.equals(result.getStatus())) {
107                 throw new TargetSetupError(
108                         String.format(
109                                 "Failed to restart adb with the build info one. stdout: %s.\n"
110                                         + "sterr: %s",
111                                 result.getStdout(), result.getStderr()),
112                         testInfo.getDevice().getDeviceDescriptor());
113             }
114         } else {
115             callAdbServerAndLog(
116                     "start-server", "Tradefed: AdbStopServerPreparer called start-server\n");
117             throw new TargetSetupError(
118                     "Could not find a new version of adb to tests.",
119                     testInfo.getDevice().getDeviceDescriptor());
120         }
121     }
122 
123     /** {@inheritDoc} */
124     @Override
tearDown(TestInformation testInfo, Throwable e)125     public void tearDown(TestInformation testInfo, Throwable e) throws DeviceNotAvailableException {
126         FileUtil.recursiveDelete(mTmpDir);
127         // Kill the test adb server
128         callAdbServerAndLog("kill-server", "Tradefed: AdbStopServerPreparer called kill-server\n");
129         // Restart the one from the parent PATH (original one)
130         CommandResult restart = getRunUtil().runTimedCmd(CMD_TIMEOUT, "adb", "start-server");
131         CLog.d("Restart adb -  stdout: %s\nstderr: %s", restart.getStdout(), restart.getStderr());
132         // Restart device manager monitor
133         getDeviceManager().restartAdbBridge();
134     }
135 
136     @VisibleForTesting
getDeviceManager()137     IDeviceManager getDeviceManager() {
138         return GlobalConfiguration.getDeviceManagerInstance();
139     }
140 
141     @VisibleForTesting
createRunUtil()142     IRunUtil createRunUtil() {
143         return new RunUtil();
144     }
145 
146     @VisibleForTesting
getEnvironment(String key)147     String getEnvironment(String key) {
148         return System.getenv(key);
149     }
150 
getRunUtil()151     private IRunUtil getRunUtil() {
152         if (mRunUtil == null) {
153             mRunUtil = createRunUtil();
154         }
155         return mRunUtil;
156     }
157 
callAdbServerAndLog(String command, String message)158     private void callAdbServerAndLog(String command, String message) {
159         getRunUtil().runTimedCmd(CMD_TIMEOUT, "adb", command);
160         File adbLog = findAdbLog();
161         if (adbLog != null) {
162             try {
163                 FileUtil.writeToFile(message, adbLog, true);
164             } catch (IOException e) {
165                 CLog.e(e);
166             }
167         }
168     }
169 
renameAdbBinary(File originalAdb)170     private File renameAdbBinary(File originalAdb) {
171         try {
172             mTmpDir = FileUtil.createTempDir("adb");
173         } catch (IOException e) {
174             CLog.e("Cannot create temp directory");
175             FileUtil.recursiveDelete(mTmpDir);
176             return null;
177         }
178         File renamedAdbBinary = new File(mTmpDir, "adb");
179         if (!originalAdb.renameTo(renamedAdbBinary)) {
180             CLog.e("Cannot rename adb binary");
181             return null;
182         }
183         if (!renamedAdbBinary.setExecutable(true)) {
184             CLog.e("Cannot set adb binary executable");
185             return null;
186         }
187         return renamedAdbBinary;
188     }
189 
190     @VisibleForTesting
findAdbLog()191     protected File findAdbLog() {
192         String tmpDir = "/tmp";
193         if (System.getenv("TMPDIR") != null) {
194             tmpDir = System.getenv("TMPDIR");
195         }
196         CommandResult uidRes =
197                 RunUtil.getDefault()
198                         .runTimedCmd(60000, "id", "-u", System.getProperty("user.name"));
199         if (!CommandStatus.SUCCESS.equals(uidRes.getStatus())) {
200             CLog.e("Failed to collect UID for adb logs: %s", uidRes.getStderr());
201             return null;
202         }
203         String uid = uidRes.getStdout().trim();
204         File adbLog = new File(tmpDir, String.format("adb.%s.log", uid));
205         if (!adbLog.exists()) {
206             CLog.i("Did not find adb log file: %s, upload skipped.", adbLog);
207             return null;
208         }
209         return adbLog;
210     }
211 }
212