1 /* 2 * Copyright (C) 2008 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; 18 19 import com.android.ddmlib.Client; 20 import com.android.ddmlib.ClientData; 21 import com.android.ddmlib.IDevice; 22 import com.android.ddmlib.IShellOutputReceiver; 23 import com.android.ddmlib.MultiLineReceiver; 24 import com.android.ddmlib.NullOutputReceiver; 25 import com.android.ddmlib.RawImage; 26 import com.android.ddmlib.SyncService; 27 import com.android.ddmlib.SyncService.ISyncProgressMonitor; 28 import com.android.ddmlib.SyncService.SyncResult; 29 import com.android.ddmlib.log.LogReceiver; 30 import com.android.ddmlib.log.LogReceiver.ILogListener; 31 32 import java.io.BufferedReader; 33 import java.io.IOException; 34 import java.io.InputStream; 35 import java.io.InputStreamReader; 36 import java.util.ArrayList; 37 import java.util.Collection; 38 import java.util.HashMap; 39 import java.util.Iterator; 40 import java.util.Timer; 41 import java.util.TimerTask; 42 import java.util.regex.Matcher; 43 import java.util.regex.Pattern; 44 45 /** 46 * Manage the testing target device for<br> 47 * <ul> 48 * <li> install/uninstall test package, and 49 * <li> execute command on device 50 * <li> get command feedback from standard output 51 * </ul> 52 */ 53 public class TestDevice implements DeviceObserver { 54 private static final String DEVICE_SETUP_APK = "TestDeviceSetup"; 55 private static final String DEVICE_SETUP_APP_PACKAGE_NAME = "android.tests.devicesetup"; 56 private static final String DEFAULT_TEST_RUNNER_NAME = 57 "android.test.InstrumentationTestRunner"; 58 private static final String ACTION_INSTALL = "install"; 59 private static final String ACTION_UNINSTALL = "uninstall"; 60 private static final String ACTION_GET_DEV_INFO = "getDeviceInfo"; 61 private static final String sInstrumentResultExpr = "INSTRUMENTATION_RESULT: (\\S+)=(.+)"; 62 63 public static final int STATUS_IDLE = 0; 64 public static final int STATUS_BUSY = STATUS_IDLE + 1; 65 public static final int STATUS_OFFLINE = STATUS_IDLE + 2; 66 private static final String STATUS_STR_IDLE = "idle"; 67 private static final String STATUS_STR_IN_USE = "in use"; 68 private static final String STATUS_STR_OFFLINE = "offline"; 69 70 /** Interval [ms] for polling a device until boot is completed. */ 71 private static final int REBOOT_POLL_INTERVAL = 5000; 72 /** Number of times a booting device should be polled before we give up. */ 73 private static final int REBOOT_POLL_COUNT = 10 * 60 * 1000 / REBOOT_POLL_INTERVAL; 74 /** Max time [ms] to wait for <code>adb shell getprop</code> to return a result. */ 75 private static final int GETPROP_TIMEOUT = 5000; 76 77 public static final Pattern INSTRUMENT_RESULT_PATTERN; 78 79 private BatchModeResultParser mBatchModeResultParser; 80 81 private DeviceObserver mDeviceObserver; 82 private IDevice mDevice; 83 private DeviceParameterCollector mDeviceInfo; 84 85 private SyncService mSyncService; 86 87 private PackageActionObserver mUninstallObserver; 88 89 private int mStatus; 90 private static HashMap<Integer, String> mStatusMap; 91 private PackageActionTimer mPackageActionTimer; 92 93 private ObjectSync mObjectSync; 94 95 private MultiplexingLogListener logListener = new MultiplexingLogListener(); 96 private LogReceiver logReceiver = new LogReceiver(logListener); 97 98 private class LogServiceThread extends Thread { 99 @Override 100 public void run() { 101 try { 102 mDevice.runLogService("main", logReceiver); 103 } catch (IOException e) { 104 } 105 } 106 107 /** 108 * Cancel logging and exit this thread. 109 */ 110 public void cancelLogService() { 111 // this will cause the loop in our run method to 112 // exit, terminating this thread. 113 logReceiver.cancel(); 114 } 115 } 116 117 private LogServiceThread logServiceThread; 118 119 static { 120 INSTRUMENT_RESULT_PATTERN = Pattern.compile(sInstrumentResultExpr); 121 mStatusMap = new HashMap<Integer, String>(); 122 mStatusMap.put(STATUS_IDLE, STATUS_STR_IDLE); 123 mStatusMap.put(STATUS_BUSY, STATUS_STR_IN_USE); 124 mStatusMap.put(STATUS_OFFLINE, STATUS_STR_OFFLINE); 125 } 126 127 // This constructor just for unit test 128 TestDevice(final String serialNumber) { 129 mDeviceInfo = new DeviceParameterCollector(); 130 mDeviceInfo.setSerialNumber(serialNumber); 131 } 132 133 public TestDevice(IDevice device) { 134 mDevice = device; 135 try { 136 mSyncService = mDevice.getSyncService(); 137 } catch (IOException e) { 138 // FIXME: handle failed connection. 139 } 140 mBatchModeResultParser = null; 141 mUninstallObserver = new PackageActionObserver(ACTION_UNINSTALL); 142 mStatus = STATUS_IDLE; 143 mDeviceInfo = new DeviceParameterCollector(); 144 mPackageActionTimer = new PackageActionTimer(); 145 mObjectSync = new ObjectSync(); 146 } 147 148 /** 149 * Gets this device's information. 150 * 151 * Assumes that the test device setup apk is already installed. 152 * See {@link #installDeviceSetupApp()}. 153 * 154 * @return information of this device. 155 */ 156 public DeviceParameterCollector getDeviceInfo() 157 throws DeviceDisconnectedException, InvalidNameSpaceException, 158 InvalidApkPathException { 159 if (mDeviceInfo.size() == 0) { 160 logServiceThread = new LogServiceThread(); 161 logServiceThread.start(); 162 genDeviceInfo(); 163 } 164 return mDeviceInfo; 165 } 166 167 /** 168 * Attempt to disable the screen guard on device. 169 * 170 * Assumes the test device setup apk is already installed. 171 * See {@link #installDeviceSetupApp()}. 172 * 173 * Note: uninstalling the device setup app {@link #uninstallDeviceSetupApp()} will re-enable 174 * keyguard. 175 * 176 * @throws DeviceDisconnectedException 177 */ 178 public void disableKeyguard () throws DeviceDisconnectedException { 179 final String commandStr = "am broadcast -a android.tests.util.disablekeyguard"; 180 Log.d(commandStr); 181 182 executeShellCommand(commandStr, new NullOutputReceiver()); 183 } 184 185 /** 186 * Return the Device instance associated with this TestDevice. 187 */ 188 public IDevice getDevice() { 189 return mDevice; 190 } 191 192 class RestartPropReceiver extends MultiLineReceiver { 193 private boolean mRestarted; 194 private boolean mCancelled; 195 private boolean mDone; 196 197 @Override 198 public void processNewLines(String[] lines) { 199 for (String line : lines) { 200 if (line.trim().equals("1")) { 201 mRestarted = true; 202 } 203 } 204 } 205 @Override 206 public void done() { 207 synchronized(this) { 208 mDone = true; 209 this.notifyAll(); 210 } 211 } 212 213 public boolean isCancelled() { 214 return mCancelled; 215 } 216 217 boolean hasRestarted(long timeout) { 218 try { 219 synchronized (this) { 220 if (!mDone) { 221 this.wait(timeout); 222 } 223 } 224 } catch (InterruptedException e) { 225 // ignore 226 } 227 mCancelled = true; 228 return mRestarted; 229 } 230 } 231 232 /** 233 * Wait until device indicates that boot is complete. 234 * 235 * @return true if the device has completed the boot process, false if it does not, or the 236 * device does not respond. 237 */ 238 public boolean waitForBootComplete() throws DeviceDisconnectedException { 239 Log.d("probe device status..."); 240 241 mDeviceInfo.set(DeviceParameterCollector.SERIAL_NUMBER, getSerialNumber()); 242 mObjectSync = new ObjectSync(); 243 244 // reset device observer 245 DeviceObserver tmpDeviceObserver = mDeviceObserver; 246 mDeviceObserver = this; 247 248 int retries = 0; 249 boolean success = false; 250 while (!success && (retries < REBOOT_POLL_COUNT)) { 251 Log.d("Waiting for device to complete boot"); 252 RestartPropReceiver rpr = new RestartPropReceiver(); 253 this.executeShellCommand("getprop dev.bootcomplete", rpr); 254 success = rpr.hasRestarted(GETPROP_TIMEOUT); 255 if (!success) { 256 try { 257 Thread.sleep(REBOOT_POLL_INTERVAL); 258 } catch (InterruptedException e) { 259 // ignore and retry 260 } 261 retries += 1; 262 } 263 } 264 mDeviceObserver = tmpDeviceObserver; 265 if (success) { 266 Log.d("Device boot complete"); 267 } 268 return success; 269 } 270 271 /** 272 * Run device information collector command to got the device info. 273 */ 274 private void genDeviceInfo() throws DeviceDisconnectedException, 275 InvalidNameSpaceException, InvalidApkPathException { 276 mDeviceInfo.set(DeviceParameterCollector.SERIAL_NUMBER, getSerialNumber()); 277 // run shell command to run device information collector 278 Log.d("run device information collector"); 279 runDeviceInfoCollectorCommand(); 280 waitForCommandFinish(); 281 } 282 283 /** 284 * Uninstall the device setup apk from device. 285 * 286 * See {@link #installDeviceSetupApp} 287 * 288 * @throws DeviceDisconnectedException 289 * @throws InvalidNameSpaceException 290 */ 291 public void uninstallDeviceSetupApp() throws DeviceDisconnectedException, 292 InvalidNameSpaceException { 293 // reset device observer 294 DeviceObserver tmpDeviceObserver = mDeviceObserver; 295 mDeviceObserver = this; 296 Log.d("uninstall get info ..."); 297 uninstallAPK(DEVICE_SETUP_APP_PACKAGE_NAME); 298 waitForCommandFinish(); 299 Log.d("uninstall device information collector successfully"); 300 mDeviceObserver = tmpDeviceObserver; 301 } 302 303 /** 304 * Install the device setup apk on the device. 305 * 306 * @throws DeviceDisconnectedException 307 * @throws InvalidApkPathException 308 */ 309 public void installDeviceSetupApp() throws DeviceDisconnectedException, InvalidApkPathException { 310 String apkPath = HostConfig.getInstance().getCaseRepository().getApkPath(DEVICE_SETUP_APK); 311 if (!HostUtils.isFileExist(apkPath)) { 312 Log.e("File doesn't exist: " + apkPath, null); 313 return; 314 } 315 316 Log.d("installing " + DEVICE_SETUP_APK + " apk"); 317 mObjectSync = new ObjectSync(); 318 319 // reset device observer 320 DeviceObserver tmpDeviceObserver = mDeviceObserver; 321 mDeviceObserver = this; 322 323 Log.d("install get info ..."); 324 installAPK(apkPath); 325 waitForCommandFinish(); 326 mDeviceObserver = tmpDeviceObserver; 327 } 328 329 /** 330 * Run command to collect device info. 331 */ 332 private void runDeviceInfoCollectorCommand() throws DeviceDisconnectedException { 333 final String commandStr = "am instrument -w -e bundle true " 334 + String.format("%s/android.tests.getinfo.DeviceInfoInstrument", 335 DEVICE_SETUP_APP_PACKAGE_NAME); 336 Log.d(commandStr); 337 338 mPackageActionTimer.start(ACTION_GET_DEV_INFO, this); 339 executeShellCommand(commandStr, new DeviceInfoReceiver(mDeviceInfo)); 340 } 341 342 /** 343 * Receiver which receives and parses the device information. 344 */ 345 final class DeviceInfoReceiver extends MultiLineReceiver { 346 347 private ArrayList<String> mResultLines = new ArrayList<String>(); 348 private DeviceParameterCollector mDeviceParamCollector; 349 350 public DeviceInfoReceiver(DeviceParameterCollector paramCollector) { 351 super(); 352 mDeviceParamCollector = paramCollector; 353 setTrimLine(false); 354 } 355 356 /** {@inheritDoc} */ 357 @Override 358 public void processNewLines(String[] lines) { 359 for (String line : lines) { 360 mResultLines.add(line); 361 } 362 } 363 364 /** {@inheritDoc} */ 365 public boolean isCancelled() { 366 return false; 367 } 368 369 /** {@inheritDoc} */ 370 @Override 371 public void done() { 372 super.done(); 373 String key, value; 374 for (String line : mResultLines) { 375 Matcher matcher = INSTRUMENT_RESULT_PATTERN.matcher(line); 376 if (matcher.matches()) { 377 key = matcher.group(1); 378 value = matcher.group(2); 379 mDeviceParamCollector.set(key, value); 380 } 381 } 382 383 synchronized(mObjectSync) { 384 mObjectSync.sendNotify(); 385 } 386 } 387 388 } 389 390 /** 391 * Store the build information of a device 392 */ 393 public static final class DeviceParameterCollector{ 394 public static final String PRODUCT_NAME = "product_name"; 395 public static final String BUILD_VERSION = "version_release"; 396 public static final String BUILD_ID = "build_id"; 397 public static final String BUILD_FINGERPRINT = "build_fingerprint"; 398 public static final String BUILD_TAGS = "build_tags"; 399 public static final String BUILD_TYPE = "build_type"; 400 public static final String BUILD_MODEL = "build_model"; 401 public static final String BUILD_BRAND = "build_brand"; 402 public static final String BUILD_BOARD = "build_board"; 403 public static final String BUILD_DEVICE = "build_device"; 404 public static final String BUILD_ABI = "build_abi"; 405 public static final String BUILD_ABI2 = "build_abi2"; 406 public static final String SCREEN_HEIGHT = "screen_height"; 407 public static final String SCREEN_WIDTH = "screen_width"; 408 public static final String SCREEN_DENSITY = "screen_density"; 409 public static final String SERIAL_NUMBER = "serialNumber"; 410 public static final String VERSION_SDK = "version_sdk"; 411 public static final String LOCALES = "locales"; 412 public static final String SCREEN_Y_DENSITY = "screen_Y_density"; 413 public static final String SCREEN_X_DENSITY = "screen_X_density"; 414 public static final String TOUCH_SCREEN = "touch_screen"; 415 public static final String NAVIGATION = "navigation"; 416 public static final String KEYPAD = "keypad"; 417 public static final String NETWORK = "network"; 418 public static final String IMEI = "imei"; 419 public static final String IMSI = "imsi"; 420 public static final String PHONE_NUMBER = "phoneNumber"; 421 422 private HashMap<String, String> mInfoMap; 423 424 public DeviceParameterCollector() { 425 mInfoMap = new HashMap<String, String>(); 426 } 427 428 /** 429 * Set the pair of key and value of device information. 430 * 431 * @param key The key of the pair. 432 * @param value The value of the pair. 433 */ 434 public void set(final String key, final String value) { 435 mInfoMap.put(key, value); 436 } 437 438 /** 439 * Return the number of device info items which stored in. 440 * 441 * @return the number of device info items which stored in. 442 */ 443 public int size() { 444 return mInfoMap.size(); 445 } 446 447 /** 448 * Set the build finger print. 449 * 450 * @param buildFingerPrint The build finger print. 451 */ 452 public void setBuildFingerPrint(final String buildFingerPrint) { 453 mInfoMap.put(BUILD_FINGERPRINT, buildFingerPrint); 454 } 455 456 /** 457 * Set the build tags. 458 * 459 * @param buildTags The build tags. 460 */ 461 public void setBuildTags(final String buildTags) { 462 mInfoMap.put(BUILD_TAGS, buildTags); 463 } 464 465 /** 466 * Set build type. 467 * 468 * @param buildType The build type. 469 */ 470 public void setBuildType(final String buildType) { 471 mInfoMap.put(BUILD_TYPE, buildType); 472 } 473 474 /** 475 * Set the build model. 476 * 477 * @param buildModel The build model. 478 */ 479 public void setBuildModel(final String buildModel) { 480 mInfoMap.put(BUILD_MODEL, buildModel); 481 } 482 483 /** 484 * Set the build brand. 485 * 486 * @param buildBrand The build brand. 487 */ 488 public void setBuildBrand(final String buildBrand) { 489 mInfoMap.put(BUILD_BRAND, buildBrand); 490 } 491 492 /** 493 * Set the build board. 494 * 495 * @param buildBoard The build board. 496 */ 497 public void setBuildBoard(final String buildBoard) { 498 mInfoMap.put(BUILD_BOARD, buildBoard); 499 } 500 501 /** 502 * Set the build device. 503 * 504 * @param buildDevice The build device. 505 */ 506 public void setBuildDevice(final String buildDevice) { 507 mInfoMap.put(BUILD_DEVICE, buildDevice); 508 } 509 510 /** 511 * Set the build abi 512 * 513 * @param buildAbi The build ABI 514 */ 515 public void setBuildAbi(final String buildAbi) { 516 mInfoMap.put(BUILD_ABI, buildAbi); 517 } 518 519 /** 520 * Set the build abi2 521 * 522 * @param buildAbi The build ABI2 523 */ 524 public void setBuildAbi2(final String buildAbi2) { 525 mInfoMap.put(BUILD_ABI2, buildAbi2); 526 } 527 528 /** 529 * set the serialNumber of this device 530 * 531 * @param serialNumber The serial number. 532 */ 533 public void setSerialNumber(final String serialNumber) { 534 mInfoMap.put(SERIAL_NUMBER, serialNumber); 535 } 536 537 /** 538 * set the build id 539 * 540 * @param bldId The build ID. 541 */ 542 public void setBuildId(final String bldId) { 543 mInfoMap.put(BUILD_ID, bldId); 544 } 545 546 /** 547 * set the build version 548 * 549 * @param bldVer The build version. 550 */ 551 public void setBuildVersion(final String bldVer) { 552 mInfoMap.put(BUILD_VERSION, bldVer); 553 } 554 555 /** 556 * set the product name 557 ** 558 * @param productName The product name. 559 */ 560 public void setProductName(final String productName) { 561 mInfoMap.put(PRODUCT_NAME, productName); 562 } 563 564 /** 565 * Get the build finger print. 566 * 567 * @return The build finger print. 568 */ 569 public String getBuildFingerPrint() { 570 return mInfoMap.get(BUILD_FINGERPRINT); 571 } 572 573 /** 574 * Get the build tags. 575 * 576 * @return The build tags. 577 */ 578 public String getBuildTags() { 579 return mInfoMap.get(BUILD_TAGS); 580 } 581 582 /** 583 * Get build type. 584 * 585 * @return The build type. 586 */ 587 public String getBuildType() { 588 return mInfoMap.get(BUILD_TYPE); 589 } 590 591 /** 592 * Get the build model. 593 * 594 * @return The build model. 595 */ 596 public String getBuildModel() { 597 return mInfoMap.get(BUILD_MODEL); 598 } 599 600 /** 601 * Get the build brand. 602 * 603 * @return The build brand. 604 */ 605 public String getBuildBrand() { 606 return mInfoMap.get(BUILD_BRAND); 607 } 608 609 /** 610 * Get the build board. 611 * 612 * @return The build board. 613 */ 614 public String getBuildBoard() { 615 return mInfoMap.get(BUILD_BOARD); 616 } 617 618 /** 619 * Get the build device. 620 * 621 * @return The build device. 622 */ 623 public String getBuildDevice() { 624 return mInfoMap.get(BUILD_DEVICE); 625 } 626 627 /** 628 * Get the build ABI. 629 * 630 * @return The build ABI. 631 */ 632 public String getBuildAbi() { 633 return mInfoMap.get(BUILD_ABI); 634 } 635 636 /** 637 * Get the build ABI2. 638 * 639 * @return The build ABI2. 640 */ 641 public String getBuildAbi2() { 642 return mInfoMap.get(BUILD_ABI2); 643 } 644 645 /** 646 * get the build id 647 ** 648 * @return The build ID. 649 */ 650 public String getBuildId() { 651 return mInfoMap.get(BUILD_ID); 652 } 653 654 /** 655 * get the build version 656 ** 657 * @return The build version. 658 */ 659 public String getBuildVersion() { 660 return mInfoMap.get(BUILD_VERSION); 661 } 662 663 /** 664 * get the product name 665 * 666 * @return The product name. 667 */ 668 public String getProductName() { 669 return mInfoMap.get(PRODUCT_NAME); 670 } 671 672 /** 673 * get the serial number 674 * 675 * @return The serial number. 676 */ 677 public String getSerialNumber() { 678 return mInfoMap.get(SERIAL_NUMBER); 679 } 680 681 /** 682 * Return screen resolution(width x height) 683 * 684 * @return The screen resolution. 685 */ 686 public String getScreenResolution() { 687 return mInfoMap.get(SCREEN_WIDTH) + "x" + mInfoMap.get(SCREEN_HEIGHT); 688 } 689 690 /** 691 * Get Android platform version. 692 * 693 * @return The Android platform version. 694 */ 695 public String getAndroidPlatformVersion() { 696 return mInfoMap.get(VERSION_SDK); 697 } 698 699 /** 700 * Get supported locales. 701 * 702 * @return The supported locales. 703 */ 704 public String getLocales() { 705 return mInfoMap.get(LOCALES); 706 } 707 708 /** 709 * Get x dip 710 * 711 * @return The X dip. 712 */ 713 public String getXdpi() { 714 return mInfoMap.get(SCREEN_X_DENSITY); 715 } 716 717 /** 718 * Get y dip 719 * 720 * @return The y dip. 721 */ 722 public String getYdpi() { 723 return mInfoMap.get(SCREEN_Y_DENSITY); 724 } 725 726 /** 727 * Get touch information 728 * 729 * @return The touch screen information. 730 */ 731 public String getTouchInfo() { 732 return mInfoMap.get(TOUCH_SCREEN); 733 } 734 735 /** 736 * Get navigation information 737 * 738 * @return The navigation information. 739 */ 740 public String getNavigation() { 741 return mInfoMap.get(NAVIGATION); 742 } 743 744 /** 745 * Get keypad information 746 * 747 * @return The keypad information. 748 */ 749 public String getKeypad() { 750 return mInfoMap.get(KEYPAD); 751 } 752 753 /** 754 * Get network information 755 * 756 * @return The network information. 757 */ 758 public String getNetwork() { 759 return mInfoMap.get(NETWORK); 760 } 761 762 /** 763 * Get IMEI 764 * 765 * @return IMEI. 766 */ 767 public String getIMEI() { 768 return mInfoMap.get(IMEI); 769 } 770 771 /** 772 * Get IMSI 773 * 774 * @return IMSI. 775 */ 776 public String getIMSI() { 777 return mInfoMap.get(IMSI); 778 } 779 780 /** 781 * Get phone number 782 * 783 * @return Phone number. 784 */ 785 public String getPhoneNumber() { 786 return mInfoMap.get(PHONE_NUMBER); 787 } 788 } 789 790 /** 791 * Get the serial number of the {@link TestDevice}. 792 * 793 * @return the serial number of the {@link TestDevice} 794 */ 795 public String getSerialNumber() { 796 if (mDevice == null) { 797 return mDeviceInfo.getSerialNumber(); 798 } 799 return mDevice.getSerialNumber(); 800 } 801 802 /** 803 * Run a specified test. 804 * 805 * @param test The test to be run. 806 */ 807 public void runTest(Test test) throws DeviceDisconnectedException { 808 809 final String appNameSpace = test.getAppNameSpace(); 810 String runner = test.getInstrumentationRunner(); 811 if (runner == null) { 812 runner = DEFAULT_TEST_RUNNER_NAME; 813 } 814 815 // need to doubly escape any '$' chars in the name since this string is 816 // passed through two shells \\\$ -> \$ -> $ 817 final String testName = test.getFullName().replaceAll("\\$", "\\\\\\$"); 818 819 final String commandStr = "am instrument -w -r -e class " 820 + testName + " " + appNameSpace + "/" + runner; 821 Log.d(commandStr); 822 executeShellCommand(commandStr, new IndividualModeResultParser(test)); 823 } 824 825 /** 826 * Run a test package in batch mode. 827 * 828 * @param testPackage The testPackage to be run. 829 * @param javaPkgName The java package name. If null, run the whole test package; 830 * else, run the specified java package contained in the test package 831 */ 832 public void runInBatchMode(TestPackage testPackage, final String javaPkgName) 833 throws DeviceDisconnectedException { 834 String appNameSpace = testPackage.getAppNameSpace(); 835 String runner = testPackage.getInstrumentationRunner(); 836 if (runner == null) { 837 runner = DEFAULT_TEST_RUNNER_NAME; 838 } 839 840 String name = testPackage.getAppPackageName(); 841 if ((javaPkgName != null) && (javaPkgName.length() != 0)) { 842 name = javaPkgName; 843 } 844 845 String cmdHeader = "am instrument -w -r -e package " + name + " "; 846 final String commandStr = cmdHeader + appNameSpace + "/" + runner; 847 Log.d(commandStr); 848 849 mBatchModeResultParser = new BatchModeResultParser(testPackage); 850 executeShellCommand(commandStr, mBatchModeResultParser); 851 } 852 853 /** 854 * Run a in batch mode of a TestPackage. 855 * 856 * @param testPackage The testPackage to be run. 857 * @param javaClassName The java class name. 858 */ 859 public void runTestCaseInBatchMode(TestPackage testPackage, final String javaClassName) 860 throws DeviceDisconnectedException { 861 if (javaClassName == null) { 862 return; 863 } 864 865 String appNameSpace = testPackage.getAppNameSpace(); 866 String runner = testPackage.getInstrumentationRunner(); 867 if (runner == null) { 868 runner = DEFAULT_TEST_RUNNER_NAME; 869 } 870 871 String cmdHeader = "am instrument -w -r -e class " + javaClassName + " "; 872 final String commandStr = cmdHeader + appNameSpace + "/" + runner; 873 Log.d(commandStr); 874 875 mBatchModeResultParser = new BatchModeResultParser(testPackage); 876 executeShellCommand(commandStr, mBatchModeResultParser); 877 } 878 879 /** 880 * Get clients. 881 * 882 * @return The clients. 883 */ 884 public Client[] getClients() { 885 return mDevice.getClients(); 886 } 887 888 /** 889 * Push a file to a given path. 890 * 891 * @param localPath The local path. 892 * @param remotePath The remote path. 893 */ 894 public void pushFile(String localPath, String remotePath) { 895 SyncResult result = mSyncService.pushFile(localPath, remotePath, 896 new PushMonitor()); 897 if (result.getCode() != SyncService.RESULT_OK) { 898 Log.e("Uploading file failed: " + result.getMessage(), null); 899 } 900 } 901 902 /** 903 * Install a specified APK using adb command install. 904 * 905 * @param apkPath Name of the package to be installed. 906 */ 907 public void installAPK(final String apkPath) throws DeviceDisconnectedException, 908 InvalidApkPathException { 909 if ((apkPath == null) || (apkPath.length() == 0) || (!HostUtils.isFileExist(apkPath))) { 910 throw new InvalidApkPathException(apkPath); 911 } 912 913 // Use re-install directly 914 final String cmd = DeviceManager.getAdbLocation() + " -s " 915 + getSerialNumber() + " install -r " + apkPath; 916 Log.d(cmd); 917 918 mPackageActionTimer.start(ACTION_INSTALL, this); 919 executeCommand(cmd, new PackageActionObserver(ACTION_INSTALL)); 920 } 921 922 /** 923 * Execute the given command. 924 * 925 * @param command The command to be executed. 926 * @param stdOutReceiver The receiver for handling the output from the device. 927 */ 928 private void executeCommand(String command, StdOutObserver stdOutReceiver) 929 throws DeviceDisconnectedException { 930 if (mStatus != STATUS_OFFLINE) { 931 try { 932 Process proc = Runtime.getRuntime().exec(command); 933 934 if (stdOutReceiver != null) { 935 stdOutReceiver.setInputStream(proc.getInputStream()); 936 } 937 } catch (IOException e) { 938 e.printStackTrace(); 939 } 940 } else { 941 throw new DeviceDisconnectedException(getSerialNumber()); 942 } 943 } 944 945 /** 946 * Standard output observer. 947 * 948 */ 949 interface StdOutObserver { 950 /** 951 * set the input Stream. 952 */ 953 public void setInputStream(InputStream is); 954 955 /** 956 * Process lines. 957 */ 958 public void processLines() throws IOException; 959 } 960 961 /** 962 * Un-install APK. 963 * 964 * @param packageName The package to be un-installed. 965 */ 966 public void uninstallAPK(String packageName) throws DeviceDisconnectedException, 967 InvalidNameSpaceException { 968 if ((packageName == null) || (packageName.length() == 0)) { 969 throw new InvalidNameSpaceException(packageName); 970 } 971 972 uninstallAPKImpl(packageName, mUninstallObserver); 973 } 974 975 /** 976 * The implementation of uninstalling APK. 977 * 978 * @param packageName The package to be uninstalled. 979 * @param observer The uninstall observer 980 */ 981 private void uninstallAPKImpl(final String packageName, final PackageActionObserver observer) 982 throws DeviceDisconnectedException { 983 final String cmdStr = DeviceManager.getAdbLocation() + " -s " 984 + getSerialNumber() + " uninstall " + packageName; 985 Log.d(cmdStr); 986 mPackageActionTimer.start(ACTION_UNINSTALL, this); 987 executeCommand(cmdStr, observer); 988 } 989 990 /** 991 * Package action(install/uninstall) timeout task 992 */ 993 class PackageActionTimeoutTask extends TimerTask { 994 995 private String mAction; 996 private TestDevice mTargetDevice; 997 998 /** 999 * Task of package action timeout. 1000 * 1001 * @param action string of action 1002 * @param testDevice the {@TestDevice} which got the timeout. 1003 */ 1004 public PackageActionTimeoutTask(final String action, 1005 TestDevice testDevice) { 1006 mAction = action; 1007 mTargetDevice = testDevice; 1008 } 1009 1010 /** {@inheritDoc}*/ 1011 @Override 1012 public void run() { 1013 Log.d("PackageActionTimeoutTask.run(): mAction=" + mAction); 1014 synchronized (mObjectSync) { 1015 mObjectSync.sendNotify(); 1016 } 1017 1018 if (mAction.toLowerCase().equals(ACTION_INSTALL)) { 1019 mDeviceObserver.notifyInstallingTimeout(mTargetDevice); 1020 } else if (mAction.toLowerCase().equals(ACTION_UNINSTALL)) { 1021 mDeviceObserver.notifyUninstallingTimeout(mTargetDevice); 1022 } else if (mAction.toLowerCase().equals(ACTION_GET_DEV_INFO)) { 1023 Log.e("Get device information timeout", null); 1024 } else { 1025 Log.e("Timeout: " + mAction, null); 1026 } 1027 } 1028 } 1029 1030 /** 1031 * Package action timer monitors the package action. 1032 * 1033 */ 1034 class PackageActionTimer { 1035 private Timer mTimer; 1036 1037 /** 1038 * Start the timer while package install/uninstall/getDeviceInfo/checkAPI. 1039 * 1040 * @param action The action of package. 1041 * @param device The TestDevice the action is taken over. 1042 */ 1043 private void start(final String action, final TestDevice device) { 1044 start(action, HostConfig.Ints.packageInstallTimeoutMs.value(), device); 1045 } 1046 1047 /** 1048 * Start the timer while package install/uninstall/getDeviceInfo/checkAPI with specific 1049 * timeout. 1050 * 1051 * @param action The action of package 1052 * @param timeout The specific timeout 1053 * @param device The TestDevice under operation 1054 */ 1055 private void start(final String action, final int timeout, final TestDevice device) { 1056 Log.d("start(), action=" + action + ",mTimer=" + mTimer + ",timeout=" + timeout); 1057 synchronized (this) { 1058 if (mTimer != null) { 1059 mTimer.cancel(); 1060 } 1061 1062 mTimer = new Timer(); 1063 mTimer.schedule(new PackageActionTimeoutTask(action, device), timeout); 1064 } 1065 } 1066 1067 /** 1068 * Stop the action timer. 1069 */ 1070 private void stop() { 1071 synchronized (this) { 1072 Log.d("stop() , mTimer=" + mTimer); 1073 if (mTimer != null) { 1074 mTimer.cancel(); 1075 mTimer = null; 1076 } 1077 } 1078 } 1079 } 1080 1081 /** 1082 * The observer of package action, currently including installing and uninstalling. 1083 */ 1084 final class PackageActionObserver implements StdOutObserver, Runnable { 1085 1086 private BufferedReader mReader; 1087 private String mAction; 1088 1089 public PackageActionObserver(final String action) { 1090 mAction = action; 1091 } 1092 1093 /** {@inheritDoc} */ 1094 public void run() { 1095 try { 1096 processLines(); 1097 } catch (IOException e) { 1098 e.printStackTrace(); 1099 } finally { 1100 try { 1101 mReader.close(); 1102 } catch (IOException e) { 1103 e.printStackTrace(); 1104 } 1105 } 1106 } 1107 1108 /** 1109 * Parse the standard out to judge where the installation is complete. 1110 */ 1111 public void processLines() throws IOException { 1112 String line = mReader.readLine(); 1113 int statusCode = DeviceObserver.FAIL; 1114 boolean gotResult = false; 1115 1116 while (line != null) { 1117 line = line.toLowerCase(); 1118 if (line.indexOf("success") != -1) { 1119 statusCode = DeviceObserver.SUCCESS; 1120 gotResult = true; 1121 } else if (line.indexOf("failure") != -1) { 1122 statusCode = DeviceObserver.FAIL; 1123 CUIOutputStream.println(mAction.toLowerCase() + " met " + line); 1124 gotResult = true; 1125 } else if (line.indexOf("error") != -1) { 1126 CUIOutputStream.println(mAction.toLowerCase() + " met " + line); 1127 statusCode = DeviceObserver.FAIL; 1128 gotResult = true; 1129 } 1130 1131 if (gotResult) { 1132 Log.d(mAction + " calls stopPackageActionTimer()"); 1133 mPackageActionTimer.stop(); 1134 1135 if (mDeviceObserver != null) { 1136 mDeviceObserver.notifyInstallingComplete(statusCode); 1137 } 1138 break; 1139 } 1140 line = mReader.readLine(); 1141 } 1142 } 1143 1144 /** {@inheritDoc} */ 1145 public void setInputStream(InputStream is) { 1146 mReader = new BufferedReader(new InputStreamReader(is)); 1147 new Thread(this).start(); 1148 } 1149 } 1150 1151 /** 1152 * Raw mode result parser. 1153 */ 1154 abstract class RawModeResultParser extends MultiLineReceiver { 1155 public final static String EQ_MARK = "="; 1156 public final static String COMMA_MARK = ":"; 1157 public final static String AT_MARK = "at "; 1158 1159 public final static String STATUS_STREAM = "INSTRUMENTATION_STATUS: stream="; 1160 public final static String STATUS_TEST = "INSTRUMENTATION_STATUS: test="; 1161 public final static String STATUS_CLASS = "INSTRUMENTATION_STATUS: class="; 1162 public final static String STATUS_CODE = "INSTRUMENTATION_STATUS_CODE:"; 1163 public final static String STATUS_STACK = "INSTRUMENTATION_STATUS: stack="; 1164 public final static String STATUS_CURRENT = "INSTRUMENTATION_STATUS: current="; 1165 public final static String STATUS_NUM = "INSTRUMENTATION_STATUS: numtests="; 1166 public final static String STATUS_ERROR_STR = "INSTRUMENTATION_STATUS: Error="; 1167 1168 public final static String FAILURE = "Failure in "; 1169 public final static String ASSERTION = "junit.framework.Assertion"; 1170 1171 public final static String RESULT_STREAM = "INSTRUMENTATION_RESULT: stream="; 1172 public final static String RESULT_CODE = "INSTRUMENTATION_CODE:"; 1173 public final static String RESULT = "Test results"; 1174 public final static String RESULT_TIME = "Time:"; 1175 public final static String RESULT_SUMMARY = "Tests run:"; 1176 1177 public final static int STATUS_STARTING = 1; 1178 public final static int STATUS_PASS = 0; 1179 public final static int STATUS_FAIL = -1; 1180 public final static int STATUS_ERROR = -2; 1181 1182 private ArrayList<String> mResultLines; 1183 1184 public String mStackTrace; 1185 public String mFailedMsg; 1186 public int mResultCode; 1187 1188 public Test mTest; 1189 1190 public RawModeResultParser(Test test) { 1191 super(); 1192 1193 setTrimLine(false); 1194 1195 mTest = test; 1196 mResultLines = new ArrayList<String>(); 1197 mStackTrace = null; 1198 mFailedMsg = null; 1199 mResultCode = CtsTestResult.CODE_PASS; 1200 } 1201 1202 /** {@inheritDoc} */ 1203 @Override 1204 public void processNewLines(String[] lines) { 1205 for (String line : lines) { 1206 processNewLine(line.trim()); 1207 } 1208 } 1209 1210 /** 1211 * Process the new line. 1212 * 1213 * @param line The new line. 1214 */ 1215 abstract public void processNewLine(final String line); 1216 1217 /** 1218 * Get the result lines. 1219 * 1220 * @return The result lines. 1221 */ 1222 public ArrayList<String> getResultLines() { 1223 return mResultLines; 1224 } 1225 1226 /** 1227 * Get the named string from the string containing the mark. 1228 * 1229 * @param mark The mark to search against the results. 1230 * @return The test name. 1231 */ 1232 public String getNamedString(String mark) { 1233 for (String line : mResultLines) { 1234 if (line.startsWith(mark)) { 1235 String name = line.substring(line.indexOf(EQ_MARK) + 1); 1236 return name.trim(); 1237 } 1238 } 1239 1240 return null; 1241 } 1242 1243 /** 1244 * Parse the int from the string containing the mark. 1245 * 1246 * @param mark The mark to search against the results. 1247 * @return The number. 1248 */ 1249 public int parseIntWithMark(String mark) { 1250 for (String line : mResultLines) { 1251 if (line.startsWith(mark)) { 1252 String code = line.substring(line.indexOf(EQ_MARK) + 1); 1253 return Integer.parseInt(code.trim()); 1254 } 1255 } 1256 1257 return 0; 1258 } 1259 1260 /** 1261 * Get failed message. 1262 * 1263 * @return The failed message. 1264 */ 1265 public String getFailedMessage() { 1266 Iterator<String> iterator = mResultLines.iterator(); 1267 while (iterator.hasNext()) { 1268 String line = iterator.next(); 1269 if (line.startsWith(STATUS_STACK)) { 1270 String failedMsg = line.substring(STATUS_STACK.length()); 1271 if (iterator.hasNext()) { 1272 failedMsg += " " + iterator.next(); 1273 } 1274 return failedMsg; 1275 } 1276 } 1277 return null; 1278 } 1279 1280 /** 1281 * Get stack trace from output result. 1282 * 1283 * @return The stack trace message. 1284 */ 1285 public String getStackTrace() { 1286 StringBuilder sb = new StringBuilder(); 1287 for (String line : mResultLines) { 1288 line = line.trim(); 1289 if (line.startsWith(AT_MARK) && line.endsWith(")")) { 1290 sb.append(line + "\n"); 1291 } 1292 } 1293 return sb.toString(); 1294 } 1295 1296 /** 1297 * Get the status code of the test result. 1298 * 1299 * @param line The string contains the status code of the test result. 1300 * @return The status code of the test result. 1301 */ 1302 public int getStatusCode(String line) { 1303 String codeStr = line.substring(line.indexOf(COMMA_MARK) + 1); 1304 return Integer.parseInt(codeStr.trim()); 1305 } 1306 1307 /** {@inheritDoc} */ 1308 public boolean isCancelled() { 1309 return false; 1310 } 1311 1312 /** {@inheritDoc} */ 1313 @Override 1314 public void done() { 1315 super.done(); 1316 } 1317 } 1318 1319 /** 1320 * Individual mode result parser. <br> 1321 * Individual mode means that the host sends request 1322 * to the device method by method. And the device 1323 * reactions and outputs the result to each request. 1324 */ 1325 final class IndividualModeResultParser extends RawModeResultParser { 1326 1327 public IndividualModeResultParser(Test test) { 1328 super(test); 1329 } 1330 1331 /** 1332 * Process a new line. 1333 * 1334 * @param line The new line. 1335 */ 1336 @Override 1337 public void processNewLine(final String line) { 1338 if ((line == null) || (line.trim().length() == 0)) { 1339 return; 1340 } 1341 1342 ArrayList<String> resultLines = getResultLines(); 1343 resultLines.add(line); 1344 1345 if (line.startsWith(STATUS_CODE)) { 1346 int statusCode = getStatusCode(line); 1347 processTestResult(statusCode); 1348 resultLines.removeAll(resultLines); 1349 } 1350 } 1351 1352 /** 1353 * Process the test result of a single test. 1354 * 1355 * @param statusCode The status code of a single test's test result. 1356 */ 1357 public void processTestResult(int statusCode) { 1358 String testName = getNamedString(STATUS_TEST); 1359 String className = getNamedString(STATUS_CLASS); 1360 String testFullName = className + Test.METHOD_SEPARATOR + testName; 1361 String errorMessage = getNamedString(STATUS_ERROR_STR); 1362 1363 mFailedMsg = null; 1364 mStackTrace = null; 1365 if ((statusCode == STATUS_FAIL) || (statusCode == STATUS_ERROR)) { 1366 mFailedMsg = getFailedMessage(); 1367 mStackTrace = getStackTrace(); 1368 } 1369 1370 if ((errorMessage != null) && (errorMessage.length() != 0)) { 1371 if (mFailedMsg == null) { 1372 mFailedMsg = errorMessage; 1373 } else { 1374 mFailedMsg += " : " + errorMessage; 1375 } 1376 } 1377 1378 Log.d(testFullName + "...(" + statusCode + ")"); 1379 Log.d("errorMessage= " + errorMessage); 1380 Log.d("mFailedMsg=" + mFailedMsg); 1381 Log.d("mStackTrace=" + mStackTrace); 1382 1383 switch (statusCode) { 1384 case STATUS_STARTING: 1385 break; 1386 1387 case STATUS_PASS: 1388 mResultCode = CtsTestResult.CODE_PASS; 1389 break; 1390 1391 case STATUS_FAIL: 1392 case STATUS_ERROR: 1393 mResultCode = CtsTestResult.CODE_FAIL; 1394 break; 1395 } 1396 } 1397 1398 /** {@inheritDoc} */ 1399 @Override 1400 public void done() { 1401 mTest.notifyResult(new CtsTestResult(mResultCode, mFailedMsg, mStackTrace)); 1402 super.done(); 1403 } 1404 } 1405 1406 /** 1407 * Batch mode result parser. 1408 * Batch mode means that the host sends only one request 1409 * for all of the methods contained in the package to the 1410 * device. And then, the device runs the method one by one 1411 * and outputs the result method by method. 1412 */ 1413 final class BatchModeResultParser extends RawModeResultParser { 1414 private TestPackage mTestPackage; 1415 private Collection<Test> mTests; 1416 public int mCurrentTestNum; 1417 public int mTotalNum; 1418 1419 public BatchModeResultParser(TestPackage testPackage) { 1420 super(null); 1421 1422 mTestPackage = testPackage; 1423 if (mTestPackage != null) { 1424 mTests = mTestPackage.getTests(); 1425 } 1426 } 1427 1428 /** 1429 * Process a new line. 1430 * 1431 * @param line The new line. 1432 */ 1433 @Override 1434 public void processNewLine(final String line) { 1435 if ((line == null) || (line.trim().length() == 0)) { 1436 return; 1437 } 1438 1439 ArrayList<String> resultLines = getResultLines(); 1440 resultLines.add(line); 1441 1442 if (line.startsWith(STATUS_CODE)) { 1443 int statusCode = getStatusCode(line); 1444 processTestResult(statusCode); 1445 resultLines.removeAll(resultLines); 1446 } else if (line.startsWith(RESULT_CODE)) { 1447 int resultCode = getStatusCode(line); 1448 switch(resultCode) { 1449 case STATUS_STARTING: 1450 break; 1451 1452 case STATUS_FAIL: 1453 case STATUS_ERROR: 1454 mResultCode = CtsTestResult.CODE_FAIL; 1455 break; 1456 1457 case STATUS_PASS: 1458 mResultCode = CtsTestResult.CODE_PASS; 1459 break; 1460 } 1461 resultLines.removeAll(resultLines); 1462 } 1463 } 1464 1465 /** 1466 * Process the test result of a single test. 1467 * 1468 * @param statusCode The status code of a single test's test result. 1469 */ 1470 public void processTestResult(int statusCode) { 1471 String testName = getNamedString(STATUS_TEST); 1472 String className = getNamedString(STATUS_CLASS); 1473 String testFullName = className + Test.METHOD_SEPARATOR + testName; 1474 mCurrentTestNum = parseIntWithMark(STATUS_CURRENT); 1475 mTotalNum = parseIntWithMark(STATUS_NUM); 1476 1477 mFailedMsg = null; 1478 mStackTrace = null; 1479 if ((statusCode == STATUS_FAIL) || ((statusCode == STATUS_ERROR))) { 1480 mFailedMsg = getFailedMessage(); 1481 mStackTrace = getStackTrace(); 1482 } 1483 1484 Log.d(testFullName + "...(" + statusCode + ")"); 1485 Log.d("mFailedMsg=" + mFailedMsg); 1486 Log.d("mStackTrace=" + mStackTrace); 1487 1488 String status = TestPackage.FINISH; 1489 1490 if (statusCode == STATUS_STARTING) { 1491 status = TestPackage.START; 1492 } 1493 1494 mTest = searchTest(testFullName); 1495 if (mTest != null) { 1496 switch(statusCode) { 1497 case STATUS_STARTING: 1498 status = TestPackage.START; 1499 break; 1500 1501 case STATUS_PASS: 1502 mTest.setResult(new CtsTestResult( 1503 CtsTestResult.CODE_PASS, null, null)); 1504 break; 1505 1506 case STATUS_ERROR: 1507 case STATUS_FAIL: 1508 mTest.setResult(new CtsTestResult( 1509 CtsTestResult.CODE_FAIL, mFailedMsg, mStackTrace)); 1510 break; 1511 } 1512 } 1513 // report status even if no matching test was found 1514 mTestPackage.notifyTestStatus(mTest, status); 1515 } 1516 1517 /** 1518 * Search Test with given test full name. 1519 * 1520 * @param testFullName The test full name. 1521 * @return The Test matches the test full name given. 1522 */ 1523 private Test searchTest(String testFullName) { 1524 for (Test test : mTests) { 1525 if (testFullName.equals(test.getFullName())) { 1526 return test; 1527 } 1528 } 1529 return null; 1530 } 1531 1532 /** {@inheritDoc} */ 1533 @Override 1534 public void done() { 1535 mTestPackage.notifyBatchModeFinish(); 1536 super.done(); 1537 } 1538 } 1539 1540 /** 1541 * Remove the run time listener. 1542 */ 1543 public void removeRuntimeListener() { 1544 mDeviceObserver = null; 1545 } 1546 1547 /** 1548 * Set the run time listener. 1549 * 1550 * @param listener The run time listener. 1551 */ 1552 public void setRuntimeListener(DeviceObserver listener) { 1553 mDeviceObserver = listener; 1554 } 1555 1556 /** 1557 * Push monitor monitoring the status of pushing a file. 1558 */ 1559 class PushMonitor implements ISyncProgressMonitor { 1560 1561 public PushMonitor() { 1562 } 1563 1564 /** {@inheritDoc} */ 1565 public void advance(int arg0) { 1566 } 1567 1568 /** {@inheritDoc} */ 1569 public boolean isCanceled() { 1570 return false; 1571 } 1572 1573 /** {@inheritDoc} */ 1574 public void start(int arg0) { 1575 } 1576 1577 /** {@inheritDoc} */ 1578 public void startSubTask(String arg0) { 1579 } 1580 1581 /** {@inheritDoc} */ 1582 public void stop() { 1583 } 1584 } 1585 1586 /** 1587 * Add a new log listener. 1588 * 1589 * @param listener the listener 1590 */ 1591 public void addMainLogListener(ILogListener listener) { 1592 logListener.addListener(listener); 1593 } 1594 1595 /** 1596 * Remove an existing log listener. 1597 * 1598 * @param listener the listener to remove. 1599 */ 1600 public void removeMainLogListener(ILogListener listener) { 1601 logListener.removeListener(listener); 1602 } 1603 1604 /** 1605 * Execute Adb shell command on {@link IDevice} 1606 * 1607 * @param cmd the string of command. 1608 * @param receiver {@link IShellOutputReceiver} 1609 * @throws DeviceDisconnectedException if the device disconnects during the command 1610 */ 1611 public void executeShellCommand(final String cmd, 1612 final IShellOutputReceiver receiver) throws DeviceDisconnectedException { 1613 executeShellCommand(cmd, receiver, null); 1614 } 1615 1616 /** 1617 * Execute Adb shell command on {@link IDevice} 1618 * 1619 * Note that the receivers run in a different thread than the caller. 1620 * 1621 * @param cmd the string of command. 1622 * @param receiver {@link IShellOutputReceiver} 1623 * @param logReceiver {@link LogReceiver} 1624 * @throws DeviceDisconnectedException if the device disconnects during the command 1625 */ 1626 public void executeShellCommand(final String cmd, 1627 final IShellOutputReceiver receiver, 1628 final LogReceiver logReceiver) 1629 throws DeviceDisconnectedException { 1630 if (mStatus == STATUS_OFFLINE) { 1631 Log.d(String.format("device %s is offline when attempting to execute %s", 1632 getSerialNumber(), cmd)); 1633 throw new DeviceDisconnectedException(getSerialNumber()); 1634 } 1635 1636 new Thread() { 1637 @Override 1638 public void run() { 1639 try { 1640 mDevice.executeShellCommand(cmd, receiver); 1641 } catch (IOException e) { 1642 Log.e("", e); 1643 } 1644 } 1645 }.start(); 1646 } 1647 1648 /** 1649 * Kill {@link Client} which running the test on the {@link IDevice} 1650 * 1651 * @param packageName the test package name 1652 */ 1653 public void killProcess(String packageName) { 1654 if (mStatus == STATUS_OFFLINE) { 1655 return; 1656 } 1657 Client[] clients = mDevice.getClients(); 1658 1659 for (Client c : clients) { 1660 ClientData cd = c.getClientData(); 1661 if (cd.getClientDescription() == null) { 1662 continue; 1663 } 1664 if (cd.getClientDescription().equals(packageName)) { 1665 c.kill(); 1666 break; 1667 } 1668 } 1669 } 1670 1671 /** 1672 * Called when the {@link TestDevice} disconnected. 1673 */ 1674 public void disconnected() { 1675 CUIOutputStream.println("Device(" + getSerialNumber() + ") disconnected"); 1676 mDevice = null; 1677 mSyncService = null; 1678 1679 synchronized (mObjectSync) { 1680 mObjectSync.sendNotify(); 1681 mPackageActionTimer.stop(); 1682 } 1683 1684 if (mStatus == STATUS_BUSY) { 1685 Log.d("TestDevice.disconnected calls notifyTestingDeviceDisconnected"); 1686 mDeviceObserver.notifyTestingDeviceDisconnected(); 1687 } else { 1688 if (!TestSession.isADBServerRestartedMode()) { 1689 CUIOutputStream.printPrompt(); 1690 } 1691 } 1692 setStatus(STATUS_OFFLINE); 1693 if (logServiceThread != null) { 1694 logServiceThread.cancelLogService(); 1695 } 1696 } 1697 1698 /** 1699 * Set the status of the {@link TestDevice} 1700 * 1701 * @param statusCode the status code of {@link TestDevice} 1702 */ 1703 public void setStatus(final int statusCode) { 1704 if (statusCode != STATUS_IDLE && statusCode != STATUS_BUSY 1705 && statusCode != STATUS_OFFLINE) { 1706 throw new IllegalArgumentException("Invalid status code"); 1707 } 1708 mStatus = statusCode; 1709 } 1710 1711 /** 1712 * Get the status code of the {@link TestDevice}. 1713 * 1714 * @return get the status code of the {@link TestDevice} 1715 */ 1716 public int getStatus() { 1717 return mStatus; 1718 } 1719 1720 /** 1721 * Get the status of the {@link TestDevice} as string. 1722 * 1723 * @return the status of the {@link TestDevice} as string. 1724 */ 1725 public String getStatusAsString() { 1726 return mStatusMap.get(mStatus); 1727 } 1728 1729 /** 1730 * Wait for command finish. 1731 */ 1732 public void waitForCommandFinish() { 1733 synchronized (mObjectSync) { 1734 try { 1735 mObjectSync.waitOn(); 1736 } catch (InterruptedException e) { 1737 } 1738 } 1739 } 1740 1741 /** 1742 * Start the action timer with specific timeout 1743 * 1744 * @param action the action to start the timer 1745 * @param timeout the specific timeout 1746 */ 1747 void startActionTimer(final String action, final int timeout) { 1748 mPackageActionTimer.start(action, timeout, this); 1749 } 1750 1751 /** 1752 * Start the action timer. 1753 * 1754 * @param action the action to start the timer. 1755 */ 1756 void startActionTimer(String action) { 1757 mPackageActionTimer.start(action, this); 1758 } 1759 1760 /** 1761 * Stop the action timer. 1762 */ 1763 void stopActionTimer() { 1764 mPackageActionTimer.stop(); 1765 } 1766 1767 /** 1768 * Allows an external test to signal that it's command is complete. 1769 */ 1770 void notifyExternalTestComplete() { 1771 synchronized (mObjectSync) { 1772 mObjectSync.sendNotify(); 1773 } 1774 } 1775 1776 /** 1777 * Notify install complete. 1778 */ 1779 public void notifyInstallingComplete(int resultCode) { 1780 synchronized (mObjectSync) { 1781 mObjectSync.sendNotify(); 1782 mPackageActionTimer.stop(); 1783 } 1784 } 1785 1786 /** {@inheritDoc} */ 1787 public void notifyInstallingTimeout(TestDevice testDevice) { 1788 synchronized (mObjectSync) { 1789 mObjectSync.sendNotify(); 1790 } 1791 } 1792 1793 /** {@inheritDoc} */ 1794 public void notifyTestingDeviceDisconnected() { 1795 synchronized (mObjectSync) { 1796 mObjectSync.sendNotify(); 1797 if (mPackageActionTimer != null) { 1798 mPackageActionTimer.stop(); 1799 } 1800 } 1801 } 1802 1803 /** {@inheritDoc} */ 1804 public void notifyUninstallingComplete(int resultCode) { 1805 synchronized (mObjectSync) { 1806 mObjectSync.sendNotify(); 1807 mPackageActionTimer.stop(); 1808 } 1809 } 1810 1811 /** {@inheritDoc} */ 1812 public void notifyUninstallingTimeout(TestDevice testDevice) { 1813 synchronized (mObjectSync) { 1814 mObjectSync.sendNotify(); 1815 } 1816 } 1817 1818 /** 1819 * Synchronization object for communication between threads. 1820 */ 1821 class ObjectSync { 1822 private boolean mNotifySent = false; 1823 1824 /** 1825 * Send notify to the waiting thread. 1826 */ 1827 public void sendNotify() { 1828 Log.d("ObjectSync.sendNotify() is called, mNotifySent=" + mNotifySent); 1829 mNotifySent = true; 1830 notify(); 1831 } 1832 1833 /** 1834 * Wait on. 1835 */ 1836 public void waitOn() throws InterruptedException { 1837 Log.d("ObjectSync.waitOn() is called, mNotifySent=" + mNotifySent); 1838 if (!mNotifySent) { 1839 wait(); 1840 } 1841 1842 mNotifySent = false; 1843 } 1844 1845 /** 1846 * Check if notify has been sent to the waiting thread. 1847 * 1848 * @return If sent, return true; else, return false. 1849 */ 1850 public boolean isNotified() { 1851 return mNotifySent; 1852 } 1853 } 1854 1855 /** 1856 * Take a screenshot of the device under test. 1857 * 1858 * @return the screenshot 1859 * @throws IOException 1860 */ 1861 public RawImage getScreenshot() throws IOException { 1862 return mDevice.getScreenshot(); 1863 } 1864 } 1865