Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2016 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 android.security.cts;
     18 
     19 import com.android.ddmlib.NullOutputReceiver;
     20 import com.android.tradefed.device.CollectingOutputReceiver;
     21 import com.android.tradefed.device.ITestDevice;
     22 import com.android.tradefed.log.LogUtil.CLog;
     23 
     24 import java.io.BufferedOutputStream;
     25 import java.io.File;
     26 import java.io.FileOutputStream;
     27 import java.io.InputStream;
     28 import java.io.OutputStream;
     29 import java.util.concurrent.TimeUnit;
     30 import java.util.Scanner;
     31 
     32 import static org.junit.Assert.*;
     33 
     34 public class AdbUtils {
     35 
     36     /** Runs a commandline on the specified device
     37      *
     38      * @param command the command to be ran
     39      * @param device device for the command to be ran on
     40      * @return the console output from running the command
     41      */
     42     public static String runCommandLine(String command, ITestDevice device) throws Exception {
     43         if ("reboot".equals(command)) {
     44             throw new IllegalArgumentException(
     45                     "You called a forbidden command! Please fix your tests.");
     46         }
     47         return device.executeShellCommand(command);
     48     }
     49 
     50     /**
     51      * Pushes and runs a binary to the selected device
     52      *
     53      * @param pocName a string path to poc from the /res folder
     54      * @param device device to be ran on
     55      * @return the console output from the binary
     56      */
     57     public static String runPoc(String pocName, ITestDevice device) throws Exception {
     58         device.executeShellCommand("chmod +x /data/local/tmp/" + pocName);
     59         return device.executeShellCommand("/data/local/tmp/" + pocName);
     60     }
     61 
     62     /**
     63      * Pushes and runs a binary to the selected device
     64      *
     65      * @param pocName a string path to poc from the /res folder
     66      * @param device device to be ran on
     67      * @param timeout time to wait for output in seconds
     68      * @return the console output from the binary
     69      */
     70     public static String runPoc(String pocName, ITestDevice device, int timeout) throws Exception {
     71         device.executeShellCommand("chmod +x /data/local/tmp/" + pocName);
     72         CollectingOutputReceiver receiver = new CollectingOutputReceiver();
     73         device.executeShellCommand("/data/local/tmp/" + pocName, receiver, timeout, TimeUnit.SECONDS, 0);
     74         String output = receiver.getOutput();
     75         return output;
     76     }
     77 
     78     /**
     79      * Pushes and runs a binary to the selected device and ignores any of its output.
     80      *
     81      * @param pocName a string path to poc from the /res folder
     82      * @param device device to be ran on
     83      * @param timeout time to wait for output in seconds
     84      */
     85     public static void runPocNoOutput(String pocName, ITestDevice device, int timeout)
     86             throws Exception {
     87         device.executeShellCommand("chmod +x /data/local/tmp/" + pocName);
     88         NullOutputReceiver receiver = new NullOutputReceiver();
     89         device.executeShellCommand("/data/local/tmp/" + pocName, receiver, timeout,
     90                 TimeUnit.SECONDS, 0);
     91     }
     92 
     93     /**
     94      * Enables malloc debug on a given process.
     95      *
     96      * @param processName the name of the process to run with libc malloc debug
     97      * @param device the device to use
     98      * @return true if enabling malloc debug succeeded
     99      */
    100     public static boolean enableLibcMallocDebug(String processName, ITestDevice device) throws Exception {
    101         device.executeShellCommand("setprop libc.debug.malloc.program " + processName);
    102         device.executeShellCommand("setprop libc.debug.malloc.options \"backtrace guard\"");
    103         /**
    104          * The pidof command is being avoided because it does not exist on versions before M, and
    105          * it behaves differently between M and N.
    106          * Also considered was the ps -AoPID,CMDLINE command, but ps does not support options on
    107          * versions before O.
    108          * The [^]] prefix is being used for the grep command to avoid the case where the output of
    109          * ps includes the grep command itself.
    110          */
    111         String cmdOut = device.executeShellCommand("ps -A | grep '[^]]" + processName + "'");
    112         /**
    113          * .hasNextInt() checks if the next token can be parsed as an integer, not if any remaining
    114          * token is an integer.
    115          * Example command: $ ps | fgrep mediaserver
    116          * Out: media     269   1     77016  24416 binder_thr 00f35142ec S /system/bin/mediaserver
    117          * The second field of the output is the PID, which is needed to restart the process.
    118          */
    119         Scanner s = new Scanner(cmdOut).useDelimiter("\\D+");
    120         if(!s.hasNextInt()) {
    121             CLog.w("Could not find pid for process: " + processName);
    122             return false;
    123         }
    124 
    125         String result = device.executeShellCommand("kill -9 " + s.nextInt());
    126         if(!result.equals("")) {
    127             CLog.w("Could not restart process: " + processName);
    128             return false;
    129         }
    130 
    131         TimeUnit.SECONDS.sleep(1);
    132         return true;
    133     }
    134 
    135     /**
    136      * Pushes and installs an apk to the selected device
    137      *
    138      * @param pathToApk a string path to apk from the /res folder
    139      * @param device device to be ran on
    140      * @return the output from attempting to install the apk
    141      */
    142     public static String installApk(String pathToApk, ITestDevice device) throws Exception {
    143 
    144         String fullResourceName = pathToApk;
    145         File apkFile = File.createTempFile("apkFile", ".apk");
    146         try {
    147             apkFile = extractResource(fullResourceName, apkFile);
    148             return device.installPackage(apkFile, true);
    149         } finally {
    150             apkFile.delete();
    151         }
    152     }
    153 
    154     /**
    155      * Extracts a resource and pushes it to the device
    156      *
    157      * @param fullResourceName a string path to resource from the res folder
    158      * @param deviceFilePath the remote destination absolute file path
    159      * @param device device to be ran on
    160      */
    161     public static void pushResource(String fullResourceName, String deviceFilePath,
    162                                     ITestDevice device) throws Exception {
    163         File resFile = File.createTempFile("CTSResource", "");
    164         try {
    165             resFile = extractResource(fullResourceName, resFile);
    166             device.pushFile(resFile, deviceFilePath);
    167         } finally {
    168             resFile.delete();
    169         }
    170     }
    171 
    172    /**
    173      * Extracts the binary data from a resource and writes it to a temp file
    174      */
    175     private static File extractResource(String fullResourceName, File file) throws Exception {
    176         try (InputStream in = AdbUtils.class.getResourceAsStream(fullResourceName);
    177             OutputStream out = new BufferedOutputStream(new FileOutputStream(file))) {
    178             if (in == null) {
    179                 throw new IllegalArgumentException("Resource not found: " + fullResourceName);
    180             }
    181             byte[] buf = new byte[65536];
    182             int chunkSize;
    183             while ((chunkSize = in.read(buf)) != -1) {
    184                 out.write(buf, 0, chunkSize);
    185             }
    186             return file;
    187         }
    188 
    189     }
    190     /**
    191      * Utility function to help check the exit code of a shell command
    192      */
    193     public static int runCommandGetExitCode(String cmd, ITestDevice device) throws Exception {
    194       return Integer.parseInt(
    195           AdbUtils.runCommandLine( "(" + cmd + ") > /dev/null 2>&1; echo $?",
    196             device).replaceAll("[^0-9]", ""));
    197     }
    198 
    199     /**
    200      * Pushes and runs a binary to the selected device and checks exit code
    201      * Return code 113 is used to indicate the vulnerability
    202      *
    203      * @param pocName a string path to poc from the /res folder
    204      * @param device device to be ran on
    205      * @param timeout time to wait for output in seconds
    206      */
    207     @Deprecated
    208     public static boolean runPocCheckExitCode(String pocName, ITestDevice device,
    209                                               int timeout) throws Exception {
    210 
    211        //Refer to go/asdl-sts-guide Test section for knowing the significance of 113 code
    212        return runPocGetExitStatus(pocName, device, timeout) == 113;
    213     }
    214 
    215     /**
    216      * Pushes and runs a binary to the device and returns the exit status.
    217      * @param pocName a string path to poc from the /res folder
    218      * @param device device to be ran on
    219      * @param timeout time to wait for output in seconds
    220 
    221      */
    222     public static int runPocGetExitStatus(String pocName, ITestDevice device, int timeout)
    223             throws Exception {
    224         device.executeShellCommand("chmod +x /data/local/tmp/" + pocName);
    225         CollectingOutputReceiver receiver = new CollectingOutputReceiver();
    226         device.executeShellCommand("/data/local/tmp/" + pocName + " > /dev/null 2>&1; echo $?",
    227                                    receiver, timeout, TimeUnit.SECONDS, 0);
    228 
    229         String exitStatus = receiver.getOutput().replaceAll("[^0-9]", "");
    230         return Integer.parseInt(exitStatus);
    231     }
    232 
    233     /**
    234      * Pushes and runs a binary and asserts that the exit status isn't 113: vulnerable.
    235      * @param pocName a string path to poc from the /res folder
    236      * @param device device to be ran on
    237      * @param timeout time to wait for output in seconds
    238      */
    239     public static void runPocAssertExitStatusNotVulnerable(
    240             String pocName, ITestDevice device, int timeout) throws Exception {
    241         assertTrue("PoC returned exit status 113: vulnerable",
    242                 runPocGetExitStatus(pocName, device, timeout) != 113);
    243     }
    244 }
    245