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