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