Home | History | Annotate | Download | only in pm
      1 /*
      2  * Copyright (C) 2010 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.content.pm;
     18 
     19 import com.android.ddmlib.AdbCommandRejectedException;
     20 import com.android.ddmlib.AndroidDebugBridge;
     21 import com.android.ddmlib.IDevice;
     22 import com.android.ddmlib.IShellOutputReceiver;
     23 import com.android.ddmlib.InstallException;
     24 import com.android.ddmlib.Log;
     25 import com.android.ddmlib.MultiLineReceiver;
     26 import com.android.ddmlib.ShellCommandUnresponsiveException;
     27 import com.android.ddmlib.SyncException;
     28 import com.android.ddmlib.TimeoutException;
     29 import com.android.ddmlib.SyncService.ISyncProgressMonitor;
     30 import com.android.ddmlib.testrunner.ITestRunListener;
     31 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
     32 import com.android.ddmlib.testrunner.TestIdentifier;
     33 
     34 import java.io.BufferedReader;
     35 import java.io.IOException;
     36 import java.io.InputStreamReader;
     37 import java.io.StringReader;
     38 import java.lang.Runtime;
     39 import java.lang.Process;
     40 import java.util.Hashtable;
     41 import java.util.Map;
     42 import java.util.Map.Entry;
     43 import java.util.regex.Matcher;
     44 import java.util.regex.Pattern;
     45 
     46 import junit.framework.Assert;
     47 
     48 /**
     49  * Set of tests that verify host side install cases
     50  */
     51 public class PackageManagerHostTestUtils extends Assert {
     52 
     53     private static final String LOG_TAG = "PackageManagerHostTests";
     54     private IDevice mDevice = null;
     55 
     56     // TODO: get this value from Android Environment instead of hardcoding
     57     private static final String APP_PRIVATE_PATH = "/data/app-private/";
     58     private static final String DEVICE_APP_PATH = "/data/app/";
     59     private static final String SDCARD_APP_PATH = "/mnt/secure/asec/";
     60 
     61     private static final int MAX_WAIT_FOR_DEVICE_TIME = 120 * 1000;
     62     private static final int WAIT_FOR_DEVICE_POLL_TIME = 10 * 1000;
     63     private static final int MAX_WAIT_FOR_APP_LAUNCH_TIME = 60 * 1000;
     64     private static final int WAIT_FOR_APP_LAUNCH_POLL_TIME = 5 * 1000;
     65 
     66     // Install preference on the device-side
     67     public static enum InstallLocPreference {
     68         AUTO,
     69         INTERNAL,
     70         EXTERNAL
     71     }
     72 
     73     // Actual install location
     74     public static enum InstallLocation {
     75         DEVICE,
     76         SDCARD
     77     }
     78 
     79     /**
     80      * Constructor takes the device to use
     81      * @param the device to use when performing operations
     82      */
     83     public PackageManagerHostTestUtils(IDevice device)
     84     {
     85           mDevice = device;
     86     }
     87 
     88     /**
     89      * Disable default constructor
     90      */
     91     private PackageManagerHostTestUtils() {}
     92 
     93     /**
     94      * Returns the path on the device of forward-locked apps.
     95      *
     96      * @return path of forward-locked apps on the device
     97      */
     98     public static String getAppPrivatePath() {
     99         return APP_PRIVATE_PATH;
    100     }
    101 
    102     /**
    103      * Returns the path on the device of normal apps.
    104      *
    105      * @return path of forward-locked apps on the device
    106      */
    107     public static String getDeviceAppPath() {
    108         return DEVICE_APP_PATH;
    109     }
    110 
    111     /**
    112      * Returns the path of apps installed on the SD card.
    113      *
    114      * @return path of forward-locked apps on the device
    115      */
    116     public static String getSDCardAppPath() {
    117         return SDCARD_APP_PATH;
    118     }
    119 
    120     /**
    121      * Helper method to run tests and return the listener that collected the results.
    122      *
    123      * For the optional params, pass null to use the default values.
    124 
    125      * @param pkgName Android application package for tests
    126      * @param className (optional) The class containing the method to test
    127      * @param methodName (optional) The method in the class of which to test
    128      * @param runnerName (optional) The name of the TestRunner of the test on the device to be run
    129      * @param params (optional) Any additional parameters to pass into the Test Runner
    130      * @throws TimeoutException in case of a timeout on the connection.
    131      * @throws AdbCommandRejectedException if adb rejects the command
    132      * @throws ShellCommandUnresponsiveException if the device did not output anything for
    133      * a period longer than the max time to output.
    134      * @throws IOException if connection to device was lost.
    135      * @return the {@link CollectingTestRunListener}
    136      */
    137     private CollectingTestRunListener doRunTests(String pkgName, String className,
    138             String methodName, String runnerName, Map<String, String> params) throws IOException,
    139             TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException {
    140         RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(pkgName, runnerName,
    141                 mDevice);
    142 
    143         if (className != null && methodName != null) {
    144             testRunner.setMethodName(className, methodName);
    145         }
    146 
    147         // Add in any additional args to pass into the test
    148         if (params != null) {
    149             for (Entry<String, String> argPair : params.entrySet()) {
    150                 testRunner.addInstrumentationArg(argPair.getKey(), argPair.getValue());
    151             }
    152         }
    153 
    154         CollectingTestRunListener listener = new CollectingTestRunListener();
    155         try {
    156             testRunner.run(listener);
    157         } catch (IOException ioe) {
    158             Log.w(LOG_TAG, "encountered IOException " + ioe);
    159         }
    160         return listener;
    161     }
    162 
    163     /**
    164      * Runs the specified packages tests, and returns whether all tests passed or not.
    165      *
    166      * @param pkgName Android application package for tests
    167      * @param className The class containing the method to test
    168      * @param methodName The method in the class of which to test
    169      * @param runnerName The name of the TestRunner of the test on the device to be run
    170      * @param params Any additional parameters to pass into the Test Runner
    171      * @return true if test passed, false otherwise.
    172      */
    173     public boolean runDeviceTestsDidAllTestsPass(String pkgName, String className,
    174             String methodName, String runnerName, Map<String, String> params) throws IOException,
    175             TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException {
    176         CollectingTestRunListener listener = doRunTests(pkgName, className, methodName,
    177                 runnerName, params);
    178         return listener.didAllTestsPass();
    179     }
    180 
    181     /**
    182      * Runs the specified packages tests, and returns whether all tests passed or not.
    183      *
    184      * @param pkgName Android application package for tests
    185      * @throws TimeoutException in case of a timeout on the connection.
    186      * @throws AdbCommandRejectedException if adb rejects the command
    187      * @throws ShellCommandUnresponsiveException if the device did not output anything for
    188      * a period longer than the max time to output.
    189      * @throws IOException if connection to device was lost.
    190      * @return true if every test passed, false otherwise.
    191      */
    192     public boolean runDeviceTestsDidAllTestsPass(String pkgName) throws IOException,
    193             TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException {
    194         CollectingTestRunListener listener = doRunTests(pkgName, null, null, null, null);
    195         return listener.didAllTestsPass();
    196     }
    197 
    198     /**
    199      * Helper method to push a file to device
    200      * @param apkAppPrivatePath
    201      * @throws TimeoutException in case of a timeout on the connection.
    202      * @throws AdbCommandRejectedException if adb rejects the command
    203      * @throws IOException if connection to device was lost.
    204      * @throws SyncException if the sync failed for another reason.
    205      */
    206     public void pushFile(final String localFilePath, final String destFilePath)
    207             throws IOException, SyncException, TimeoutException, AdbCommandRejectedException {
    208         mDevice.getSyncService().pushFile(localFilePath,
    209                 destFilePath, new NullSyncProgressMonitor());
    210     }
    211 
    212     /**
    213      * Helper method to install a file
    214      * @param localFilePath the absolute file system path to file on local host to install
    215      * @param reinstall set to <code>true</code> if re-install of app should be performed
    216      * @throws IOException if connection to device was lost.
    217      * @throws InstallException if the install failed
    218      */
    219     public void installFile(final String localFilePath, final boolean replace) throws IOException,
    220             InstallException {
    221         String result = mDevice.installPackage(localFilePath, replace);
    222         assertEquals(null, result);
    223     }
    224 
    225     /**
    226      * Helper method to install a file that should not be install-able
    227      * @param localFilePath the absolute file system path to file on local host to install
    228      * @param reinstall set to <code>true</code> if re-install of app should be performed
    229      * @return the string output of the failed install attempt
    230      * @throws IOException if connection to device was lost.
    231      * @throws InstallException if the install failed
    232      */
    233     public String installFileFail(final String localFilePath, final boolean replace)
    234             throws IOException, InstallException {
    235         String result = mDevice.installPackage(localFilePath, replace);
    236         assertNotNull(result);
    237         return result;
    238     }
    239 
    240     /**
    241      * Helper method to install a file to device as forward locked
    242      * @param localFilePath the absolute file system path to file on local host to install
    243      * @param reinstall set to <code>true</code> if re-install of app should be performed
    244      * @throws TimeoutException in case of a timeout on the connection.
    245      * @throws AdbCommandRejectedException if adb rejects the command
    246      * @throws ShellCommandUnresponsiveException if the device did not output anything for
    247      * a period longer than the max time to output.
    248      * @throws IOException if connection to device was lost.
    249      * @throws SyncException if the sync failed for another reason.
    250      * @throws InstallException if the install failed.
    251      */
    252     public String installFileForwardLocked(final String localFilePath, final boolean replace)
    253             throws IOException, SyncException, TimeoutException, AdbCommandRejectedException,
    254             ShellCommandUnresponsiveException, InstallException {
    255         String remoteFilePath = mDevice.syncPackageToDevice(localFilePath);
    256         InstallReceiver receiver = new InstallReceiver();
    257         String cmd = String.format(replace ? "pm install -r -l \"%1$s\"" :
    258                 "pm install -l \"%1$s\"", remoteFilePath);
    259         mDevice.executeShellCommand(cmd, receiver);
    260         mDevice.removeRemotePackage(remoteFilePath);
    261         return receiver.getErrorMessage();
    262     }
    263 
    264     /**
    265      * Helper method to determine if file on device exists.
    266      *
    267      * @param destPath the absolute path of file on device to check
    268      * @return <code>true</code> if file exists, <code>false</code> otherwise.
    269      * @throws TimeoutException in case of a timeout on the connection.
    270      * @throws AdbCommandRejectedException if adb rejects the command
    271      * @throws ShellCommandUnresponsiveException if the device did not output anything for
    272      * a period longer than the max time to output.
    273      * @throws IOException if connection to device was lost.
    274      */
    275     public boolean doesRemoteFileExist(String destPath) throws IOException, TimeoutException,
    276             AdbCommandRejectedException, ShellCommandUnresponsiveException {
    277         String lsGrep = executeShellCommand(String.format("ls %s", destPath));
    278         return !lsGrep.contains("No such file or directory");
    279     }
    280 
    281     /**
    282      * Helper method to determine if file exists on the device containing a given string.
    283      *
    284      * @param destPath the absolute path of the file
    285      * @return <code>true</code> if file exists containing given string,
    286      *         <code>false</code> otherwise.
    287      * @throws TimeoutException in case of a timeout on the connection.
    288      * @throws AdbCommandRejectedException if adb rejects the command
    289      * @throws ShellCommandUnresponsiveException if the device did not output anything for
    290      * a period longer than the max time to output.
    291      * @throws IOException if connection to device was lost.
    292      */
    293     public boolean doesRemoteFileExistContainingString(String destPath, String searchString)
    294             throws IOException, TimeoutException, AdbCommandRejectedException,
    295             ShellCommandUnresponsiveException {
    296         String lsResult = executeShellCommand(String.format("ls %s", destPath));
    297         return lsResult.contains(searchString);
    298     }
    299 
    300     /**
    301      * Helper method to determine if package on device exists.
    302      *
    303      * @param packageName the Android manifest package to check.
    304      * @return <code>true</code> if package exists, <code>false</code> otherwise
    305      * @throws TimeoutException in case of a timeout on the connection.
    306      * @throws AdbCommandRejectedException if adb rejects the command
    307      * @throws ShellCommandUnresponsiveException if the device did not output anything for
    308      * a period longer than the max time to output.
    309      * @throws IOException if connection to device was lost.
    310      */
    311     public boolean doesPackageExist(String packageName) throws IOException, TimeoutException,
    312             AdbCommandRejectedException, ShellCommandUnresponsiveException {
    313         String pkgGrep = executeShellCommand(String.format("pm path %s", packageName));
    314         return pkgGrep.contains("package:");
    315     }
    316 
    317     /**
    318      * Determines if app was installed on device.
    319      *
    320      * @param packageName package name to check for
    321      * @return <code>true</code> if file exists, <code>false</code> otherwise.
    322      * @throws TimeoutException in case of a timeout on the connection.
    323      * @throws AdbCommandRejectedException if adb rejects the command
    324      * @throws ShellCommandUnresponsiveException if the device did not output anything for
    325      * a period longer than the max time to output.
    326      * @throws IOException if connection to device was lost.
    327      */
    328     public boolean doesAppExistOnDevice(String packageName) throws IOException, TimeoutException,
    329             AdbCommandRejectedException, ShellCommandUnresponsiveException {
    330         return doesRemoteFileExistContainingString(DEVICE_APP_PATH, packageName);
    331     }
    332 
    333     /**
    334      * Determines if app was installed on SD card.
    335      *
    336      * @param packageName package name to check for
    337      * @return <code>true</code> if file exists, <code>false</code> otherwise.
    338      * @throws TimeoutException in case of a timeout on the connection.
    339      * @throws AdbCommandRejectedException if adb rejects the command
    340      * @throws ShellCommandUnresponsiveException if the device did not output anything for
    341      * a period longer than the max time to output.
    342      * @throws IOException if connection to device was lost.
    343      */
    344     public boolean doesAppExistOnSDCard(String packageName) throws IOException, TimeoutException,
    345             AdbCommandRejectedException, ShellCommandUnresponsiveException {
    346         return doesRemoteFileExistContainingString(SDCARD_APP_PATH, packageName);
    347     }
    348 
    349     /**
    350      * Helper method to determine if app was installed on SD card.
    351      *
    352      * @param packageName package name to check for
    353      * @return <code>true</code> if file exists, <code>false</code> otherwise.
    354      * @throws TimeoutException in case of a timeout on the connection.
    355      * @throws AdbCommandRejectedException if adb rejects the command
    356      * @throws ShellCommandUnresponsiveException if the device did not output anything for
    357      * a period longer than the max time to output.
    358      * @throws IOException if connection to device was lost.
    359      */
    360     public boolean doesAppExistAsForwardLocked(String packageName) throws IOException,
    361             TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException {
    362         return doesRemoteFileExistContainingString(APP_PRIVATE_PATH, packageName);
    363     }
    364 
    365     /**
    366      * Waits for device's package manager to respond.
    367      *
    368      * @throws InterruptedException
    369      * @throws TimeoutException in case of a timeout on the connection.
    370      * @throws AdbCommandRejectedException if adb rejects the command
    371      * @throws ShellCommandUnresponsiveException if the device did not output anything for
    372      * a period longer than the max time to output.
    373      * @throws IOException if connection to device was lost.
    374      */
    375     public void waitForPackageManager() throws InterruptedException, IOException, TimeoutException,
    376             AdbCommandRejectedException, ShellCommandUnresponsiveException {
    377         Log.i(LOG_TAG, "waiting for device");
    378         int currentWaitTime = 0;
    379         // poll the package manager until it returns something for android
    380         while (!doesPackageExist("android")) {
    381             Thread.sleep(WAIT_FOR_DEVICE_POLL_TIME);
    382             currentWaitTime += WAIT_FOR_DEVICE_POLL_TIME;
    383             if (currentWaitTime > MAX_WAIT_FOR_DEVICE_TIME) {
    384                 Log.e(LOG_TAG, "time out waiting for device");
    385                 throw new InterruptedException();
    386             }
    387         }
    388     }
    389 
    390     /**
    391      * Helper to determine if the device is currently online and visible via ADB.
    392      *
    393      * @return true iff the device is currently available to ADB and online, false otherwise.
    394      */
    395     private boolean deviceIsOnline() {
    396         AndroidDebugBridge bridge = AndroidDebugBridge.getBridge();
    397         IDevice[] devices = bridge.getDevices();
    398 
    399         for (IDevice device : devices) {
    400             // only online if the device appears in the devices list, and its state is online
    401             if ((mDevice != null) &&
    402                     mDevice.getSerialNumber().equals(device.getSerialNumber()) &&
    403                     device.isOnline()) {
    404                 return true;
    405             }
    406         }
    407         return false;
    408     }
    409 
    410     /**
    411      * Waits for device to be online (visible to ADB) before returning, or times out if we've
    412      * waited too long. Note that this only means the device is visible via ADB, not that
    413      * PackageManager is fully up and running yet.
    414      *
    415      * @throws InterruptedException
    416      * @throws IOException
    417      */
    418     public void waitForDeviceToComeOnline() throws InterruptedException, IOException {
    419         Log.i(LOG_TAG, "waiting for device to be online");
    420         int currentWaitTime = 0;
    421 
    422         // poll ADB until we see the device is online
    423         while (!deviceIsOnline()) {
    424             Thread.sleep(WAIT_FOR_DEVICE_POLL_TIME);
    425             currentWaitTime += WAIT_FOR_DEVICE_POLL_TIME;
    426             if (currentWaitTime > MAX_WAIT_FOR_DEVICE_TIME) {
    427                 Log.e(LOG_TAG, "time out waiting for device");
    428                 throw new InterruptedException();
    429             }
    430         }
    431         // Note: if we try to access the device too quickly after it is "officially" online,
    432         // there are sometimes strange issues where it's actually not quite ready yet,
    433         // so we pause for a bit once more before actually returning.
    434         Thread.sleep(WAIT_FOR_DEVICE_POLL_TIME);
    435     }
    436 
    437     /**
    438      * Queries package manager and waits until a package is launched (or times out)
    439      *
    440      * @param packageName The name of the package to wait to load
    441      * @throws InterruptedException
    442      * @throws TimeoutException in case of a timeout on the connection.
    443      * @throws AdbCommandRejectedException if adb rejects the command
    444      * @throws ShellCommandUnresponsiveException if the device did not output anything for
    445      * a period longer than the max time to output.
    446      * @throws IOException if connection to device was lost.
    447      */
    448     public void waitForApp(String packageName) throws InterruptedException, IOException,
    449             TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException {
    450         Log.i(LOG_TAG, "waiting for app to launch");
    451         int currentWaitTime = 0;
    452         // poll the package manager until it returns something for the package we're looking for
    453         while (!doesPackageExist(packageName)) {
    454             Thread.sleep(WAIT_FOR_APP_LAUNCH_POLL_TIME);
    455             currentWaitTime += WAIT_FOR_APP_LAUNCH_POLL_TIME;
    456             if (currentWaitTime > MAX_WAIT_FOR_APP_LAUNCH_TIME) {
    457                 Log.e(LOG_TAG, "time out waiting for app to launch: " + packageName);
    458                 throw new InterruptedException();
    459             }
    460         }
    461     }
    462 
    463     /**
    464      * Helper method which executes a adb shell command and returns output as a {@link String}
    465      * @return the output of the command
    466      * @throws TimeoutException in case of a timeout on the connection.
    467      * @throws AdbCommandRejectedException if adb rejects the command
    468      * @throws ShellCommandUnresponsiveException if the device did not output anything for
    469      * a period longer than the max time to output.
    470      * @throws IOException if connection to device was lost.
    471      */
    472     public String executeShellCommand(String command) throws IOException, TimeoutException,
    473             AdbCommandRejectedException, ShellCommandUnresponsiveException {
    474         Log.i(LOG_TAG, String.format("adb shell %s", command));
    475         CollectingOutputReceiver receiver = new CollectingOutputReceiver();
    476         mDevice.executeShellCommand(command, receiver);
    477         String output = receiver.getOutput();
    478         Log.i(LOG_TAG, String.format("Result: %s", output));
    479         return output;
    480     }
    481 
    482     /**
    483      * Helper method ensures we are in root mode on the host side. It returns only after
    484      * PackageManager is actually up and running.
    485      * @throws TimeoutException in case of a timeout on the connection.
    486      * @throws AdbCommandRejectedException if adb rejects the command
    487      * @throws ShellCommandUnresponsiveException if the device did not output anything for
    488      * a period longer than the max time to output.
    489      * @throws IOException if connection to device was lost.
    490      */
    491     public void runAdbRoot() throws IOException, InterruptedException, TimeoutException,
    492             AdbCommandRejectedException, ShellCommandUnresponsiveException {
    493         Log.i(LOG_TAG, "adb root");
    494         Runtime runtime = Runtime.getRuntime();
    495         Process process = runtime.exec("adb root"); // adb should be in the path
    496         BufferedReader output = new BufferedReader(new InputStreamReader(process.getInputStream()));
    497 
    498         String nextLine = null;
    499         while (null != (nextLine = output.readLine())) {
    500             Log.i(LOG_TAG, nextLine);
    501         }
    502         process.waitFor();
    503         waitForDeviceToComeOnline();
    504         waitForPackageManager(); // now wait for package manager to actually load
    505     }
    506 
    507     /**
    508      * Helper method which reboots the device and returns once the device is online again
    509      * and package manager is up and running (note this function is synchronous to callers).
    510      * @throws InterruptedException
    511      * @throws TimeoutException in case of a timeout on the connection.
    512      * @throws AdbCommandRejectedException if adb rejects the command
    513      * @throws ShellCommandUnresponsiveException if the device did not output anything for
    514      * a period longer than the max time to output.
    515      * @throws IOException if connection to device was lost.
    516      */
    517     public void rebootDevice() throws IOException, InterruptedException, TimeoutException,
    518             AdbCommandRejectedException, ShellCommandUnresponsiveException {
    519         String command = "reboot"; // no need for -s since mDevice is already tied to a device
    520         Log.i(LOG_TAG, command);
    521         CollectingOutputReceiver receiver = new CollectingOutputReceiver();
    522         mDevice.executeShellCommand(command, receiver);
    523         String output = receiver.getOutput();
    524         Log.i(LOG_TAG, String.format("Result: %s", output));
    525         waitForDeviceToComeOnline(); // wait for device to come online
    526         runAdbRoot();
    527     }
    528 
    529     /**
    530      * A {@link IShellOutputReceiver} which collects the whole shell output into one {@link String}
    531      */
    532     private class CollectingOutputReceiver extends MultiLineReceiver {
    533 
    534         private StringBuffer mOutputBuffer = new StringBuffer();
    535 
    536         public String getOutput() {
    537             return mOutputBuffer.toString();
    538         }
    539 
    540         @Override
    541         public void processNewLines(String[] lines) {
    542             for (String line: lines) {
    543                 mOutputBuffer.append(line);
    544                 mOutputBuffer.append("\n");
    545             }
    546         }
    547 
    548         public boolean isCancelled() {
    549             return false;
    550         }
    551     }
    552 
    553     private class NullSyncProgressMonitor implements ISyncProgressMonitor {
    554         public void advance(int work) {
    555             // ignore
    556         }
    557 
    558         public boolean isCanceled() {
    559             // ignore
    560             return false;
    561         }
    562 
    563         public void start(int totalWork) {
    564             // ignore
    565 
    566         }
    567 
    568         public void startSubTask(String name) {
    569             // ignore
    570         }
    571 
    572         public void stop() {
    573             // ignore
    574         }
    575     }
    576 
    577     // For collecting results from running device tests
    578     public static class CollectingTestRunListener implements ITestRunListener {
    579 
    580         private boolean mAllTestsPassed = true;
    581         private String mTestRunErrorMessage = null;
    582 
    583         public void testEnded(TestIdentifier test, Map<String, String> metrics) {
    584             // ignore
    585         }
    586 
    587         public void testFailed(TestFailure status, TestIdentifier test,
    588                 String trace) {
    589             Log.w(LOG_TAG, String.format("%s#%s failed: %s", test.getClassName(),
    590                     test.getTestName(), trace));
    591             mAllTestsPassed = false;
    592         }
    593 
    594         public void testRunEnded(long elapsedTime, Map<String, String> resultBundle) {
    595             // ignore
    596         }
    597 
    598         public void testRunFailed(String errorMessage) {
    599             Log.w(LOG_TAG, String.format("test run failed: %s", errorMessage));
    600             mAllTestsPassed = false;
    601             mTestRunErrorMessage = errorMessage;
    602         }
    603 
    604         public void testRunStarted(String runName, int testCount) {
    605             // ignore
    606         }
    607 
    608         public void testRunStopped(long elapsedTime) {
    609             // ignore
    610         }
    611 
    612         public void testStarted(TestIdentifier test) {
    613             // ignore
    614         }
    615 
    616         boolean didAllTestsPass() {
    617             return mAllTestsPassed;
    618         }
    619 
    620         /**
    621          * Get the test run failure error message.
    622          * @return the test run failure error message or <code>null</code> if test run completed.
    623          */
    624         String getTestRunErrorMessage() {
    625             return mTestRunErrorMessage;
    626         }
    627     }
    628 
    629     /**
    630      * Output receiver for "pm install package.apk" command line.
    631      *
    632      */
    633     private static final class InstallReceiver extends MultiLineReceiver {
    634 
    635         private static final String SUCCESS_OUTPUT = "Success"; //$NON-NLS-1$
    636         private static final Pattern FAILURE_PATTERN = Pattern.compile("Failure\\s+\\[(.*)\\]"); //$NON-NLS-1$
    637 
    638         private String mErrorMessage = null;
    639 
    640         public InstallReceiver() {
    641         }
    642 
    643         @Override
    644         public void processNewLines(String[] lines) {
    645             for (String line : lines) {
    646                 if (line.length() > 0) {
    647                     if (line.startsWith(SUCCESS_OUTPUT)) {
    648                         mErrorMessage = null;
    649                     } else {
    650                         Matcher m = FAILURE_PATTERN.matcher(line);
    651                         if (m.matches()) {
    652                             mErrorMessage = m.group(1);
    653                         }
    654                     }
    655                 }
    656             }
    657         }
    658 
    659         public boolean isCancelled() {
    660             return false;
    661         }
    662 
    663         public String getErrorMessage() {
    664             return mErrorMessage;
    665         }
    666     }
    667 
    668     /**
    669      * Helper method for installing an app to wherever is specified in its manifest, and
    670      * then verifying the app was installed onto SD Card.
    671      * <p/>
    672      * Assumes adb is running as root in device under test.
    673      *
    674      * @param the path of the apk to install
    675      * @param the name of the package
    676      * @param true</code> if the app should be overwritten, <code>false</code> otherwise
    677      * @throws InterruptedException if the thread was interrupted
    678      * @throws TimeoutException in case of a timeout on the connection.
    679      * @throws AdbCommandRejectedException if adb rejects the command
    680      * @throws ShellCommandUnresponsiveException if the device did not output anything for
    681      * a period longer than the max time to output.
    682      * @throws IOException if connection to device was lost.
    683      * @throws InstallException if the install failed.
    684      */
    685     public void installAppAndVerifyExistsOnSDCard(String apkPath, String pkgName, boolean overwrite)
    686             throws IOException, InterruptedException, InstallException, TimeoutException,
    687             AdbCommandRejectedException, ShellCommandUnresponsiveException {
    688         // Start with a clean slate if we're not overwriting
    689         if (!overwrite) {
    690             // cleanup test app just in case it already exists
    691             mDevice.uninstallPackage(pkgName);
    692             // grep for package to make sure its not installed
    693             assertFalse(doesPackageExist(pkgName));
    694         }
    695 
    696         installFile(apkPath, overwrite);
    697         assertTrue(doesAppExistOnSDCard(pkgName));
    698         assertFalse(doesAppExistOnDevice(pkgName));
    699         waitForPackageManager();
    700 
    701         // grep for package to make sure it is installed
    702         assertTrue(doesPackageExist(pkgName));
    703     }
    704 
    705     /**
    706      * Helper method for installing an app to wherever is specified in its manifest, and
    707      * then verifying the app was installed onto device.
    708      * <p/>
    709      * Assumes adb is running as root in device under test.
    710      *
    711      * @param the path of the apk to install
    712      * @param the name of the package
    713      * @param true</code> if the app should be overwritten, <code>false</code> otherwise
    714      * @throws InterruptedException if the thread was interrupted
    715      * @throws TimeoutException in case of a timeout on the connection.
    716      * @throws AdbCommandRejectedException if adb rejects the command
    717      * @throws ShellCommandUnresponsiveException if the device did not output anything for
    718      * a period longer than the max time to output.
    719      * @throws IOException if connection to device was lost.
    720      * @throws InstallException if the install failed.
    721      */
    722     public void installAppAndVerifyExistsOnDevice(String apkPath, String pkgName, boolean overwrite)
    723             throws IOException, InterruptedException, InstallException, TimeoutException,
    724             AdbCommandRejectedException, ShellCommandUnresponsiveException {
    725         // Start with a clean slate if we're not overwriting
    726         if (!overwrite) {
    727             // cleanup test app just in case it already exists
    728             mDevice.uninstallPackage(pkgName);
    729             // grep for package to make sure its not installed
    730             assertFalse(doesPackageExist(pkgName));
    731         }
    732 
    733         installFile(apkPath, overwrite);
    734         assertFalse(doesAppExistOnSDCard(pkgName));
    735         assertTrue(doesAppExistOnDevice(pkgName));
    736         waitForPackageManager();
    737 
    738         // grep for package to make sure it is installed
    739         assertTrue(doesPackageExist(pkgName));
    740     }
    741 
    742     /**
    743      * Helper method for installing an app as forward-locked, and
    744      * then verifying the app was installed in the proper forward-locked location.
    745      * <p/>
    746      * Assumes adb is running as root in device under test.
    747      *
    748      * @param the path of the apk to install
    749      * @param the name of the package
    750      * @param true</code> if the app should be overwritten, <code>false</code> otherwise
    751      * @throws InterruptedException if the thread was interrupted
    752      * @throws IOException if connection to device was lost.
    753      * @throws InstallException if the install failed.
    754      * @throws TimeoutException in case of a timeout on the connection.
    755      * @throws AdbCommandRejectedException if adb rejects the command
    756      * @throws ShellCommandUnresponsiveException if the device did not output anything for
    757      * a period longer than the max time to output.
    758      */
    759     public void installFwdLockedAppAndVerifyExists(String apkPath,
    760             String pkgName, boolean overwrite) throws IOException, InterruptedException,
    761             InstallException, SyncException, TimeoutException, AdbCommandRejectedException,
    762             ShellCommandUnresponsiveException {
    763         // Start with a clean slate if we're not overwriting
    764         if (!overwrite) {
    765             // cleanup test app just in case it already exists
    766             mDevice.uninstallPackage(pkgName);
    767             // grep for package to make sure its not installed
    768             assertFalse(doesPackageExist(pkgName));
    769         }
    770 
    771         String result = installFileForwardLocked(apkPath, overwrite);
    772         assertEquals(null, result);
    773         assertTrue(doesAppExistAsForwardLocked(pkgName));
    774         assertFalse(doesAppExistOnSDCard(pkgName));
    775         waitForPackageManager();
    776 
    777         // grep for package to make sure it is installed
    778         assertTrue(doesPackageExist(pkgName));
    779     }
    780 
    781     /**
    782      * Helper method for uninstalling an app.
    783      * <p/>
    784      * Assumes adb is running as root in device under test.
    785      *
    786      * @param pkgName package name to uninstall
    787      * @throws InterruptedException if the thread was interrupted
    788      * @throws TimeoutException in case of a timeout on the connection.
    789      * @throws AdbCommandRejectedException if adb rejects the command
    790      * @throws ShellCommandUnresponsiveException if the device did not output anything for
    791      * a period longer than the max time to output.
    792      * @throws IOException if connection to device was lost.
    793      * @throws InstallException if the uninstall failed.
    794      */
    795     public void uninstallApp(String pkgName) throws IOException, InterruptedException,
    796             InstallException, TimeoutException, AdbCommandRejectedException,
    797             ShellCommandUnresponsiveException {
    798         mDevice.uninstallPackage(pkgName);
    799         // make sure its not installed anymore
    800         assertFalse(doesPackageExist(pkgName));
    801     }
    802 
    803     /**
    804      * Helper method for clearing any installed non-system apps.
    805      * Useful ensuring no non-system apps are installed, and for cleaning up stale files that
    806      * may be lingering on the system for whatever reason.
    807      * <p/>
    808      * Assumes adb is running as root in device under test.
    809      *
    810      * @throws TimeoutException in case of a timeout on the connection.
    811      * @throws AdbCommandRejectedException if adb rejects the command
    812      * @throws ShellCommandUnresponsiveException if the device did not output anything for
    813      * a period longer than the max time to output.
    814      * @throws IOException if connection to device was lost.
    815      * @throws InstallException if the uninstall failed.
    816      */
    817     public void wipeNonSystemApps() throws IOException, TimeoutException,
    818             AdbCommandRejectedException, ShellCommandUnresponsiveException, InstallException {
    819       String allInstalledPackages = executeShellCommand("pm list packages -f");
    820       BufferedReader outputReader = new BufferedReader(new StringReader(allInstalledPackages));
    821 
    822       // First use Package Manager to uninstall all non-system apps
    823       String currentLine = null;
    824       while ((currentLine = outputReader.readLine()) != null) {
    825           // Skip over any system apps...
    826           if (currentLine.contains("/system/")) {
    827               continue;
    828           }
    829           String packageName = currentLine.substring(currentLine.indexOf('=') + 1);
    830           mDevice.uninstallPackage(packageName);
    831       }
    832       // Make sure there are no stale app files under these directories
    833       executeShellCommand(String.format("rm %s*", SDCARD_APP_PATH, "*"));
    834       executeShellCommand(String.format("rm %s*", DEVICE_APP_PATH, "*"));
    835       executeShellCommand(String.format("rm %s*", APP_PRIVATE_PATH, "*"));
    836     }
    837 
    838     /**
    839      * Sets the device's install location preference.
    840      *
    841      * <p/>
    842      * Assumes adb is running as root in device under test.
    843      * @throws TimeoutException in case of a timeout on the connection.
    844      * @throws AdbCommandRejectedException if adb rejects the command
    845      * @throws ShellCommandUnresponsiveException if the device did not output anything for
    846      * a period longer than the max time to output.
    847      * @throws IOException if connection to device was lost.
    848      */
    849     public void setDevicePreferredInstallLocation(InstallLocPreference pref) throws IOException,
    850             TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException {
    851         String command = "pm setInstallLocation %d";
    852         int locValue = 0;
    853         switch (pref) {
    854             case INTERNAL:
    855                 locValue = 1;
    856                 break;
    857             case EXTERNAL:
    858                 locValue = 2;
    859                 break;
    860             default: // AUTO
    861                 locValue = 0;
    862                 break;
    863         }
    864         executeShellCommand(String.format(command, locValue));
    865     }
    866 
    867     /**
    868      * Gets the device's install location preference.
    869      *
    870      * <p/>
    871      * Assumes adb is running as root in device under test.
    872      * @throws TimeoutException in case of a timeout on the connection.
    873      * @throws AdbCommandRejectedException if adb rejects the command
    874      * @throws ShellCommandUnresponsiveException if the device did not output anything for
    875      * a period longer than the max time to output.
    876      * @throws IOException if connection to device was lost.
    877      */
    878     public InstallLocPreference getDevicePreferredInstallLocation() throws IOException,
    879             TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException {
    880         String result = executeShellCommand("pm getInstallLocation");
    881         if (result.indexOf('0') != -1) {
    882             return InstallLocPreference.AUTO;
    883         }
    884         else if (result.indexOf('1') != -1) {
    885             return InstallLocPreference.INTERNAL;
    886         }
    887         else {
    888             return InstallLocPreference.EXTERNAL;
    889         }
    890     }
    891 }
    892