Home | History | Annotate | Download | only in appsecurity
      1 /*
      2  * Copyright (C) 2009 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.cts.appsecurity;
     18 
     19 import com.android.cts.tradefed.build.CtsBuildHelper;
     20 import com.android.ddmlib.Log;
     21 import com.android.ddmlib.testrunner.InstrumentationResultParser;
     22 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
     23 import com.android.ddmlib.testrunner.TestIdentifier;
     24 import com.android.tradefed.build.IBuildInfo;
     25 import com.android.tradefed.device.DeviceNotAvailableException;
     26 import com.android.tradefed.device.ITestDevice;
     27 import com.android.tradefed.result.CollectingTestListener;
     28 import com.android.tradefed.result.TestResult;
     29 import com.android.tradefed.result.TestResult.TestStatus;
     30 import com.android.tradefed.result.TestRunResult;
     31 import com.android.tradefed.testtype.DeviceTestCase;
     32 import com.android.tradefed.testtype.IBuildReceiver;
     33 
     34 import java.io.File;
     35 import java.io.FileNotFoundException;
     36 import java.util.Map;
     37 
     38 /**
     39  * Set of tests that verify various security checks involving multiple apps are properly enforced.
     40  */
     41 public class AppSecurityTests extends DeviceTestCase implements IBuildReceiver {
     42 
     43     // testSharedUidDifferentCerts constants
     44     private static final String SHARED_UI_APK = "CtsSharedUidInstall.apk";
     45     private static final String SHARED_UI_PKG = "com.android.cts.shareuidinstall";
     46     private static final String SHARED_UI_DIFF_CERT_APK = "CtsSharedUidInstallDiffCert.apk";
     47     private static final String SHARED_UI_DIFF_CERT_PKG =
     48         "com.android.cts.shareuidinstalldiffcert";
     49 
     50     // testAppUpgradeDifferentCerts constants
     51     private static final String SIMPLE_APP_APK = "CtsSimpleAppInstall.apk";
     52     private static final String SIMPLE_APP_PKG = "com.android.cts.simpleappinstall";
     53     private static final String SIMPLE_APP_DIFF_CERT_APK = "CtsSimpleAppInstallDiffCert.apk";
     54 
     55     // testAppFailAccessPrivateData constants
     56     private static final String APP_WITH_DATA_APK = "CtsAppWithData.apk";
     57     private static final String APP_WITH_DATA_PKG = "com.android.cts.appwithdata";
     58     private static final String APP_WITH_DATA_CLASS =
     59             "com.android.cts.appwithdata.CreatePrivateDataTest";
     60     private static final String APP_WITH_DATA_CREATE_METHOD =
     61             "testCreatePrivateData";
     62     private static final String APP_WITH_DATA_CHECK_NOEXIST_METHOD =
     63             "testEnsurePrivateDataNotExist";
     64     private static final String APP_ACCESS_DATA_APK = "CtsAppAccessData.apk";
     65     private static final String APP_ACCESS_DATA_PKG = "com.android.cts.appaccessdata";
     66 
     67     // External storage constants
     68     private static final String COMMON_EXTERNAL_STORAGE_APP_CLASS = "com.android.cts.externalstorageapp.CommonExternalStorageTest";
     69     private static final String EXTERNAL_STORAGE_APP_APK = "CtsExternalStorageApp.apk";
     70     private static final String EXTERNAL_STORAGE_APP_PKG = "com.android.cts.externalstorageapp";
     71     private static final String EXTERNAL_STORAGE_APP_CLASS = EXTERNAL_STORAGE_APP_PKG + ".ExternalStorageTest";
     72     private static final String READ_EXTERNAL_STORAGE_APP_APK = "CtsReadExternalStorageApp.apk";
     73     private static final String READ_EXTERNAL_STORAGE_APP_PKG = "com.android.cts.readexternalstorageapp";
     74     private static final String READ_EXTERNAL_STORAGE_APP_CLASS = READ_EXTERNAL_STORAGE_APP_PKG + ".ReadExternalStorageTest";
     75     private static final String WRITE_EXTERNAL_STORAGE_APP_APK = "CtsWriteExternalStorageApp.apk";
     76     private static final String WRITE_EXTERNAL_STORAGE_APP_PKG = "com.android.cts.writeexternalstorageapp";
     77     private static final String WRITE_EXTERNAL_STORAGE_APP_CLASS = WRITE_EXTERNAL_STORAGE_APP_PKG + ".WriteExternalStorageTest";
     78 
     79     // testInstrumentationDiffCert constants
     80     private static final String TARGET_INSTRUMENT_APK = "CtsTargetInstrumentationApp.apk";
     81     private static final String TARGET_INSTRUMENT_PKG = "com.android.cts.targetinstrumentationapp";
     82     private static final String INSTRUMENT_DIFF_CERT_APK = "CtsInstrumentationAppDiffCert.apk";
     83     private static final String INSTRUMENT_DIFF_CERT_PKG =
     84         "com.android.cts.instrumentationdiffcertapp";
     85 
     86     // testPermissionDiffCert constants
     87     private static final String DECLARE_PERMISSION_APK = "CtsPermissionDeclareApp.apk";
     88     private static final String DECLARE_PERMISSION_PKG = "com.android.cts.permissiondeclareapp";
     89     private static final String DECLARE_PERMISSION_COMPAT_APK = "CtsPermissionDeclareAppCompat.apk";
     90     private static final String DECLARE_PERMISSION_COMPAT_PKG = "com.android.cts.permissiondeclareappcompat";
     91 
     92     private static final String PERMISSION_DIFF_CERT_APK = "CtsUsePermissionDiffCert.apk";
     93     private static final String PERMISSION_DIFF_CERT_PKG =
     94         "com.android.cts.usespermissiondiffcertapp";
     95 
     96     private static final String READ_EXTERNAL_STORAGE = "android.permission.READ_EXTERNAL_STORAGE";
     97 
     98     private static final String MULTIUSER_STORAGE_APK = "CtsMultiUserStorageApp.apk";
     99     private static final String MULTIUSER_STORAGE_PKG = "com.android.cts.multiuserstorageapp";
    100     private static final String MULTIUSER_STORAGE_CLASS = MULTIUSER_STORAGE_PKG
    101             + ".MultiUserStorageTest";
    102 
    103     private static final String LOG_TAG = "AppSecurityTests";
    104 
    105     private CtsBuildHelper mCtsBuild;
    106 
    107     /**
    108      * {@inheritDoc}
    109      */
    110     @Override
    111     public void setBuild(IBuildInfo buildInfo) {
    112         mCtsBuild = CtsBuildHelper.createBuildHelper(buildInfo);
    113     }
    114 
    115     private File getTestAppFile(String fileName) throws FileNotFoundException {
    116         return mCtsBuild.getTestApp(fileName);
    117     }
    118 
    119     @Override
    120     protected void setUp() throws Exception {
    121         super.setUp();
    122         // ensure build has been set before test is run
    123         assertNotNull(mCtsBuild);
    124     }
    125 
    126     /**
    127      * Test that an app that declares the same shared uid as an existing app, cannot be installed
    128      * if it is signed with a different certificate.
    129      */
    130     public void testSharedUidDifferentCerts() throws Exception {
    131         Log.i(LOG_TAG, "installing apks with shared uid, but different certs");
    132         try {
    133             // cleanup test apps that might be installed from previous partial test run
    134             getDevice().uninstallPackage(SHARED_UI_PKG);
    135             getDevice().uninstallPackage(SHARED_UI_DIFF_CERT_PKG);
    136 
    137             String installResult = getDevice().installPackage(getTestAppFile(SHARED_UI_APK),
    138                     false);
    139             assertNull(String.format("failed to install shared uid app, Reason: %s", installResult),
    140                     installResult);
    141             installResult = getDevice().installPackage(getTestAppFile(SHARED_UI_DIFF_CERT_APK),
    142                     false);
    143             assertNotNull("shared uid app with different cert than existing app installed " +
    144                     "successfully", installResult);
    145             assertEquals("INSTALL_FAILED_SHARED_USER_INCOMPATIBLE", installResult);
    146         }
    147         finally {
    148             getDevice().uninstallPackage(SHARED_UI_PKG);
    149             getDevice().uninstallPackage(SHARED_UI_DIFF_CERT_PKG);
    150         }
    151     }
    152 
    153     /**
    154      * Test that an app update cannot be installed over an existing app if it has a different
    155      * certificate.
    156      */
    157     public void testAppUpgradeDifferentCerts() throws Exception {
    158         Log.i(LOG_TAG, "installing app upgrade with different certs");
    159         try {
    160             // cleanup test app that might be installed from previous partial test run
    161             getDevice().uninstallPackage(SIMPLE_APP_PKG);
    162 
    163             String installResult = getDevice().installPackage(getTestAppFile(SIMPLE_APP_APK),
    164                     false);
    165             assertNull(String.format("failed to install simple app. Reason: %s", installResult),
    166                     installResult);
    167             installResult = getDevice().installPackage(getTestAppFile(SIMPLE_APP_DIFF_CERT_APK),
    168                     true /* reinstall */);
    169             assertNotNull("app upgrade with different cert than existing app installed " +
    170                     "successfully", installResult);
    171             assertEquals("INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES", installResult);
    172         }
    173         finally {
    174             getDevice().uninstallPackage(SIMPLE_APP_PKG);
    175         }
    176     }
    177 
    178     /**
    179      * Test that an app cannot access another app's private data.
    180      */
    181     public void testAppFailAccessPrivateData() throws Exception {
    182         Log.i(LOG_TAG, "installing app that attempts to access another app's private data");
    183         try {
    184             // cleanup test app that might be installed from previous partial test run
    185             getDevice().uninstallPackage(APP_WITH_DATA_PKG);
    186             getDevice().uninstallPackage(APP_ACCESS_DATA_PKG);
    187 
    188             String installResult = getDevice().installPackage(getTestAppFile(APP_WITH_DATA_APK),
    189                     false);
    190             assertNull(String.format("failed to install app with data. Reason: %s", installResult),
    191                     installResult);
    192             // run appwithdata's tests to create private data
    193             assertTrue("failed to create app's private data", runDeviceTests(APP_WITH_DATA_PKG,
    194                     APP_WITH_DATA_CLASS, APP_WITH_DATA_CREATE_METHOD));
    195 
    196             installResult = getDevice().installPackage(getTestAppFile(APP_ACCESS_DATA_APK),
    197                     false);
    198             assertNull(String.format("failed to install app access data. Reason: %s",
    199                     installResult), installResult);
    200             // run appaccessdata's tests which attempt to access appwithdata's private data
    201             assertTrue("could access app's private data", runDeviceTests(APP_ACCESS_DATA_PKG));
    202         }
    203         finally {
    204             getDevice().uninstallPackage(APP_WITH_DATA_PKG);
    205             getDevice().uninstallPackage(APP_ACCESS_DATA_PKG);
    206         }
    207     }
    208 
    209     /**
    210      * Verify that app with no external storage permissions works correctly.
    211      */
    212     public void testExternalStorageNone() throws Exception {
    213         try {
    214             wipePrimaryExternalStorage(getDevice());
    215 
    216             getDevice().uninstallPackage(EXTERNAL_STORAGE_APP_PKG);
    217             assertNull(getDevice()
    218                     .installPackage(getTestAppFile(EXTERNAL_STORAGE_APP_APK), false));
    219             assertTrue("Failed external storage with no permissions",
    220                     runDeviceTests(EXTERNAL_STORAGE_APP_PKG));
    221         } finally {
    222             getDevice().uninstallPackage(EXTERNAL_STORAGE_APP_PKG);
    223         }
    224     }
    225 
    226     /**
    227      * Verify that app with
    228      * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} works
    229      * correctly.
    230      */
    231     public void testExternalStorageRead() throws Exception {
    232         try {
    233             wipePrimaryExternalStorage(getDevice());
    234 
    235             getDevice().uninstallPackage(READ_EXTERNAL_STORAGE_APP_PKG);
    236             assertNull(getDevice()
    237                     .installPackage(getTestAppFile(READ_EXTERNAL_STORAGE_APP_APK), false));
    238             assertTrue("Failed external storage with read permissions",
    239                     runDeviceTests(READ_EXTERNAL_STORAGE_APP_PKG));
    240         } finally {
    241             getDevice().uninstallPackage(READ_EXTERNAL_STORAGE_APP_PKG);
    242         }
    243     }
    244 
    245     /**
    246      * Verify that app with
    247      * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} works
    248      * correctly.
    249      */
    250     public void testExternalStorageWrite() throws Exception {
    251         try {
    252             wipePrimaryExternalStorage(getDevice());
    253 
    254             getDevice().uninstallPackage(WRITE_EXTERNAL_STORAGE_APP_PKG);
    255             assertNull(getDevice()
    256                     .installPackage(getTestAppFile(WRITE_EXTERNAL_STORAGE_APP_APK), false));
    257             assertTrue("Failed external storage with write permissions",
    258                     runDeviceTests(WRITE_EXTERNAL_STORAGE_APP_PKG));
    259         } finally {
    260             getDevice().uninstallPackage(WRITE_EXTERNAL_STORAGE_APP_PKG);
    261         }
    262     }
    263 
    264     /**
    265      * Verify that app with WRITE_EXTERNAL can leave gifts in external storage
    266      * directories belonging to other apps, and those apps can read.
    267      */
    268     public void testExternalStorageGifts() throws Exception {
    269         try {
    270             wipePrimaryExternalStorage(getDevice());
    271 
    272             getDevice().uninstallPackage(EXTERNAL_STORAGE_APP_PKG);
    273             getDevice().uninstallPackage(READ_EXTERNAL_STORAGE_APP_PKG);
    274             getDevice().uninstallPackage(WRITE_EXTERNAL_STORAGE_APP_PKG);
    275             assertNull(getDevice()
    276                     .installPackage(getTestAppFile(EXTERNAL_STORAGE_APP_APK), false));
    277             assertNull(getDevice()
    278                     .installPackage(getTestAppFile(READ_EXTERNAL_STORAGE_APP_APK), false));
    279             assertNull(getDevice()
    280                     .installPackage(getTestAppFile(WRITE_EXTERNAL_STORAGE_APP_APK), false));
    281 
    282             assertTrue("Failed to write gifts", runDeviceTests(WRITE_EXTERNAL_STORAGE_APP_PKG,
    283                     WRITE_EXTERNAL_STORAGE_APP_CLASS, "doWriteGifts"));
    284 
    285             assertTrue("Read failed to verify gifts", runDeviceTests(READ_EXTERNAL_STORAGE_APP_PKG,
    286                     READ_EXTERNAL_STORAGE_APP_CLASS, "doVerifyGifts"));
    287             assertTrue("None failed to verify gifts", runDeviceTests(EXTERNAL_STORAGE_APP_PKG,
    288                     EXTERNAL_STORAGE_APP_CLASS, "doVerifyGifts"));
    289 
    290         } finally {
    291             getDevice().uninstallPackage(EXTERNAL_STORAGE_APP_PKG);
    292             getDevice().uninstallPackage(READ_EXTERNAL_STORAGE_APP_PKG);
    293             getDevice().uninstallPackage(WRITE_EXTERNAL_STORAGE_APP_PKG);
    294         }
    295     }
    296 
    297     /**
    298      * Test that uninstall of an app removes its private data.
    299      */
    300     public void testUninstallRemovesData() throws Exception {
    301         Log.i(LOG_TAG, "Uninstalling app, verifying data is removed.");
    302         try {
    303             // cleanup test app that might be installed from previous partial test run
    304             getDevice().uninstallPackage(APP_WITH_DATA_PKG);
    305 
    306             String installResult = getDevice().installPackage(getTestAppFile(APP_WITH_DATA_APK),
    307                     false);
    308             assertNull(String.format("failed to install app with data. Reason: %s", installResult),
    309                     installResult);
    310             // run appwithdata's tests to create private data
    311             assertTrue("failed to create app's private data", runDeviceTests(APP_WITH_DATA_PKG,
    312                     APP_WITH_DATA_CLASS, APP_WITH_DATA_CREATE_METHOD));
    313 
    314             getDevice().uninstallPackage(APP_WITH_DATA_PKG);
    315 
    316             installResult = getDevice().installPackage(getTestAppFile(APP_WITH_DATA_APK),
    317                     false);
    318             assertNull(String.format("failed to install app with data second time. Reason: %s",
    319                     installResult), installResult);
    320             // run appwithdata's 'check if file exists' test
    321             assertTrue("app's private data still exists after install", runDeviceTests(
    322                     APP_WITH_DATA_PKG, APP_WITH_DATA_CLASS, APP_WITH_DATA_CHECK_NOEXIST_METHOD));
    323 
    324         }
    325         finally {
    326             getDevice().uninstallPackage(APP_WITH_DATA_PKG);
    327         }
    328     }
    329 
    330     /**
    331      * Test that an app cannot instrument another app that is signed with different certificate.
    332      */
    333     public void testInstrumentationDiffCert() throws Exception {
    334         Log.i(LOG_TAG, "installing app that attempts to instrument another app");
    335         try {
    336             // cleanup test app that might be installed from previous partial test run
    337             getDevice().uninstallPackage(TARGET_INSTRUMENT_PKG);
    338             getDevice().uninstallPackage(INSTRUMENT_DIFF_CERT_PKG);
    339 
    340             String installResult = getDevice().installPackage(
    341                     getTestAppFile(TARGET_INSTRUMENT_APK), false);
    342             assertNull(String.format("failed to install target instrumentation app. Reason: %s",
    343                     installResult), installResult);
    344 
    345             // the app will install, but will get error at runtime when starting instrumentation
    346             installResult = getDevice().installPackage(getTestAppFile(INSTRUMENT_DIFF_CERT_APK),
    347                     false);
    348             assertNull(String.format(
    349                     "failed to install instrumentation app with diff cert. Reason: %s",
    350                     installResult), installResult);
    351             // run INSTRUMENT_DIFF_CERT_PKG tests
    352             // this test will attempt to call startInstrumentation directly and verify
    353             // SecurityException is thrown
    354             assertTrue("running instrumentation with diff cert unexpectedly succeeded",
    355                     runDeviceTests(INSTRUMENT_DIFF_CERT_PKG));
    356         }
    357         finally {
    358             getDevice().uninstallPackage(TARGET_INSTRUMENT_PKG);
    359             getDevice().uninstallPackage(INSTRUMENT_DIFF_CERT_PKG);
    360         }
    361     }
    362 
    363     /**
    364      * Test that an app cannot use a signature-enforced permission if it is signed with a different
    365      * certificate than the app that declared the permission.
    366      */
    367     public void testPermissionDiffCert() throws Exception {
    368         Log.i(LOG_TAG, "installing app that attempts to use permission of another app");
    369         try {
    370             // cleanup test app that might be installed from previous partial test run
    371             getDevice().uninstallPackage(DECLARE_PERMISSION_PKG);
    372             getDevice().uninstallPackage(DECLARE_PERMISSION_COMPAT_PKG);
    373             getDevice().uninstallPackage(PERMISSION_DIFF_CERT_PKG);
    374 
    375             String installResult = getDevice().installPackage(
    376                     getTestAppFile(DECLARE_PERMISSION_APK), false);
    377             assertNull(String.format("failed to install declare permission app. Reason: %s",
    378                     installResult), installResult);
    379 
    380             installResult = getDevice().installPackage(
    381                     getTestAppFile(DECLARE_PERMISSION_COMPAT_APK), false);
    382             assertNull(String.format("failed to install declare permission compat app. Reason: %s",
    383                     installResult), installResult);
    384 
    385             // the app will install, but will get error at runtime
    386             installResult = getDevice().installPackage(getTestAppFile(PERMISSION_DIFF_CERT_APK),
    387                     false);
    388             assertNull(String.format("failed to install permission app with diff cert. Reason: %s",
    389                     installResult), installResult);
    390             // run PERMISSION_DIFF_CERT_PKG tests which try to access the permission
    391             TestRunResult result = doRunTests(PERMISSION_DIFF_CERT_PKG, null, null);
    392             assertDeviceTestsPass(result);
    393         }
    394         finally {
    395             getDevice().uninstallPackage(DECLARE_PERMISSION_PKG);
    396             getDevice().uninstallPackage(DECLARE_PERMISSION_COMPAT_PKG);
    397             getDevice().uninstallPackage(PERMISSION_DIFF_CERT_PKG);
    398         }
    399     }
    400 
    401     /**
    402      * Test multi-user emulated storage environment, ensuring that each user has
    403      * isolated storage.
    404      */
    405     public void testMultiUserStorage() throws Exception {
    406         final String PACKAGE = MULTIUSER_STORAGE_PKG;
    407         final String CLAZZ = MULTIUSER_STORAGE_CLASS;
    408 
    409         if (!isMultiUserSupportedOnDevice(getDevice())) {
    410             Log.d(LOG_TAG, "Single user device; skipping isolated storage tests");
    411             return;
    412         }
    413 
    414         int owner = 0;
    415         int secondary = -1;
    416         try {
    417             // Create secondary user
    418             secondary = createUserOnDevice(getDevice());
    419 
    420             // Install our test app
    421             getDevice().uninstallPackage(MULTIUSER_STORAGE_PKG);
    422             final String installResult = getDevice()
    423                     .installPackage(getTestAppFile(MULTIUSER_STORAGE_APK), false);
    424             assertNull("Failed to install: " + installResult, installResult);
    425 
    426             // Clear data from previous tests
    427             assertDeviceTestsPass(
    428                     doRunTestsAsUser(PACKAGE, CLAZZ, "cleanIsolatedStorage", owner));
    429             assertDeviceTestsPass(
    430                     doRunTestsAsUser(PACKAGE, CLAZZ, "cleanIsolatedStorage", secondary));
    431 
    432             // Have both users try writing into isolated storage
    433             assertDeviceTestsPass(
    434                     doRunTestsAsUser(PACKAGE, CLAZZ, "writeIsolatedStorage", owner));
    435             assertDeviceTestsPass(
    436                     doRunTestsAsUser(PACKAGE, CLAZZ, "writeIsolatedStorage", secondary));
    437 
    438             // Verify they both have isolated view of storage
    439             assertDeviceTestsPass(
    440                     doRunTestsAsUser(PACKAGE, CLAZZ, "readIsolatedStorage", owner));
    441             assertDeviceTestsPass(
    442                     doRunTestsAsUser(PACKAGE, CLAZZ, "readIsolatedStorage", secondary));
    443         } finally {
    444             getDevice().uninstallPackage(MULTIUSER_STORAGE_PKG);
    445             if (secondary != -1) {
    446                 removeUserOnDevice(getDevice(), secondary);
    447             }
    448         }
    449     }
    450 
    451     /**
    452      * Helper method that checks that all tests in given result passed, and attempts to generate
    453      * a meaningful error message if they failed.
    454      *
    455      * @param result
    456      */
    457     private void assertDeviceTestsPass(TestRunResult result) {
    458         // TODO: consider rerunning if this occurred
    459         assertFalse(String.format("Failed to successfully run device tests for %s. Reason: %s",
    460                 result.getName(), result.getRunFailureMessage()), result.isRunFailure());
    461 
    462         if (result.hasFailedTests()) {
    463             // build a meaningful error message
    464             StringBuilder errorBuilder = new StringBuilder("on-device tests failed:\n");
    465             for (Map.Entry<TestIdentifier, TestResult> resultEntry :
    466                 result.getTestResults().entrySet()) {
    467                 if (!resultEntry.getValue().getStatus().equals(TestStatus.PASSED)) {
    468                     errorBuilder.append(resultEntry.getKey().toString());
    469                     errorBuilder.append(":\n");
    470                     errorBuilder.append(resultEntry.getValue().getStackTrace());
    471                 }
    472             }
    473             fail(errorBuilder.toString());
    474         }
    475     }
    476 
    477     /**
    478      * Helper method that will the specified packages tests on device.
    479      *
    480      * @param pkgName Android application package for tests
    481      * @return <code>true</code> if all tests passed.
    482      * @throws DeviceNotAvailableException if connection to device was lost.
    483      */
    484     private boolean runDeviceTests(String pkgName) throws DeviceNotAvailableException {
    485         return runDeviceTests(pkgName, null, null);
    486     }
    487 
    488     /**
    489      * Helper method that will the specified packages tests on device.
    490      *
    491      * @param pkgName Android application package for tests
    492      * @return <code>true</code> if all tests passed.
    493      * @throws DeviceNotAvailableException if connection to device was lost.
    494      */
    495     private boolean runDeviceTests(String pkgName, String testClassName, String testMethodName)
    496             throws DeviceNotAvailableException {
    497         TestRunResult runResult = doRunTests(pkgName, testClassName, testMethodName);
    498         return !runResult.hasFailedTests();
    499     }
    500 
    501     /**
    502      * Helper method to run tests and return the listener that collected the results.
    503      *
    504      * @param pkgName Android application package for tests
    505      * @return the {@link TestRunResult}
    506      * @throws DeviceNotAvailableException if connection to device was lost.
    507      */
    508     private TestRunResult doRunTests(String pkgName, String testClassName,
    509             String testMethodName) throws DeviceNotAvailableException {
    510 
    511         RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(pkgName,
    512                 getDevice().getIDevice());
    513         if (testClassName != null && testMethodName != null) {
    514             testRunner.setMethodName(testClassName, testMethodName);
    515         }
    516         CollectingTestListener listener = new CollectingTestListener();
    517         getDevice().runInstrumentationTests(testRunner, listener);
    518         return listener.getCurrentRunResults();
    519     }
    520 
    521     private static boolean isMultiUserSupportedOnDevice(ITestDevice device)
    522             throws DeviceNotAvailableException {
    523         // TODO: move this to ITestDevice once it supports users
    524         final String output = device.executeShellCommand("pm get-max-users");
    525         try {
    526             return Integer.parseInt(output.substring(output.lastIndexOf(" ")).trim()) > 1;
    527         } catch (NumberFormatException e) {
    528             fail("Failed to parse result: " + output);
    529         }
    530         return false;
    531     }
    532 
    533     private static int createUserOnDevice(ITestDevice device) throws DeviceNotAvailableException {
    534         // TODO: move this to ITestDevice once it supports users
    535         final String name = "CTS_" + System.currentTimeMillis();
    536         final String output = device.executeShellCommand("pm create-user " + name);
    537         if (output.startsWith("Success")) {
    538             try {
    539                 return Integer.parseInt(output.substring(output.lastIndexOf(" ")).trim());
    540             } catch (NumberFormatException e) {
    541                 fail("Failed to parse result: " + output);
    542             }
    543         } else {
    544             fail("Failed to create user: " + output);
    545         }
    546         throw new IllegalStateException();
    547     }
    548 
    549     private static void removeUserOnDevice(ITestDevice device, int userId)
    550             throws DeviceNotAvailableException {
    551         // TODO: move this to ITestDevice once it supports users
    552         final String output = device.executeShellCommand("pm remove-user " + userId);
    553         if (output.startsWith("Error")) {
    554             fail("Failed to remove user: " + output);
    555         }
    556     }
    557 
    558     private TestRunResult doRunTestsAsUser(
    559             String pkgName, String testClassName, String testMethodName, int userId)
    560             throws DeviceNotAvailableException {
    561         // TODO: move this to RemoteAndroidTestRunner once it supports users
    562         final String cmd = "am instrument --user " + userId + " -w -r -e class " + testClassName
    563                 + "#" + testMethodName + " " + pkgName + "/android.test.InstrumentationTestRunner";
    564         Log.i(LOG_TAG, "Running " + cmd + " on " + getDevice().getSerialNumber());
    565 
    566         CollectingTestListener listener = new CollectingTestListener();
    567         InstrumentationResultParser parser = new InstrumentationResultParser(pkgName, listener);
    568 
    569         getDevice().executeShellCommand(cmd, parser);
    570         return listener.getCurrentRunResults();
    571     }
    572 
    573     private static void wipePrimaryExternalStorage(ITestDevice device)
    574             throws DeviceNotAvailableException {
    575         device.executeShellCommand("rm -rf /sdcard/*");
    576     }
    577 }
    578