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 package com.android.tradefed.device.internal;
17 
18 import com.android.tradefed.config.IConfiguration;
19 import com.android.tradefed.config.IConfigurationReceiver;
20 import com.android.tradefed.config.IDeviceConfiguration;
21 import com.android.tradefed.device.DeviceNotAvailableException;
22 import com.android.tradefed.device.ITestDevice;
23 import com.android.tradefed.device.RemoteAndroidDevice;
24 import com.android.tradefed.device.cloud.GceAvdInfo;
25 import com.android.tradefed.device.cloud.NestedRemoteDevice;
26 import com.android.tradefed.device.cloud.RemoteAndroidVirtualDevice;
27 import com.android.tradefed.device.connection.AbstractConnection;
28 import com.android.tradefed.device.connection.AdbSshConnection;
29 import com.android.tradefed.invoker.TestInformation;
30 import com.android.tradefed.result.error.DeviceErrorIdentifier;
31 import com.android.tradefed.service.IRemoteFeature;
32 import com.android.tradefed.targetprep.ITargetPreparer;
33 import com.android.tradefed.targetprep.TargetSetupError;
34 import com.android.tradefed.testtype.ITestInformationReceiver;
35 import com.android.tradefed.util.CommandResult;
36 import com.android.tradefed.util.CommandStatus;
37 import com.android.tradefed.util.SerializationUtil;
38 
39 import com.proto.tradefed.feature.ErrorInfo;
40 import com.proto.tradefed.feature.FeatureRequest;
41 import com.proto.tradefed.feature.FeatureResponse;
42 
43 import java.io.IOException;
44 
45 /**
46  * Server side implementation of device reset.
47  */
48 public class DeviceResetFeature implements IRemoteFeature, IConfigurationReceiver, ITestInformationReceiver {
49 
50     public static final String DEVICE_RESET_FEATURE_NAME = "resetDevice";
51     public static final String DEVICE_NAME = "device_name";
52 
53     private IConfiguration mConfig;
54     private TestInformation mTestInformation;
55 
56     @Override
getName()57     public String getName() {
58         return DEVICE_RESET_FEATURE_NAME;
59     }
60 
61     @Override
setConfiguration(IConfiguration configuration)62     public void setConfiguration(IConfiguration configuration) {
63         mConfig = configuration;
64     }
65 
66     @Override
setTestInformation(TestInformation testInformation)67     public void setTestInformation(TestInformation testInformation) {
68         mTestInformation = testInformation;
69     }
70 
71     @Override
getTestInformation()72     public TestInformation getTestInformation() {
73         return mTestInformation;
74     }
75 
76     @Override
execute(FeatureRequest request)77     public FeatureResponse execute(FeatureRequest request) {
78         FeatureResponse.Builder responseBuilder = FeatureResponse.newBuilder();
79         String deviceName = request.getArgsMap().get(DEVICE_NAME);
80         if (deviceName == null) {
81             responseBuilder.setErrorInfo(
82                     ErrorInfo.newBuilder().setErrorTrace("No device_name args specified."));
83             return responseBuilder.build();
84         }
85         IDeviceConfiguration configHolder = mConfig.getDeviceConfigByName(deviceName);
86         int index = 0;
87         for (IDeviceConfiguration deviceConfig : mConfig.getDeviceConfig()) {
88             if (deviceConfig == configHolder) {
89                 break;
90             }
91             index++;
92         }
93         mTestInformation.setActiveDeviceIndex(index);
94         String response =
95                 String.format(
96                         "Attempting device reset on %s (%s).",
97                         mTestInformation.getDevice().getSerialNumber(),
98                         mTestInformation.getDevice().getClass().getSimpleName());
99         try {
100             AbstractConnection connection = mTestInformation.getDevice().getConnection();
101             if ((mTestInformation.getDevice() instanceof RemoteAndroidVirtualDevice)
102                     || (connection instanceof AdbSshConnection)) {
103                 GceAvdInfo info = getAvdInfo(mTestInformation.getDevice(), connection);
104                 if (info == null) {
105                     throw new RuntimeException("GceAvdInfo was null. skipping");
106                 }
107                 Integer offset = info.getDeviceOffset();
108                 String user = info.getInstanceUser();
109                 long startTime = System.currentTimeMillis();
110                 CommandResult powerwashResult = powerwash(connection, user, offset);
111                 if (!CommandStatus.SUCCESS.equals(powerwashResult.getStatus())) {
112                     throw new DeviceNotAvailableException(
113                             String.format(
114                                     "Failed to powerwash device: %s. status:%s\n"
115                                             + "stdout: %s\n"
116                                             + "stderr:%s",
117                                     mTestInformation.getDevice().getSerialNumber(),
118                                     powerwashResult.getStatus(),
119                                     powerwashResult.getStdout(),
120                                     powerwashResult.getStderr()),
121                             mTestInformation.getDevice().getSerialNumber(),
122                             DeviceErrorIdentifier.DEVICE_FAILED_TO_RESET);
123                 }
124                 response +=
125                         String.format(
126                                 " Powerwash finished in %d ms.",
127                                 System.currentTimeMillis() - startTime);
128             } else if (mTestInformation.getDevice() instanceof RemoteAndroidDevice) {
129                 response += " RemoteAndroidDevice has no powerwash support.";
130             } else if (mTestInformation.getDevice() instanceof NestedRemoteDevice) {
131                 boolean res =
132                         ((NestedRemoteDevice) mTestInformation.getDevice()).resetVirtualDevice();
133                 if (!res) {
134                     throw new DeviceNotAvailableException(
135                             String.format(
136                                     "Failed to powerwash device: %s",
137                                     mTestInformation.getDevice().getSerialNumber()),
138                             mTestInformation.getDevice().getSerialNumber(),
139                             DeviceErrorIdentifier.DEVICE_FAILED_TO_RESET);
140                 }
141             }
142             for (ITargetPreparer labPreparer : configHolder.getLabPreparers()) {
143                 if (labPreparer.isDisabled()) {
144                     continue;
145                 }
146                 labPreparer.setUp(mTestInformation);
147             }
148             for (ITargetPreparer preparer : configHolder.getTargetPreparers()) {
149                 if (preparer.isDisabled()) {
150                     continue;
151                 }
152                 preparer.setUp(mTestInformation);
153             }
154         } catch (Exception e) {
155             String error = "Failed to setup after reset device.";
156             try {
157                 error = SerializationUtil.serializeToString(e);
158             } catch (RuntimeException | IOException serializationError) {
159                 // Ignore
160             }
161             responseBuilder.setErrorInfo(ErrorInfo.newBuilder().setErrorTrace(error));
162         } finally {
163             mTestInformation.setActiveDeviceIndex(0);
164         }
165         responseBuilder.setResponse(response);
166         return responseBuilder.build();
167     }
168 
getAvdInfo(ITestDevice device, AbstractConnection connection)169     private GceAvdInfo getAvdInfo(ITestDevice device, AbstractConnection connection) {
170         if (connection instanceof AdbSshConnection) {
171             return ((AdbSshConnection) connection).getAvdInfo();
172         }
173         return null;
174     }
175 
powerwash(AbstractConnection connection, String user, Integer offset)176     private CommandResult powerwash(AbstractConnection connection, String user, Integer offset)
177             throws TargetSetupError {
178         if (connection instanceof AdbSshConnection) {
179             return ((AdbSshConnection) connection).powerwashGce(user, offset);
180         }
181         return new CommandResult(CommandStatus.EXCEPTION);
182     }
183 }
184