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