Home | History | Annotate | Download | only in util
      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.
     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      */
     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      */
     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.i("Running a command: %s", cmd);
     78                 String out = device.executeShellCommand(cmd);
     79                 CLog.i("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      */
     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      */
    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.i("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      */
    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.i("validation cmd output: %s", out);
    143         if (out != null && predicate.test(out)) {
    144             CLog.i("Exit condition satisfied.");
    145             return true;
    146         } else {
    147             CLog.i("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      */
    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      */
    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      */
    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
    204     void setSleeper(ISleeper sleeper) {
    205         mSleeper = sleeper;
    206     }
    207 }
    208