1 /* 2 * Copyright (C) 2012 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.tradefed.targetprep; 18 19 import com.android.tradefed.config.Option; 20 import com.android.tradefed.config.OptionClass; 21 import com.android.tradefed.device.BackgroundDeviceAction; 22 import com.android.tradefed.device.CollectingOutputReceiver; 23 import com.android.tradefed.device.DeviceNotAvailableException; 24 import com.android.tradefed.device.ITestDevice; 25 import com.android.tradefed.invoker.TestInformation; 26 import com.android.tradefed.log.LogUtil.CLog; 27 import com.android.tradefed.result.error.DeviceErrorIdentifier; 28 import com.android.tradefed.util.CommandResult; 29 import com.android.tradefed.util.CommandStatus; 30 import com.android.tradefed.util.RunUtil; 31 32 import java.util.ArrayList; 33 import java.util.HashMap; 34 import java.util.List; 35 import java.util.Map; 36 import java.util.concurrent.TimeUnit; 37 38 @OptionClass(alias = "run-command") 39 public class RunCommandTargetPreparer extends BaseTargetPreparer { 40 41 @Option(name = "run-command", description = "adb shell command to run") 42 private List<String> mCommands = new ArrayList<String>(); 43 44 @Option(name = "run-bg-command", description = "Command to run repeatedly in the" 45 + " device background. Can be repeated to run multiple commands" 46 + " in the background.") 47 private List<String> mBgCommands = new ArrayList<String>(); 48 49 @Option(name = "hide-bg-output", description = "if true, don't log background command output") 50 private boolean mHideBgOutput = false; 51 52 @Option(name = "teardown-command", description = "adb shell command to run at teardown time") 53 private List<String> mTeardownCommands = new ArrayList<String>(); 54 55 @Option(name = "delay-after-commands", 56 description = "Time to delay after running commands, in msecs") 57 private long mDelayMsecs = 0; 58 59 @Option(name = "run-command-timeout", 60 description = "Timeout for execute shell command", 61 isTimeVal = true) 62 private long mRunCmdTimeout = 0; 63 64 @Option(name = "throw-if-cmd-fail", description = "Whether or not to throw if a command fails") 65 private boolean mThrowIfFailed = false; 66 67 private Map<BackgroundDeviceAction, CollectingOutputReceiver> mBgDeviceActionsMap = 68 new HashMap<>(); 69 70 /** {@inheritDoc} */ 71 @Override setUp(TestInformation testInfo)72 public void setUp(TestInformation testInfo) 73 throws TargetSetupError, DeviceNotAvailableException { 74 ITestDevice device = getDevice(testInfo); 75 for (String bgCmd : mBgCommands) { 76 CollectingOutputReceiver receiver = new CollectingOutputReceiver(); 77 BackgroundDeviceAction mBgDeviceAction = 78 new BackgroundDeviceAction(bgCmd, bgCmd, device, receiver, 0); 79 mBgDeviceAction.start(); 80 mBgDeviceActionsMap.put(mBgDeviceAction, receiver); 81 } 82 83 for (String cmd : mCommands) { 84 CommandResult result; 85 // Shell v2 with command status checks 86 if (mRunCmdTimeout > 0) { 87 result = 88 device.executeShellV2Command(cmd, mRunCmdTimeout, TimeUnit.MILLISECONDS, 0); 89 } else { 90 result = device.executeShellV2Command(cmd); 91 } 92 // Ensure the command ran successfully. 93 if (!CommandStatus.SUCCESS.equals(result.getStatus())) { 94 if (mThrowIfFailed) { 95 throw new TargetSetupError( 96 String.format( 97 "Failed to run '%s' without error. stdout: '%s'\nstderr: '%s'", 98 cmd, result.getStdout(), result.getStderr()), 99 device.getDeviceDescriptor(), 100 DeviceErrorIdentifier.SHELL_COMMAND_ERROR); 101 } else { 102 CLog.d( 103 "cmd: '%s' failed, returned:\nstdout:%s\nstderr:%s", 104 cmd, result.getStdout(), result.getStderr()); 105 } 106 } 107 } 108 109 if (mDelayMsecs > 0) { 110 CLog.d("Sleeping %d msecs on device %s", mDelayMsecs, device.getSerialNumber()); 111 RunUtil.getDefault().sleep(mDelayMsecs); 112 } 113 } 114 115 /** {@inheritDoc} */ 116 @Override tearDown(TestInformation testInfo, Throwable e)117 public void tearDown(TestInformation testInfo, Throwable e) throws DeviceNotAvailableException { 118 for (Map.Entry<BackgroundDeviceAction, CollectingOutputReceiver> bgAction : 119 mBgDeviceActionsMap.entrySet()) { 120 if (!mHideBgOutput) { 121 CLog.d("Background command output : %s", bgAction.getValue().getOutput()); 122 } 123 bgAction.getKey().cancel(); 124 } 125 if (e instanceof DeviceNotAvailableException) { 126 CLog.e("Skipping command teardown since exception was DeviceNotAvailable"); 127 return; 128 } 129 for (String cmd : mTeardownCommands) { 130 try { 131 CommandResult result = getDevice(testInfo).executeShellV2Command(cmd); 132 if (!CommandStatus.SUCCESS.equals(result.getStatus())) { 133 CLog.d( 134 "tearDown cmd: '%s' failed, returned:\nstdout:%s\nstderr:%s", 135 cmd, result.getStdout(), result.getStderr()); 136 } 137 } catch (TargetSetupError tse) { 138 CLog.e(tse); 139 } 140 } 141 } 142 143 /** Add a command that will be run by the preparer. */ addRunCommand(String cmd)144 public final void addRunCommand(String cmd) { 145 mCommands.add(cmd); 146 } 147 148 /** 149 * Returns the device to apply the preparer on. 150 * 151 * @param testInfo 152 * @return The device to apply the preparer on. 153 * @throws TargetSetupError 154 */ getDevice(TestInformation testInfo)155 protected ITestDevice getDevice(TestInformation testInfo) throws TargetSetupError { 156 return testInfo.getDevice(); 157 } 158 } 159 160