• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 package com.android.tradefed.device.cloud;
17 
18 import com.android.ddmlib.IDevice;
19 import com.android.tradefed.build.IBuildInfo;
20 import com.android.tradefed.device.DeviceNotAvailableException;
21 import com.android.tradefed.device.IDeviceMonitor;
22 import com.android.tradefed.device.IDeviceStateMonitor;
23 import com.android.tradefed.device.TestDevice;
24 import com.android.tradefed.device.cloud.CommonLogRemoteFileUtil.KnownLogFileEntry;
25 import com.android.tradefed.invoker.RemoteInvocationExecution;
26 import com.android.tradefed.log.ITestLogger;
27 import com.android.tradefed.log.LogUtil.CLog;
28 import com.android.tradefed.result.FileInputStreamSource;
29 import com.android.tradefed.result.InputStreamSource;
30 import com.android.tradefed.result.LogDataType;
31 import com.android.tradefed.targetprep.TargetSetupError;
32 import com.android.tradefed.util.CommandResult;
33 import com.android.tradefed.util.CommandStatus;
34 
35 import com.google.common.base.Joiner;
36 
37 import java.io.File;
38 import java.text.SimpleDateFormat;
39 import java.util.Date;
40 import java.util.HashMap;
41 import java.util.List;
42 import java.util.Map;
43 
44 /**
45  * Representation of the device running inside a remote Cuttlefish VM. It will alter the local
46  * device {@link TestDevice} behavior in some cases to take advantage of the setup.
47  */
48 public class NestedRemoteDevice extends TestDevice {
49 
50     // TODO: Improve the way we associate nested device with their user
51     private static final Map<String, String> IP_TO_USER = new HashMap<>();
52 
53     static {
54         IP_TO_USER.put("127.0.0.1:6520", "vsoc-01");
55         IP_TO_USER.put("127.0.0.1:6521", "vsoc-02");
56         IP_TO_USER.put("127.0.0.1:6522", "vsoc-03");
57         IP_TO_USER.put("127.0.0.1:6523", "vsoc-04");
58         IP_TO_USER.put("127.0.0.1:6524", "vsoc-05");
59         IP_TO_USER.put("127.0.0.1:6525", "vsoc-06");
60         IP_TO_USER.put("127.0.0.1:6526", "vsoc-07");
61         IP_TO_USER.put("127.0.0.1:6527", "vsoc-08");
62         IP_TO_USER.put("127.0.0.1:6528", "vsoc-09");
63         IP_TO_USER.put("127.0.0.1:6529", "vsoc-10");
64         IP_TO_USER.put("127.0.0.1:6530", "vsoc-11");
65         IP_TO_USER.put("127.0.0.1:6531", "vsoc-12");
66         IP_TO_USER.put("127.0.0.1:6532", "vsoc-13");
67         IP_TO_USER.put("127.0.0.1:6533", "vsoc-14");
68         IP_TO_USER.put("127.0.0.1:6534", "vsoc-15");
69     }
70 
71     /** When calling launch_cvd, the launcher.log is populated. */
72     private static final String LAUNCHER_LOG_PATH = "/home/%s/cuttlefish_runtime/launcher.log";
73 
74     /**
75      * Creates a {@link NestedRemoteDevice}.
76      *
77      * @param device the associated {@link IDevice}
78      * @param stateMonitor the {@link IDeviceStateMonitor} mechanism to use
79      * @param allocationMonitor the {@link IDeviceMonitor} to inform of allocation state changes.
80      */
NestedRemoteDevice( IDevice device, IDeviceStateMonitor stateMonitor, IDeviceMonitor allocationMonitor)81     public NestedRemoteDevice(
82             IDevice device, IDeviceStateMonitor stateMonitor, IDeviceMonitor allocationMonitor) {
83         super(device, stateMonitor, allocationMonitor);
84         // TODO: Use IDevice directly
85         if (stateMonitor instanceof NestedDeviceStateMonitor) {
86             ((NestedDeviceStateMonitor) stateMonitor).setDevice(this);
87         }
88     }
89 
90     /** Teardown and restore the virtual device so testing can proceed. */
resetVirtualDevice( ITestLogger logger, IBuildInfo info, boolean resetDueToFailure)91     public final boolean resetVirtualDevice(
92             ITestLogger logger, IBuildInfo info, boolean resetDueToFailure)
93             throws DeviceNotAvailableException {
94         String username = IP_TO_USER.get(getSerialNumber());
95         // stop_cvd
96         String stopCvdCommand = String.format("sudo runuser -l %s -c 'stop_cvd'", username);
97         CommandResult stopCvdRes = getRunUtil().runTimedCmd(60000L, stopCvdCommand.split(" "));
98         if (!CommandStatus.SUCCESS.equals(stopCvdRes.getStatus())) {
99             CLog.e("%s", stopCvdRes.getStderr());
100             // Log 'adb devices' to confirm device is gone
101             CommandResult printAdbDevices = getRunUtil().runTimedCmd(60000L, "adb", "devices");
102             CLog.e("%s\n%s", printAdbDevices.getStdout(), printAdbDevices.getStderr());
103             // Proceed here, device could have been already gone.
104         }
105         // Synchronize this so multiple reset do not occur at the same time inside one VM.
106         synchronized (NestedRemoteDevice.class) {
107             if (resetDueToFailure) {
108                 // Log the common files before restarting otherwise they are lost
109                 logDebugFiles(logger, username);
110             }
111             // Restart the device without re-creating the data partitions.
112             List<String> createCommand =
113                     LaunchCvdHelper.createSimpleDeviceCommand(username, true, false, false);
114             CommandResult createRes =
115                     getRunUtil()
116                             .runTimedCmd(
117                                     RemoteInvocationExecution.LAUNCH_EXTRA_DEVICE,
118                                     "sh",
119                                     "-c",
120                                     Joiner.on(" ").join(createCommand));
121             if (!CommandStatus.SUCCESS.equals(createRes.getStatus())) {
122                 CLog.e("%s", createRes.getStderr());
123                 captureLauncherLog(username, logger);
124                 return false;
125             }
126             // Wait for the device to start for real.
127             getRunUtil().sleep(5000);
128             waitForDeviceAvailable();
129             // Re-init the freshly started device.
130             return reInitDevice(info);
131         }
132     }
133 
134     /**
135      * Log the runtime files of the virtual device before resetting it since they will be deleted.
136      */
logDebugFiles(ITestLogger logger, String username)137     private void logDebugFiles(ITestLogger logger, String username) {
138         List<KnownLogFileEntry> toFetch =
139                 CommonLogRemoteFileUtil.KNOWN_FILES_TO_FETCH.get(getOptions().getInstanceType());
140         if (toFetch != null) {
141             SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss");
142             for (KnownLogFileEntry entry : toFetch) {
143                 File toLog = new File(String.format(entry.path, username));
144                 if (!toLog.exists()) {
145                     continue;
146                 }
147                 try (FileInputStreamSource source = new FileInputStreamSource(toLog)) {
148                     logger.testLog(
149                             String.format(
150                                     "before_reset_%s_%s_%s",
151                                     toLog.getName(), username, formatter.format(new Date())),
152                             entry.type,
153                             source);
154                 }
155             }
156         }
157         logBugreport(String.format("before_reset_%s_bugreport", username), logger);
158     }
159 
160     /** TODO: Re-run the target_preparation. */
reInitDevice(IBuildInfo info)161     private boolean reInitDevice(IBuildInfo info) throws DeviceNotAvailableException {
162         // Reset recovery since it's a new device
163         setRecoveryMode(RecoveryMode.AVAILABLE);
164         try {
165             preInvocationSetup(info);
166         } catch (TargetSetupError e) {
167             CLog.e("Failed to re-init the device %s", getSerialNumber());
168             CLog.e(e);
169             return false;
170         }
171         // Success
172         return true;
173     }
174 
175     /** Capture and log the launcher.log to debug why the device didn't start properly. */
captureLauncherLog(String username, ITestLogger logger)176     private void captureLauncherLog(String username, ITestLogger logger) {
177         String logName = String.format("launcher_log_failure_%s", username);
178         File launcherLog = new File(String.format(LAUNCHER_LOG_PATH, username));
179         if (!launcherLog.exists()) {
180             CLog.e("%s doesn't exists, skip logging it.", launcherLog.getAbsolutePath());
181             return;
182         }
183         try (InputStreamSource source = new FileInputStreamSource(launcherLog)) {
184             logger.testLog(logName, LogDataType.TEXT, source);
185         }
186     }
187 }
188