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 * @paramtrue</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