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