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