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