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 17 package com.android.tradefed.util; 18 19 import com.android.annotations.VisibleForTesting; 20 import com.android.ddmlib.CollectingOutputReceiver; 21 import com.android.tradefed.device.DeviceNotAvailableException; 22 import com.android.tradefed.device.ITestDevice; 23 import com.android.tradefed.log.LogUtil.CLog; 24 25 import java.util.Vector; 26 import java.util.concurrent.TimeUnit; 27 import java.util.function.Predicate; 28 29 public class CmdUtil { 30 public static final int MAX_RETRY_COUNT = 10; 31 static final int DELAY_BETWEEN_RETRY_IN_SECS = 3; 32 static final long FRAMEWORK_START_TIMEOUT = 1000 * 60 * 2; // 2 minutes. 33 static final String BOOTCOMPLETE_PROP = "dev.bootcomplete"; 34 35 // An interface to wait with delay. Used for testing purpose. sleep(int seconds)36 public interface ISleeper { public void sleep(int seconds) throws InterruptedException; } 37 private ISleeper mSleeper = null; 38 39 /** 40 * Helper method to retry given cmd until the expected results are satisfied. 41 * An example usage it to retry 'lshal' until the expected hal service appears. 42 * 43 * @param device testing device. 44 * @param cmd the command string to be executed on device. 45 * @param predicate function that checks the exit condition. 46 * @return true if the exit condition is satisfied, false otherwise. 47 * @throws DeviceNotAvailableException 48 */ waitCmdResultWithDelay(ITestDevice device, String cmd, Predicate<String> predicate)49 public boolean waitCmdResultWithDelay(ITestDevice device, String cmd, 50 Predicate<String> predicate) throws DeviceNotAvailableException { 51 for (int count = 0; count < MAX_RETRY_COUNT; count++) { 52 if (validateCmdSuccess(device, cmd, predicate)) { 53 return true; 54 } 55 } 56 return false; 57 } 58 59 /** 60 * Helper method to retry a given set of commands, cmds. 61 * 62 * @param device testing device. 63 * @param cmds a vector of the command strings to be executed on device. 64 * @param validation_cmd the validation command string to be executed on device. 65 * @param predicate function that checks the exit condition. 66 * @return true if the exit condition is satisfied, false otherwise. 67 * @throws DeviceNotAvailableException 68 */ retry(ITestDevice device, Vector<String> cmds, String validation_cmd, Predicate<String> predicate)69 public boolean retry(ITestDevice device, Vector<String> cmds, String validation_cmd, 70 Predicate<String> predicate) throws DeviceNotAvailableException { 71 if (cmds.isEmpty()) { 72 CLog.w("retry() called but cmd is an epmty vector."); 73 return false; 74 } 75 for (int count = 0; count < MAX_RETRY_COUNT; count++) { 76 for (String cmd : cmds) { 77 CLog.d("Running a command: %s", cmd); 78 String out = device.executeShellCommand(cmd); 79 CLog.d("Command output: %s", out); 80 } 81 if (validateCmdSuccess(device, validation_cmd, predicate)) { 82 return true; 83 } 84 } 85 return false; 86 } 87 88 /** 89 * Helper method to retry a given cmd. 90 * 91 * @param device testing device. 92 * @param cmd the command string to be executed on device. 93 * @param validation_cmd the validation command string to be executed on device. 94 * @param predicate function that checks the exit condition. 95 * @return true if the exit condition is satisfied, false otherwise. 96 * @throws DeviceNotAvailableException 97 */ retry(ITestDevice device, String cmd, String validation_cmd, Predicate<String> predicate)98 public boolean retry(ITestDevice device, String cmd, String validation_cmd, 99 Predicate<String> predicate) throws DeviceNotAvailableException { 100 return retry(device, cmd, validation_cmd, predicate, MAX_RETRY_COUNT); 101 } 102 103 /** 104 * Helper method to retry a given cmd. 105 * 106 * @param device testing device. 107 * @param cmd the command string to be executed on device. 108 * @param validation_cmd the validation command string to be executed on device. 109 * @param predicate function that checks the exit condition. 110 * @param retry_count the max number of times to try 111 * @return true if the exit condition is satisfied, false otherwise. 112 * @throws DeviceNotAvailableException 113 */ retry(ITestDevice device, String cmd, String validation_cmd, Predicate<String> predicate, int retry_count)114 public boolean retry(ITestDevice device, String cmd, String validation_cmd, 115 Predicate<String> predicate, int retry_count) throws DeviceNotAvailableException { 116 for (int count = 0; count < retry_count; count++) { 117 CLog.d("Running a command: %s", cmd); 118 device.executeShellCommand(cmd); 119 if (validateCmdSuccess(device, validation_cmd, predicate)) { 120 return true; 121 } 122 } 123 return false; 124 } 125 126 /** 127 * Validates the device status and waits if the validation fails. 128 * 129 * @param device testing device. 130 * @param cmd the command string to be executed on device. 131 * @param predicate function that checks the exit condition. 132 * @return true if the exit condition is satisfied, false otherwise. 133 * @throws DeviceNotAvailableException 134 */ validateCmdSuccess(ITestDevice device, String cmd, Predicate<String> predicate)135 protected boolean validateCmdSuccess(ITestDevice device, String cmd, 136 Predicate<String> predicate) throws DeviceNotAvailableException { 137 if (cmd == null) { 138 CLog.w("validateCmdSuccess() called but cmd is null"); 139 return false; 140 } 141 String out = device.executeShellCommand(cmd); 142 CLog.d("validating cmd output: %s", out); 143 if (out != null && predicate.test(out)) { 144 CLog.d("Exit condition satisfied."); 145 return true; 146 } else { 147 CLog.d("Exit condition not satisfied. Waiting for %s more seconds.", 148 DELAY_BETWEEN_RETRY_IN_SECS); 149 try { 150 if (mSleeper != null) { 151 mSleeper.sleep(DELAY_BETWEEN_RETRY_IN_SECS); 152 } else { 153 TimeUnit.SECONDS.sleep(DELAY_BETWEEN_RETRY_IN_SECS); 154 } 155 } catch (InterruptedException ex) { 156 /* pass */ 157 } 158 } 159 return false; 160 } 161 162 /** 163 * Restarts the Andriod framework and waits for the device boot completion. 164 * 165 * @param device the test device instance. 166 * @throws DeviceNotAvailableException 167 */ restartFramework(ITestDevice device)168 public void restartFramework(ITestDevice device) throws DeviceNotAvailableException { 169 device.executeShellCommand("stop"); 170 setSystemProperty(device, BOOTCOMPLETE_PROP, "0"); 171 device.executeShellCommand("start"); 172 device.waitForDeviceAvailable(FRAMEWORK_START_TIMEOUT); 173 } 174 175 /** 176 * Gets a sysprop from the device. 177 * 178 * @param device the test device instance. 179 * @param name the name of a sysprop. 180 * @return the device sysprop value. 181 * @throws DeviceNotAvailableException 182 */ getSystemProperty(ITestDevice device, String name)183 public String getSystemProperty(ITestDevice device, String name) 184 throws DeviceNotAvailableException { 185 CollectingOutputReceiver receiver = new CollectingOutputReceiver(); 186 device.executeShellCommand(String.format("getprop %s", name), receiver); 187 return receiver.getOutput(); 188 } 189 190 /** 191 * Sets a sysprop on the device. 192 * 193 * @param device the test device instance. 194 * @param name the name of a sysprop. 195 * @param value the value of a sysprop. 196 * @throws DeviceNotAvailableException 197 */ setSystemProperty(ITestDevice device, String name, String value)198 public void setSystemProperty(ITestDevice device, String name, String value) 199 throws DeviceNotAvailableException { 200 device.executeShellCommand(String.format("setprop %s %s", name, value)); 201 } 202 203 @VisibleForTesting setSleeper(ISleeper sleeper)204 void setSleeper(ISleeper sleeper) { 205 mSleeper = sleeper; 206 } 207 } 208