1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tradefed.device; 18 19 import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener; 20 import com.android.ddmlib.DdmPreferences; 21 import com.android.ddmlib.IDevice; 22 import com.android.ddmlib.IDevice.DeviceState; 23 import com.android.ddmlib.PropertyFetcher; 24 import com.android.tradefed.command.remote.DeviceDescriptor; 25 import com.android.tradefed.config.GlobalConfiguration; 26 import com.android.tradefed.config.IGlobalConfiguration; 27 import com.android.tradefed.config.Option; 28 import com.android.tradefed.config.OptionClass; 29 import com.android.tradefed.device.IDeviceMonitor.DeviceLister; 30 import com.android.tradefed.device.IManagedTestDevice.DeviceEventResponse; 31 import com.android.tradefed.device.cloud.VmRemoteDevice; 32 import com.android.tradefed.host.IHostOptions; 33 import com.android.tradefed.invoker.tracing.CloseableTraceScope; 34 import com.android.tradefed.log.ILogRegistry.EventType; 35 import com.android.tradefed.log.Log.LogLevel; 36 import com.android.tradefed.log.LogRegistry; 37 import com.android.tradefed.log.LogUtil.CLog; 38 import com.android.tradefed.result.error.InfraErrorIdentifier; 39 import com.android.tradefed.sandbox.TradefedSandbox; 40 import com.android.tradefed.util.ArrayUtil; 41 import com.android.tradefed.util.CommandResult; 42 import com.android.tradefed.util.CommandStatus; 43 import com.android.tradefed.util.FileUtil; 44 import com.android.tradefed.util.IRunUtil; 45 import com.android.tradefed.util.RunUtil; 46 import com.android.tradefed.util.SizeLimitedOutputStream; 47 import com.android.tradefed.util.StreamUtil; 48 import com.android.tradefed.util.TableFormatter; 49 import com.android.tradefed.util.ZipUtil2; 50 import com.android.tradefed.util.hostmetric.IHostMonitor; 51 52 import com.google.common.annotations.VisibleForTesting; 53 54 import java.io.File; 55 import java.io.IOException; 56 import java.io.PrintWriter; 57 import java.lang.reflect.Field; 58 import java.util.ArrayList; 59 import java.util.Arrays; 60 import java.util.Collection; 61 import java.util.Collections; 62 import java.util.Comparator; 63 import java.util.HashMap; 64 import java.util.HashSet; 65 import java.util.List; 66 import java.util.Map; 67 import java.util.Map.Entry; 68 import java.util.Set; 69 import java.util.UUID; 70 import java.util.concurrent.CountDownLatch; 71 import java.util.concurrent.TimeUnit; 72 import java.util.regex.Pattern; 73 74 @OptionClass(alias = "dmgr", global_namespace = false) 75 public class DeviceManager implements IDeviceManager { 76 77 /** Display string for unknown properties */ 78 public static final String UNKNOWN_DISPLAY_STRING = "unknown"; 79 80 /** max wait time in ms for fastboot devices command to complete */ 81 private static final long FASTBOOT_CMD_TIMEOUT = 1 * 60 * 1000; 82 /** time to wait in ms between fastboot devices requests */ 83 private static final long FASTBOOT_POLL_WAIT_TIME = 5 * 1000; 84 /** 85 * time to wait for device adb shell responsive connection before declaring it unavailable for 86 * testing 87 */ 88 private static final int CHECK_WAIT_DEVICE_AVAIL_MS = 30 * 1000; 89 90 /* the max size of the emulator output in bytes */ 91 private static final long MAX_EMULATOR_OUTPUT = 20 * 1024 * 1024; 92 93 /* the emulator output log name */ 94 private static final String EMULATOR_OUTPUT = "emulator_log"; 95 96 /** the max timeout for available device executing command. */ 97 private static final long AVAILABLE_DEV_TIMEOUT_MAX_MS = 1000; 98 99 /** a {@link DeviceSelectionOptions} that matches any device. Visible for testing. */ 100 static final IDeviceSelection ANY_DEVICE_OPTIONS = new DeviceSelectionOptions(); 101 private static final String NULL_DEVICE_SERIAL_PREFIX = "null-device"; 102 private static final String EMULATOR_SERIAL_PREFIX = "emulator"; 103 private static final String TCP_DEVICE_SERIAL_PREFIX = "tcp-device"; 104 private static final String GCE_DEVICE_SERIAL_PREFIX = "gce-device"; 105 private static final String REMOTE_DEVICE_SERIAL_PREFIX = "remote-device"; 106 private static final String LOCAL_VIRTUAL_DEVICE_SERIAL_PREFIX = "local-virtual-device"; 107 108 /** 109 * Pattern for a device listed by 'adb devices': 110 * 111 * <p>List of devices attached 112 * 113 * <p>serial1 device 114 * 115 * <p>serial2 offline 116 */ 117 private static final String DEVICE_LIST_PATTERN = ".*\n(%s)\\s+(device|offline|recovery).*"; 118 119 protected DeviceMonitorMultiplexer mDvcMon = new DeviceMonitorMultiplexer(); 120 private Boolean mDvcMonRunning = false; 121 122 private boolean mIsInitialized = false; 123 124 private ManagedDeviceList mManagedDeviceList; 125 126 private IAndroidDebugBridge mAdbBridge; 127 private ManagedDeviceListener mManagedDeviceListener; 128 protected boolean mFastbootEnabled; 129 private Set<IFastbootListener> mFastbootListeners; 130 private FastbootMonitor mFastbootMonitor; 131 private boolean mIsTerminated = false; 132 private IDeviceSelection mGlobalDeviceFilter; 133 private IDeviceSelection mDeviceSelectionOptions; 134 135 @Option(name = "max-emulators", 136 description = "the maximum number of emulators that can be allocated at one time") 137 private int mNumEmulatorSupported = 1; 138 @Option(name = "max-null-devices", 139 description = "the maximum number of no device runs that can be allocated at one time.") 140 private int mNumNullDevicesSupported = 7; 141 @Deprecated 142 @Option(name = "max-tcp-devices", 143 description = "the maximum number of tcp devices that can be allocated at one time") 144 private int mNumTcpDevicesSupported = 0; 145 146 @Option( 147 name = "max-gce-devices", 148 description = "the maximum number of remote gce devices that can be allocated at one time" 149 ) 150 private int mNumGceDevicesSupported = 1; 151 152 @Option( 153 name = "max-remote-devices", 154 description = "the maximum number of remote devices that can be allocated at one time" 155 ) 156 private int mNumRemoteDevicesSupported = 1; 157 158 @Option( 159 name = "max-local-virtual-devices", 160 description = 161 "the maximum number of local virtual devices that can be allocated at one time") 162 private int mNumLocalVirtualDevicesSupported = 0; 163 164 private boolean mSynchronousMode = false; 165 166 @Option(name = "device-recovery-interval", 167 description = "the interval in ms between attempts to recover unavailable devices.", 168 isTimeVal = true) 169 private long mDeviceRecoveryInterval = 30 * 60 * 1000; 170 171 @Option(name = "adb-path", description = "path of the adb binary to use, " 172 + "default use the one in $PATH.") 173 private String mAdbPath = "adb"; 174 175 @Option( 176 name = "fastboot-path", 177 description = "path of the fastboot binary to use, default use the one in $PATH." 178 ) 179 private File mFastbootFile = new File("fastboot"); 180 181 @Option( 182 name = "enabled-filesystem-check", 183 description = 184 "Whether or not to check the file system type as part of device storage " 185 + "readiness") 186 private boolean mMountFileSystemCheckEnabled = true; 187 188 private File mUnpackedFastbootDir = null; 189 private File mUnpackedFastboot = null; 190 191 private DeviceRecoverer mDeviceRecoverer; 192 193 private List<IHostMonitor> mGlobalHostMonitors = null; 194 195 /** Counter to wait for the first physical connection before proceeding **/ 196 private CountDownLatch mFirstDeviceAdded = new CountDownLatch(1); 197 198 /** Flag to remember if adb bridge has been disconnected and needs to be reset * */ 199 private boolean mAdbBridgeNeedRestart = false; 200 201 private Map<String, String> mMonitoringTcpFastbootDevices = new HashMap<>(); 202 203 /** 204 * The DeviceManager should be retrieved from the {@link GlobalConfiguration} 205 */ DeviceManager()206 public DeviceManager() { 207 } 208 209 @Override init()210 public void init() { 211 init(null, null); 212 } 213 214 /** 215 * Initialize the device manager. This must be called once and only once before any other 216 * methods are called. 217 */ 218 @Override init(IDeviceSelection globalDeviceFilter, List<IDeviceMonitor> globalDeviceMonitors)219 public void init(IDeviceSelection globalDeviceFilter, 220 List<IDeviceMonitor> globalDeviceMonitors) { 221 init(globalDeviceFilter, globalDeviceMonitors, 222 new ManagedTestDeviceFactory(mFastbootEnabled, DeviceManager.this, mDvcMon)); 223 } 224 225 /** 226 * Initialize the device manager. This must be called once and only once before any other 227 * methods are called. 228 */ init(IDeviceSelection globalDeviceFilter, List<IDeviceMonitor> globalDeviceMonitors, IManagedTestDeviceFactory deviceFactory)229 public synchronized void init(IDeviceSelection globalDeviceFilter, 230 List<IDeviceMonitor> globalDeviceMonitors, IManagedTestDeviceFactory deviceFactory) { 231 if (mIsInitialized) { 232 throw new IllegalStateException("already initialized"); 233 } 234 235 if (globalDeviceFilter == null) { 236 globalDeviceFilter = getGlobalConfig().getDeviceRequirements(); 237 } 238 239 if (globalDeviceMonitors == null) { 240 globalDeviceMonitors = getGlobalConfig().getDeviceMonitors(); 241 } 242 243 mGlobalHostMonitors = getGlobalConfig().getHostMonitors(); 244 if (mGlobalHostMonitors != null) { 245 for (IHostMonitor hm : mGlobalHostMonitors) { 246 hm.start(); 247 } 248 } 249 250 mIsInitialized = true; 251 mGlobalDeviceFilter = globalDeviceFilter; 252 if (globalDeviceMonitors != null) { 253 mDvcMon.addMonitors(globalDeviceMonitors); 254 } 255 mManagedDeviceList = new ManagedDeviceList(deviceFactory); 256 257 // Setup fastboot- if it's zipped, unzip it 258 if (".zip".equals(FileUtil.getExtension(mFastbootFile.getName()))) { 259 // Unzip the fastboot files 260 try { 261 mUnpackedFastbootDir = 262 ZipUtil2.extractZipToTemp(mFastbootFile, "unpacked-fastboot"); 263 mUnpackedFastboot = FileUtil.findFile(mUnpackedFastbootDir, "fastboot"); 264 } catch (IOException e) { 265 CLog.e("Failed to unpacked zipped fastboot."); 266 CLog.e(e); 267 FileUtil.recursiveDelete(mUnpackedFastbootDir); 268 mUnpackedFastbootDir = null; 269 } 270 } 271 272 final FastbootHelper fastboot = new FastbootHelper(getRunUtil(), getFastbootPath()); 273 if (fastboot.isFastbootAvailable()) { 274 mFastbootListeners = Collections.synchronizedSet(new HashSet<IFastbootListener>()); 275 mFastbootMonitor = new FastbootMonitor(); 276 startFastbootMonitor(); 277 // don't set fastboot enabled bit until mFastbootListeners has been initialized 278 mFastbootEnabled = true; 279 deviceFactory.setFastbootEnabled(mFastbootEnabled); 280 CLog.d("Using Fastboot from: '%s'", getFastbootPath()); 281 } else { 282 CLog.w("Fastboot is not available."); 283 mFastbootListeners = null; 284 mFastbootMonitor = null; 285 mFastbootEnabled = false; 286 deviceFactory.setFastbootEnabled(mFastbootEnabled); 287 } 288 289 // don't start adding devices until fastboot support has been established 290 try (CloseableTraceScope ignored = 291 new CloseableTraceScope("startAdbBridgeAndDependentServices")) { 292 startAdbBridgeAndDependentServices(); 293 } 294 // We change the state of some mutable properties quite often so we can't keep this caching 295 // for our invocations. 296 PropertyFetcher.enableCachingMutableProps(false); 297 } 298 299 /** Initialize adb connection and services depending on adb connection. */ startAdbBridgeAndDependentServices()300 private synchronized void startAdbBridgeAndDependentServices() { 301 // TODO: Temporarily increase default timeout as workaround for syncFiles timeouts 302 DdmPreferences.setTimeOut(120 * 1000); 303 mAdbBridge = createAdbBridge(); 304 mManagedDeviceListener = new ManagedDeviceListener(); 305 // It's important to add the listener before initializing the ADB bridge to avoid a race 306 // condition when detecting devices. 307 mAdbBridge.addDeviceChangeListener(mManagedDeviceListener); 308 if (mDvcMon != null && !mDvcMonRunning) { 309 mDvcMon.setDeviceLister( 310 new DeviceLister() { 311 @Override 312 public List<DeviceDescriptor> listDevices() { 313 return listAllDevices(); 314 } 315 316 @Override 317 public DeviceDescriptor getDeviceDescriptor(String serial) { 318 return DeviceManager.this.getDeviceDescriptor(serial); 319 } 320 }); 321 mDvcMon.run(); 322 mDvcMonRunning = true; 323 } 324 325 mAdbBridge.init(false /* client support */, mAdbPath); 326 try (CloseableTraceScope add = new CloseableTraceScope("add_devices")) { 327 addEmulators(); 328 addNullDevices(); 329 addGceDevices(); 330 addRemoteDevices(); 331 addLocalVirtualDevices(); 332 addNetworkDevices(); 333 } 334 335 List<IMultiDeviceRecovery> recoverers = getGlobalConfig().getMultiDeviceRecoveryHandlers(); 336 if (recoverers != null && !recoverers.isEmpty()) { 337 for (IMultiDeviceRecovery recoverer : recoverers) { 338 recoverer.setFastbootPath(getFastbootPath()); 339 } 340 mDeviceRecoverer = new DeviceRecoverer(recoverers); 341 startDeviceRecoverer(); 342 } else { 343 CLog.d("No IMultiDeviceRecovery configured."); 344 } 345 } 346 347 348 /** 349 * Return if adb bridge has been stopped and needs restart. 350 * 351 * <p>Exposed for unit testing. 352 */ 353 @VisibleForTesting shouldAdbBridgeBeRestarted()354 boolean shouldAdbBridgeBeRestarted() { 355 return mAdbBridgeNeedRestart; 356 } 357 358 /** {@inheritDoc} */ 359 @Override restartAdbBridge()360 public synchronized void restartAdbBridge() { 361 if (mAdbBridgeNeedRestart) { 362 mAdbBridgeNeedRestart = false; 363 startAdbBridgeAndDependentServices(); 364 } 365 } 366 367 /** 368 * Instruct DeviceManager whether to use background threads or not. 369 * <p/> 370 * Exposed to make unit tests more deterministic. 371 * 372 * @param syncMode 373 */ setSynchronousMode(boolean syncMode)374 void setSynchronousMode(boolean syncMode) { 375 mSynchronousMode = syncMode; 376 } 377 checkInit()378 private void checkInit() { 379 if (!mIsInitialized) { 380 throw new IllegalStateException("DeviceManager has not been initialized"); 381 } 382 } 383 384 /** 385 * Start fastboot monitoring. 386 * <p/> 387 * Exposed for unit testing. 388 */ startFastbootMonitor()389 void startFastbootMonitor() { 390 mFastbootMonitor.start(); 391 } 392 393 /** 394 * Start device recovery. 395 * <p/> 396 * Exposed for unit testing. 397 */ startDeviceRecoverer()398 void startDeviceRecoverer() { 399 mDeviceRecoverer.start(); 400 } 401 402 /** 403 * Get the {@link IGlobalConfiguration} instance to use. 404 * <p /> 405 * Exposed for unit testing. 406 */ getGlobalConfig()407 IGlobalConfiguration getGlobalConfig() { 408 return GlobalConfiguration.getInstance(); 409 } 410 411 /** 412 * Gets the {@link IHostOptions} instance to use. 413 * <p/> 414 * Exposed for unit testing 415 */ getHostOptions()416 IHostOptions getHostOptions() { 417 return getGlobalConfig().getHostOptions(); 418 } 419 420 /** 421 * Get the {@link RunUtil} instance to use. 422 * <p/> 423 * Exposed for unit testing. 424 */ getRunUtil()425 IRunUtil getRunUtil() { 426 return RunUtil.getDefault(); 427 } 428 429 /** 430 * Create a {@link RunUtil} instance to use. 431 * <p/> 432 * Exposed for unit testing. 433 */ createRunUtil()434 IRunUtil createRunUtil() { 435 return new RunUtil(); 436 } 437 438 /** 439 * Asynchronously checks if device is available, and adds to queue 440 * 441 * @param testDevice 442 */ checkAndAddAvailableDevice(final IManagedTestDevice testDevice)443 private void checkAndAddAvailableDevice(final IManagedTestDevice testDevice) { 444 if (mGlobalDeviceFilter != null && !mGlobalDeviceFilter.matches(testDevice.getIDevice())) { 445 CLog.logAndDisplay(LogLevel.INFO, "device %s doesn't match global filter, ignoring", 446 testDevice.getSerialNumber()); 447 Map<String, String> reasons = mGlobalDeviceFilter.getNoMatchReason(); 448 for (Map.Entry<String, String> reason : reasons.entrySet()) { 449 CLog.logAndDisplay( 450 LogLevel.INFO, 451 "Match failed because " + reason.getKey() + ": " + reason.getValue()); 452 } 453 mManagedDeviceList.handleDeviceEvent(testDevice, DeviceEvent.AVAILABLE_CHECK_IGNORED); 454 return; 455 } 456 457 final String threadName = String.format("Check device %s", testDevice.getSerialNumber()); 458 Runnable checkRunnable = new Runnable() { 459 @Override 460 public void run() { 461 CLog.d("checking new '%s' '%s' responsiveness", testDevice.getClass().getName(), 462 testDevice.getSerialNumber()); 463 if (testDevice.getMonitor().waitForDeviceShell(CHECK_WAIT_DEVICE_AVAIL_MS)) { 464 DeviceEventResponse r = mManagedDeviceList.handleDeviceEvent(testDevice, 465 DeviceEvent.AVAILABLE_CHECK_PASSED); 466 if (r.stateChanged && r.allocationState == DeviceAllocationState.Available) { 467 CLog.logAndDisplay(LogLevel.INFO, "Detected new device %s", 468 testDevice.getSerialNumber()); 469 } else { 470 CLog.d("Device %s failed or ignored responsiveness check, ", 471 testDevice.getSerialNumber()); 472 } 473 } else { 474 DeviceEventResponse r = mManagedDeviceList.handleDeviceEvent(testDevice, 475 DeviceEvent.AVAILABLE_CHECK_FAILED); 476 if (r.stateChanged && r.allocationState == DeviceAllocationState.Unavailable) { 477 CLog.w("Device %s is unresponsive, will not be available for testing", 478 testDevice.getSerialNumber()); 479 } 480 } 481 } 482 }; 483 if (mSynchronousMode) { 484 checkRunnable.run(); 485 } else { 486 Thread checkThread = new Thread(checkRunnable, threadName); 487 // Device checking threads shouldn't hold the JVM open 488 checkThread.setName("DeviceManager-checkRunnable"); 489 checkThread.setDaemon(true); 490 checkThread.start(); 491 } 492 } 493 494 /** 495 * Add placeholder objects for the max number of 'no device required' concurrent allocations 496 */ addNullDevices()497 private void addNullDevices() { 498 for (int i = 0; i < mNumNullDevicesSupported; i++) { 499 addAvailableDevice( 500 new NullDevice(String.format("%s-%d", NULL_DEVICE_SERIAL_PREFIX, i))); 501 } 502 } 503 504 /** 505 * Add placeholder objects for the max number of emulators that can be allocated 506 */ addEmulators()507 private void addEmulators() { 508 // TODO currently this means 'additional emulators not already running' 509 // start at a high port to limit chances of potential port conflicts with existing emulators 510 int port = 5586; 511 for (int i = 0; i < mNumEmulatorSupported; i++) { 512 addAvailableDevice(new EmulatorDevice(port)); 513 port += 2; 514 } 515 } 516 517 /** Add placeholder objects for the max number of gce devices that can be connected */ addGceDevices()518 private void addGceDevices() { 519 for (int i = 0; i < mNumGceDevicesSupported; i++) { 520 addAvailableDevice( 521 new RemoteAvdIDevice(String.format("%s-%d", GCE_DEVICE_SERIAL_PREFIX, i))); 522 } 523 } 524 525 /** Add placeholder objects for the max number of remote devices that can be managed */ addRemoteDevices()526 private void addRemoteDevices() { 527 for (int i = 0; i < mNumRemoteDevicesSupported; i++) { 528 addAvailableDevice( 529 new VmRemoteDevice(String.format("%s-%s", REMOTE_DEVICE_SERIAL_PREFIX, i))); 530 } 531 } 532 addNetworkDevices()533 private void addNetworkDevices() { 534 for (String ip : getGlobalConfig().getHostOptions().getKnownGceDeviceIpPool()) { 535 addAvailableDevice( 536 new RemoteAvdIDevice( 537 String.format("%s-%s", GCE_DEVICE_SERIAL_PREFIX, ip), ip)); 538 } 539 540 for (String ip : 541 getGlobalConfig().getHostOptions().getKnownPreconfigureNativeDevicePool()) { 542 addAvailableNativeDevice( 543 new RemoteAvdIDevice(String.format("%s-%s", GCE_DEVICE_SERIAL_PREFIX, ip), ip)); 544 } 545 546 Map<String, List<String>> preconfigureHostUsers = new HashMap<>(); 547 for (String preconfigureDevice : 548 getGlobalConfig().getHostOptions().getKnownPreconfigureVirtualDevicePool()) { 549 // Expect the preconfigureDevice string in a certain format($hostname:$user). 550 // hostname.google.com:vsoc-1 551 String[] parts = preconfigureDevice.split(":", 2); 552 preconfigureHostUsers.putIfAbsent(parts[0], new ArrayList<>()); 553 preconfigureHostUsers.get(parts[0]).add(parts.length > 1 ? parts[1] : null); 554 } 555 for (Map.Entry<String, List<String>> hostUsers : preconfigureHostUsers.entrySet()) { 556 for (int i = 0; i < hostUsers.getValue().size(); i++) { 557 String user = hostUsers.getValue().get(i); 558 String serial = 559 String.format("%s-%s-%d", GCE_DEVICE_SERIAL_PREFIX, hostUsers.getKey(), i); 560 if (user != null) { 561 serial += "-" + user; 562 } 563 addAvailableDevice(new RemoteAvdIDevice(serial, hostUsers.getKey(), user, i)); 564 } 565 } 566 567 for (String ip : getGlobalConfig().getHostOptions().getKnownRemoteDeviceIpPool()) { 568 addAvailableDevice( 569 new VmRemoteDevice( 570 String.format("%s-%s", REMOTE_DEVICE_SERIAL_PREFIX, ip), ip)); 571 } 572 } 573 addLocalVirtualDevices()574 private void addLocalVirtualDevices() { 575 for (int i = 0; i < mNumLocalVirtualDevicesSupported; i++) { 576 addAvailableDevice( 577 new StubLocalAndroidVirtualDevice( 578 String.format("%s-%s", LOCAL_VIRTUAL_DEVICE_SERIAL_PREFIX, i), i)); 579 } 580 } 581 addFastbootDevice(FastbootDevice fastbootDevice)582 public void addFastbootDevice(FastbootDevice fastbootDevice) { 583 IManagedTestDevice d = mManagedDeviceList.findOrCreateFastboot(fastbootDevice); 584 if (d != null) { 585 mManagedDeviceList.handleDeviceEvent(d, DeviceEvent.FASTBOOT_DETECTED); 586 } else { 587 CLog.e("Could not create stub device"); 588 } 589 } 590 addAvailableDevice(IDevice stubDevice)591 public void addAvailableDevice(IDevice stubDevice) { 592 IManagedTestDevice d = mManagedDeviceList.findOrCreate(stubDevice); 593 if (d != null) { 594 mManagedDeviceList.handleDeviceEvent(d, DeviceEvent.FORCE_AVAILABLE); 595 } else { 596 CLog.e("Could not create stub device"); 597 } 598 } 599 addAvailableNativeDevice(IDevice stubDevice)600 public void addAvailableNativeDevice(IDevice stubDevice) { 601 IManagedTestDevice d = mManagedDeviceList.findOrCreate(stubDevice, true); 602 if (d != null) { 603 mManagedDeviceList.handleDeviceEvent(d, DeviceEvent.FORCE_AVAILABLE); 604 } else { 605 CLog.e("Could not create native stub device"); 606 } 607 } 608 609 /** Representation of a device in Fastboot mode. */ 610 public static class FastbootDevice extends StubDevice { 611 612 private boolean mIsFastbootd = false; 613 FastbootDevice(String serial)614 public FastbootDevice(String serial) { 615 super(serial, false); 616 } 617 setFastbootd(boolean isFastbootd)618 public void setFastbootd(boolean isFastbootd) { 619 mIsFastbootd = isFastbootd; 620 } 621 isFastbootD()622 public boolean isFastbootD() { 623 return mIsFastbootd; 624 } 625 } 626 627 /** Represents a 'stub' unlaunched emulator */ 628 private static class EmulatorDevice extends StubDevice { 629 630 private final int mPort; 631 EmulatorDevice(int port)632 public EmulatorDevice(int port) { 633 super(String.format("emulator-%d", port), true); 634 mPort = port; 635 } 636 EmulatorDevice(String serial)637 public EmulatorDevice(String serial) { 638 super(serial, true); 639 mPort = Integer.valueOf(serial.substring("emulator-".length())); 640 } 641 } 642 643 /** 644 * Creates a {@link IDeviceStateMonitor} to use. 645 * <p/> 646 * Exposed so unit tests can mock 647 */ createStateMonitor(IDevice device)648 IDeviceStateMonitor createStateMonitor(IDevice device) { 649 return new DeviceStateMonitor(this, device, mFastbootEnabled); 650 } 651 652 /** 653 * {@inheritDoc} 654 */ 655 @Override allocateDevice()656 public ITestDevice allocateDevice() { 657 return allocateDevice(ANY_DEVICE_OPTIONS, false); 658 } 659 660 /** 661 * {@inheritDoc} 662 */ 663 @Override allocateDevice(IDeviceSelection options)664 public ITestDevice allocateDevice(IDeviceSelection options) { 665 return allocateDevice(options, false); 666 } 667 668 /** {@inheritDoc} */ 669 @Override allocateDevice(IDeviceSelection options, boolean isTemporary)670 public ITestDevice allocateDevice(IDeviceSelection options, boolean isTemporary) { 671 checkInit(); 672 if (isTemporary) { 673 String rand = UUID.randomUUID().toString(); 674 String serial = String.format("%s%s", NullDevice.TEMP_NULL_DEVICE_PREFIX, rand); 675 addAvailableDevice(new NullDevice(serial, true)); 676 options.setSerial(serial); 677 } 678 ITestDevice device = mManagedDeviceList.allocate(options); 679 int maxRetry = 6; 680 while (device == null 681 && System.getenv(TradefedSandbox.SANDBOX_ENABLED) != null 682 && maxRetry != 0) { 683 RunUtil.getDefault().sleep(500); // Give up to 30 seconds to detect a device in sandbox 684 device = mManagedDeviceList.allocate(options); 685 maxRetry--; 686 } 687 return device; 688 } 689 690 /** 691 * {@inheritDoc} 692 */ 693 @Override forceAllocateDevice(String serial)694 public ITestDevice forceAllocateDevice(String serial) { 695 checkInit(); 696 IManagedTestDevice d = mManagedDeviceList.forceAllocate(serial); 697 if (d != null) { 698 DeviceEventResponse r = d.handleAllocationEvent(DeviceEvent.FORCE_ALLOCATE_REQUEST); 699 if (r.stateChanged && r.allocationState == DeviceAllocationState.Allocated) { 700 // Wait for the fastboot state to be updated once to update the IDevice. 701 d.getMonitor().waitForDeviceBootloaderStateUpdate(); 702 return d; 703 } 704 } 705 return null; 706 } 707 708 /** 709 * Creates the {@link IAndroidDebugBridge} to use. 710 * <p/> 711 * Exposed so tests can mock this. 712 * @return the {@link IAndroidDebugBridge} 713 */ createAdbBridge()714 synchronized IAndroidDebugBridge createAdbBridge() { 715 return new AndroidDebugBridgeWrapper(); 716 } 717 718 /** 719 * {@inheritDoc} 720 */ 721 @Override freeDevice(ITestDevice device, FreeDeviceState deviceState)722 public void freeDevice(ITestDevice device, FreeDeviceState deviceState) { 723 checkInit(); 724 IManagedTestDevice managedDevice = (IManagedTestDevice) device; 725 // Reset fastboot path to original one no matter what 726 managedDevice.setFastbootPath(getFastbootPath()); 727 // force stop capturing logcat just to be sure 728 managedDevice.stopLogcat(); 729 IDevice ideviceToReturn = device.getIDevice(); 730 if (ideviceToReturn instanceof NullDevice) { 731 NullDevice nullDevice = (NullDevice) ideviceToReturn; 732 if (nullDevice.isTemporary()) { 733 DeviceEventResponse r = 734 mManagedDeviceList.handleDeviceEvent( 735 managedDevice, DeviceEvent.FREE_UNKNOWN); 736 CLog.d( 737 "Temporary device '%s' final allocation state: '%s'", 738 device.getSerialNumber(), r.allocationState.toString()); 739 return; 740 } 741 } 742 // don't kill emulator if it wasn't launched by launchEmulator (ie emulatorProcess is null). 743 if (ideviceToReturn.isEmulator() && managedDevice.getEmulatorProcess() != null) { 744 try { 745 killEmulator(device); 746 // stop emulator output log 747 device.stopEmulatorOutput(); 748 // emulator killed - return a stub device 749 ideviceToReturn = device.getIDevice(); 750 deviceState = FreeDeviceState.AVAILABLE; 751 } catch (DeviceNotAvailableException e) { 752 CLog.e(e); 753 deviceState = FreeDeviceState.UNAVAILABLE; 754 } 755 } 756 if (ideviceToReturn instanceof RemoteAvdIDevice 757 || ideviceToReturn instanceof VmRemoteDevice 758 || ideviceToReturn instanceof StubLocalAndroidVirtualDevice) { 759 // Make sure the device goes back to the original state. 760 managedDevice.setDeviceState(TestDeviceState.NOT_AVAILABLE); 761 } 762 DeviceEventResponse r = mManagedDeviceList.handleDeviceEvent(managedDevice, 763 getEventFromFree(managedDevice, deviceState)); 764 if (r != null && !r.stateChanged) { 765 CLog.e("Device %s was in unexpected state %s when freeing", device.getSerialNumber(), 766 r.allocationState.toString()); 767 } 768 } 769 770 /** 771 * Helper method to convert from a {@link com.android.tradefed.device.FreeDeviceState} to a 772 * {@link com.android.tradefed.device.DeviceEvent} 773 * 774 * @param managedDevice 775 */ getEventFromFree( IManagedTestDevice managedDevice, FreeDeviceState deviceState)776 private DeviceEvent getEventFromFree( 777 IManagedTestDevice managedDevice, FreeDeviceState deviceState) { 778 switch (deviceState) { 779 case UNRESPONSIVE: 780 return DeviceEvent.FREE_UNRESPONSIVE; 781 case AVAILABLE: 782 return DeviceEvent.FREE_AVAILABLE; 783 case UNAVAILABLE: 784 // We double check if device is still showing in adb or not to confirm the 785 // connection is gone. 786 if (TestDeviceState.NOT_AVAILABLE.equals(managedDevice.getDeviceState())) { 787 String devices = executeGlobalAdbCommand("devices"); 788 Pattern p = 789 Pattern.compile( 790 String.format( 791 DEVICE_LIST_PATTERN, managedDevice.getSerialNumber())); 792 if (devices == null || !p.matcher(devices).find()) { 793 return DeviceEvent.FREE_UNKNOWN; 794 } 795 } 796 return DeviceEvent.FREE_UNAVAILABLE; 797 case IGNORE: 798 return DeviceEvent.FREE_UNKNOWN; 799 } 800 throw new IllegalStateException("unknown FreeDeviceState"); 801 } 802 803 /** {@inheritDoc} */ 804 @Override executeCmdOnAvailableDevice( String serial, String command, long timeout, TimeUnit timeUnit)805 public synchronized CommandResult executeCmdOnAvailableDevice( 806 String serial, String command, long timeout, TimeUnit timeUnit) { 807 if (timeUnit.toMillis(timeout) > AVAILABLE_DEV_TIMEOUT_MAX_MS) { 808 // Fail when user tries to execute long run command. 809 CommandResult result = new CommandResult(CommandStatus.FAILED); 810 result.setStderr( 811 "The maximum timeout value is " 812 + AVAILABLE_DEV_TIMEOUT_MAX_MS 813 + " ms, but got " 814 + timeUnit.toMillis(timeout) 815 + " ms."); 816 return result; 817 } 818 IManagedTestDevice device = mManagedDeviceList.find(serial); 819 if (device == null) { 820 CommandResult result = new CommandResult(CommandStatus.FAILED); 821 result.setStderr("Can not find the device with serial " + serial); 822 return result; 823 } 824 synchronized (device) { 825 if (!device.getAllocationState().equals(DeviceAllocationState.Available)) { 826 CommandResult result = new CommandResult(CommandStatus.FAILED); 827 result.setStderr( 828 String.format( 829 "The device '%s' is not available to execute the command", serial)); 830 return result; 831 } 832 if (!TestDeviceState.ONLINE.equals(device.getDeviceState())) { 833 CommandResult result = new CommandResult(CommandStatus.FAILED); 834 result.setStderr( 835 String.format( 836 "The device '%s' is not online to execute the command", serial)); 837 return result; 838 } 839 try { 840 return device.executeShellV2Command(command, timeout, timeUnit); 841 } catch (DeviceNotAvailableException e) { 842 CommandResult result = new CommandResult(CommandStatus.FAILED); 843 result.setStderr(e.getMessage()); 844 return result; 845 } 846 } 847 } 848 849 /** 850 * {@inheritDoc} 851 */ 852 @Override launchEmulator(ITestDevice device, long bootTimeout, IRunUtil runUtil, List<String> emulatorArgs)853 public void launchEmulator(ITestDevice device, long bootTimeout, IRunUtil runUtil, 854 List<String> emulatorArgs) 855 throws DeviceNotAvailableException { 856 if (!(device.getIDevice() instanceof EmulatorDevice)) { 857 throw new IllegalStateException( 858 String.format( 859 "Device %s is not stub emulator device", device.getSerialNumber())); 860 } 861 if (!device.getDeviceState().equals(TestDeviceState.NOT_AVAILABLE)) { 862 throw new IllegalStateException(String.format( 863 "Emulator device %s is in state %s. Expected: %s", device.getSerialNumber(), 864 device.getDeviceState(), TestDeviceState.NOT_AVAILABLE)); 865 } 866 List<String> fullArgs = new ArrayList<String>(emulatorArgs); 867 EmulatorDevice emulatorDevice = (EmulatorDevice) device.getIDevice(); 868 fullArgs.add("-port"); 869 fullArgs.add(Integer.toString(emulatorDevice.mPort)); 870 871 try { 872 CLog.i("launching emulator with %s", fullArgs.toString()); 873 SizeLimitedOutputStream emulatorOutput = new SizeLimitedOutputStream( 874 MAX_EMULATOR_OUTPUT, EMULATOR_OUTPUT, ".txt"); 875 Process p = runUtil.runCmdInBackground(fullArgs, emulatorOutput); 876 // sleep a small amount to wait for process to start successfully 877 getRunUtil().sleep(500); 878 assertEmulatorProcessAlive(p, device); 879 TestDevice testDevice = (TestDevice) device; 880 testDevice.setEmulatorProcess(p); 881 testDevice.setEmulatorOutputStream(emulatorOutput); 882 } catch (IOException e) { 883 // TODO: is this the most appropriate exception to throw? 884 throw new DeviceNotAvailableException("Failed to start emulator process", e, 885 device.getSerialNumber()); 886 } 887 888 device.waitForDeviceAvailable(bootTimeout); 889 } 890 assertEmulatorProcessAlive(Process p, ITestDevice device)891 private void assertEmulatorProcessAlive(Process p, ITestDevice device) 892 throws DeviceNotAvailableException { 893 if (!p.isAlive()) { 894 try { 895 CLog.e("Emulator process has died . stdout: '%s', stderr: '%s'", 896 StreamUtil.getStringFromStream(p.getInputStream()), 897 StreamUtil.getStringFromStream(p.getErrorStream())); 898 } catch (IOException e) { 899 // ignore 900 } 901 throw new DeviceNotAvailableException("emulator died after launch", 902 device.getSerialNumber()); 903 } 904 } 905 906 /** 907 * {@inheritDoc} 908 */ 909 @Override killEmulator(ITestDevice device)910 public void killEmulator(ITestDevice device) throws DeviceNotAvailableException { 911 try { 912 device.executeAdbCommand("emu", "kill"); 913 914 // check and wait for device to become not avail 915 device.waitForDeviceNotAvailable(10 * 1000); 916 // lets ensure process is killed too - fall through 917 918 // lets try killing the process 919 Process emulatorProcess = ((IManagedTestDevice) device).getEmulatorProcess(); 920 if (emulatorProcess != null) { 921 emulatorProcess.destroy(); 922 if (emulatorProcess.isAlive()) { 923 CLog.w( 924 "Emulator process still running after destroy for %s", 925 device.getSerialNumber()); 926 forceKillProcess(emulatorProcess, device.getSerialNumber()); 927 } 928 } 929 if (!device.waitForDeviceNotAvailable(20 * 1000)) { 930 throw new DeviceNotAvailableException( 931 String.format("Failed to kill emulator %s", device.getSerialNumber()), 932 device.getSerialNumber()); 933 } 934 } finally { 935 // TODO: a more robust solution might be to have the DeviceManager 936 // do this when deviceDisconnected event is received 937 ((IManagedTestDevice) device).setIDevice(new EmulatorDevice(device.getSerialNumber())); 938 } 939 } 940 941 /** 942 * Disgusting hack alert! Attempt to force kill given process. 943 * Relies on implementation details. Only works on linux 944 * 945 * @param emulatorProcess the {@link Process} to kill 946 * @param emulatorSerial the serial number of emulator. Only used for logging 947 */ forceKillProcess(Process emulatorProcess, String emulatorSerial)948 private void forceKillProcess(Process emulatorProcess, String emulatorSerial) { 949 if (emulatorProcess.getClass().getName().equals("java.lang.UNIXProcess")) { 950 try { 951 CLog.i("Attempting to force kill emulator process for %s", emulatorSerial); 952 Field f = emulatorProcess.getClass().getDeclaredField("pid"); 953 f.setAccessible(true); 954 Integer pid = (Integer)f.get(emulatorProcess); 955 if (pid != null) { 956 RunUtil.getDefault().runTimedCmd(5 * 1000, "kill", "-9", pid.toString()); 957 } 958 } catch (NoSuchFieldException e) { 959 CLog.d("got NoSuchFieldException when attempting to read process pid"); 960 } catch (IllegalAccessException e) { 961 CLog.d("got IllegalAccessException when attempting to read process pid"); 962 } 963 } 964 } 965 966 /** 967 * {@inheritDoc} 968 */ 969 @Override connectToTcpDevice(String ipAndPort)970 public ITestDevice connectToTcpDevice(String ipAndPort) { 971 IManagedTestDevice tcpDevice = mManagedDeviceList.findOrCreate(new StubDevice(ipAndPort)); 972 if (tcpDevice == null) { 973 return null; 974 } 975 DeviceEventResponse r = tcpDevice.handleAllocationEvent(DeviceEvent.FORCE_ALLOCATE_REQUEST); 976 if (r.stateChanged && r.allocationState == DeviceAllocationState.Allocated) { 977 // Wait for the fastboot state to be updated once to update the IDevice. 978 tcpDevice.getMonitor().waitForDeviceBootloaderStateUpdate(); 979 } else { 980 return null; 981 } 982 if (doAdbConnect(ipAndPort)) { 983 try { 984 tcpDevice.setRecovery(new WaitDeviceRecovery()); 985 tcpDevice.waitForDeviceOnline(); 986 return tcpDevice; 987 } catch (DeviceNotAvailableException e) { 988 CLog.w("Device with tcp serial %s did not come online", ipAndPort); 989 } 990 } 991 freeDevice(tcpDevice, FreeDeviceState.IGNORE); 992 return null; 993 } 994 995 /** 996 * {@inheritDoc} 997 */ 998 @Override reconnectDeviceToTcp(ITestDevice usbDevice)999 public ITestDevice reconnectDeviceToTcp(ITestDevice usbDevice) 1000 throws DeviceNotAvailableException { 1001 CLog.i("Reconnecting device %s to adb over tcpip", usbDevice.getSerialNumber()); 1002 ITestDevice tcpDevice = null; 1003 if (usbDevice instanceof IManagedTestDevice) { 1004 IManagedTestDevice managedUsbDevice = (IManagedTestDevice) usbDevice; 1005 String ipAndPort = managedUsbDevice.switchToAdbTcp(); 1006 if (ipAndPort != null) { 1007 CLog.d("Device %s was switched to adb tcp on %s", usbDevice.getSerialNumber(), 1008 ipAndPort); 1009 tcpDevice = connectToTcpDevice(ipAndPort); 1010 if (tcpDevice == null) { 1011 // ruh roh, could not connect to device 1012 // Try to re-establish connection back to usb device 1013 managedUsbDevice.recoverDevice(); 1014 } 1015 } 1016 } else { 1017 CLog.e("reconnectDeviceToTcp: unrecognized device type."); 1018 } 1019 return tcpDevice; 1020 } 1021 1022 @Override disconnectFromTcpDevice(ITestDevice tcpDevice)1023 public boolean disconnectFromTcpDevice(ITestDevice tcpDevice) { 1024 CLog.i("Disconnecting and freeing tcp device %s", tcpDevice.getSerialNumber()); 1025 boolean result = false; 1026 try { 1027 result = tcpDevice.switchToAdbUsb(); 1028 } catch (DeviceNotAvailableException e) { 1029 CLog.w("Failed to switch device %s to usb mode: %s", tcpDevice.getSerialNumber(), 1030 e.getMessage()); 1031 } 1032 freeDevice(tcpDevice, FreeDeviceState.IGNORE); 1033 return result; 1034 } 1035 doAdbConnect(String ipAndPort)1036 private boolean doAdbConnect(String ipAndPort) { 1037 final String resultSuccess = String.format("connected to %s", ipAndPort); 1038 for (int i = 1; i <= 3; i++) { 1039 String adbConnectResult = executeGlobalAdbCommand("connect", ipAndPort); 1040 // runcommand "adb connect ipAndPort" 1041 if (adbConnectResult != null && adbConnectResult.startsWith(resultSuccess)) { 1042 return true; 1043 } 1044 CLog.w("Failed to connect to device on %s, attempt %d of 3. Response: %s.", 1045 ipAndPort, i, adbConnectResult); 1046 getRunUtil().sleep(5 * 1000); 1047 } 1048 return false; 1049 } 1050 1051 /** 1052 * Execute a adb command not targeted to a particular device eg. 'adb connect' 1053 * 1054 * @param cmdArgs 1055 * @return std output if the command succeedm null otherwise. 1056 */ executeGlobalAdbCommand(String... cmdArgs)1057 public String executeGlobalAdbCommand(String... cmdArgs) { 1058 String[] fullCmd = ArrayUtil.buildArray(new String[] {getAdbPath()}, cmdArgs); 1059 CommandResult result = getRunUtil().runTimedCmd(FASTBOOT_CMD_TIMEOUT, fullCmd); 1060 if (CommandStatus.SUCCESS.equals(result.getStatus())) { 1061 return result.getStdout(); 1062 } 1063 CLog.w("adb %s failed", cmdArgs[0]); 1064 return null; 1065 } 1066 1067 /** 1068 * {@inheritDoc} 1069 */ 1070 @Override terminate()1071 public synchronized void terminate() { 1072 checkInit(); 1073 if (!mIsTerminated) { 1074 mIsTerminated = true; 1075 stopAdbBridgeAndDependentServices(); 1076 // We are not terminating mFastbootMonitor here since it is a daemon thread. 1077 // Early terminating it can cause other threads to be blocked if they check 1078 // fastboot state of a device. 1079 if (mGlobalHostMonitors != null ) { 1080 for (IHostMonitor hm : mGlobalHostMonitors) { 1081 hm.terminate(); 1082 } 1083 } 1084 } 1085 FileUtil.recursiveDelete(mUnpackedFastbootDir); 1086 } 1087 1088 /** Stop adb bridge and services depending on adb connection. */ stopAdbBridgeAndDependentServices()1089 private synchronized void stopAdbBridgeAndDependentServices() { 1090 terminateDeviceRecovery(); 1091 mAdbBridge.removeDeviceChangeListener(mManagedDeviceListener); 1092 mAdbBridge.terminate(); 1093 } 1094 1095 /** {@inheritDoc} */ 1096 @Override stopAdbBridge()1097 public synchronized void stopAdbBridge() { 1098 stopAdbBridgeAndDependentServices(); 1099 mAdbBridgeNeedRestart = true; 1100 } 1101 1102 /** {@inheritDoc} */ 1103 @Override terminateDeviceRecovery()1104 public synchronized void terminateDeviceRecovery() { 1105 if (mDeviceRecoverer != null) { 1106 mDeviceRecoverer.terminate(); 1107 } 1108 } 1109 1110 /** {@inheritDoc} */ 1111 @Override terminateDeviceMonitor()1112 public synchronized void terminateDeviceMonitor() { 1113 mDvcMon.stop(); 1114 mDvcMonRunning = false; 1115 } 1116 1117 /** {@inheritDoc} */ 1118 @Override terminateHard()1119 public synchronized void terminateHard() { 1120 terminateHard("No reason given."); 1121 } 1122 1123 /** {@inheritDoc} */ 1124 @Override terminateHard(String reason)1125 public void terminateHard(String reason) { 1126 checkInit(); 1127 if (!mIsTerminated ) { 1128 for (IManagedTestDevice device : mManagedDeviceList) { 1129 device.setRecovery(new AbortRecovery(reason)); 1130 } 1131 mAdbBridge.disconnectBridge(); 1132 terminate(); 1133 } 1134 } 1135 1136 private static class AbortRecovery implements IDeviceRecovery { 1137 1138 private String mMessage; 1139 AbortRecovery(String reason)1140 AbortRecovery(String reason) { 1141 mMessage = "aborted test session: " + reason; 1142 } 1143 1144 /** 1145 * {@inheritDoc} 1146 */ 1147 @Override recoverDevice(IDeviceStateMonitor monitor, boolean recoverUntilOnline)1148 public void recoverDevice(IDeviceStateMonitor monitor, boolean recoverUntilOnline) 1149 throws DeviceNotAvailableException { 1150 throw new DeviceNotAvailableException( 1151 mMessage, monitor.getSerialNumber(), InfraErrorIdentifier.INVOCATION_CANCELLED); 1152 } 1153 1154 /** 1155 * {@inheritDoc} 1156 */ 1157 @Override recoverDeviceBootloader(IDeviceStateMonitor monitor)1158 public void recoverDeviceBootloader(IDeviceStateMonitor monitor) 1159 throws DeviceNotAvailableException { 1160 throw new DeviceNotAvailableException( 1161 mMessage, monitor.getSerialNumber(), InfraErrorIdentifier.INVOCATION_CANCELLED); 1162 } 1163 1164 /** 1165 * {@inheritDoc} 1166 */ 1167 @Override recoverDeviceRecovery(IDeviceStateMonitor monitor)1168 public void recoverDeviceRecovery(IDeviceStateMonitor monitor) 1169 throws DeviceNotAvailableException { 1170 throw new DeviceNotAvailableException( 1171 mMessage, monitor.getSerialNumber(), InfraErrorIdentifier.INVOCATION_CANCELLED); 1172 } 1173 1174 /** {@inheritDoc} */ 1175 @Override recoverDeviceFastbootd(IDeviceStateMonitor monitor)1176 public void recoverDeviceFastbootd(IDeviceStateMonitor monitor) 1177 throws DeviceNotAvailableException { 1178 throw new DeviceNotAvailableException( 1179 mMessage, monitor.getSerialNumber(), InfraErrorIdentifier.INVOCATION_CANCELLED); 1180 } 1181 } 1182 1183 @Override listAllDevices(boolean shortDescriptor)1184 public List<DeviceDescriptor> listAllDevices(boolean shortDescriptor) { 1185 final List<DeviceDescriptor> serialStates = new ArrayList<DeviceDescriptor>(); 1186 if (mAdbBridgeNeedRestart) { 1187 return serialStates; 1188 } 1189 for (IManagedTestDevice d : mManagedDeviceList) { 1190 if (d == null) { 1191 continue; 1192 } 1193 DeviceDescriptor desc = d.getCachedDeviceDescriptor(shortDescriptor); 1194 if (desc != null) { 1195 serialStates.add(desc); 1196 } 1197 } 1198 return serialStates; 1199 } 1200 1201 /** {@inheritDoc} */ 1202 @Override listAllDevices()1203 public List<DeviceDescriptor> listAllDevices() { 1204 return listAllDevices(false); 1205 } 1206 1207 /** {@inheritDoc} */ 1208 @Override getDeviceDescriptor(String serial)1209 public DeviceDescriptor getDeviceDescriptor(String serial) { 1210 IManagedTestDevice device = mManagedDeviceList.find(serial); 1211 if (device == null) { 1212 return null; 1213 } 1214 return device.getDeviceDescriptor(false); 1215 } 1216 1217 @Override displayDevicesInfo(PrintWriter stream, boolean includeStub)1218 public void displayDevicesInfo(PrintWriter stream, boolean includeStub) { 1219 List<List<String>> displayRows = new ArrayList<List<String>>(); 1220 List<String> headers = 1221 new ArrayList<>( 1222 Arrays.asList( 1223 "Serial", 1224 "State", 1225 "Allocation", 1226 "Product", 1227 "Variant", 1228 "Build", 1229 "Battery")); 1230 if (includeStub) { 1231 headers.add("class"); 1232 headers.add("TestDeviceState"); 1233 } 1234 displayRows.add(headers); 1235 List<DeviceDescriptor> deviceList = listAllDevices(); 1236 sortDeviceList(deviceList); 1237 addDevicesInfo(displayRows, deviceList, includeStub); 1238 new TableFormatter().displayTable(displayRows, stream); 1239 } 1240 1241 /** 1242 * Sorts list by state, then by serial. 1243 */ 1244 @VisibleForTesting sortDeviceList(List<DeviceDescriptor> deviceList)1245 static List<DeviceDescriptor> sortDeviceList(List<DeviceDescriptor> deviceList) { 1246 1247 Comparator<DeviceDescriptor> c = new Comparator<DeviceDescriptor>() { 1248 1249 @Override 1250 public int compare(DeviceDescriptor o1, DeviceDescriptor o2) { 1251 if (o1.getState() != o2.getState()) { 1252 // sort by state 1253 return o1.getState().toString() 1254 .compareTo(o2.getState().toString()); 1255 } 1256 // states are equal, sort by serial 1257 return o1.getSerial().compareTo(o2.getSerial()); 1258 } 1259 1260 }; 1261 Collections.sort(deviceList, c); 1262 return deviceList; 1263 } 1264 1265 /** 1266 * Get the {@link IDeviceSelection} to use to display device info 1267 * 1268 * <p>Exposed for unit testing. 1269 */ getDeviceSelectionOptions()1270 IDeviceSelection getDeviceSelectionOptions() { 1271 if (mDeviceSelectionOptions == null) { 1272 mDeviceSelectionOptions = new DeviceSelectionOptions(); 1273 } 1274 return mDeviceSelectionOptions; 1275 } 1276 addDevicesInfo( List<List<String>> displayRows, List<DeviceDescriptor> sortedDeviceList, boolean includeStub)1277 private void addDevicesInfo( 1278 List<List<String>> displayRows, 1279 List<DeviceDescriptor> sortedDeviceList, 1280 boolean includeStub) { 1281 for (DeviceDescriptor desc : sortedDeviceList) { 1282 if (!includeStub) { 1283 if (desc.isStubDevice() && desc.getState() != DeviceAllocationState.Allocated) { 1284 // don't add placeholder devices 1285 continue; 1286 } 1287 } 1288 String serial = desc.getSerial(); 1289 if (desc.getDisplaySerial() != null) { 1290 serial = desc.getDisplaySerial(); 1291 } 1292 List<String> infos = 1293 new ArrayList<>( 1294 Arrays.asList( 1295 serial, 1296 desc.getDeviceState().toString(), 1297 desc.getState().toString(), 1298 desc.getProduct(), 1299 desc.getProductVariant(), 1300 desc.getBuildId(), 1301 desc.getBatteryLevel())); 1302 if (includeStub) { 1303 infos.add(desc.getDeviceClass()); 1304 infos.add(desc.getTestDeviceState().toString()); 1305 } 1306 displayRows.add(infos); 1307 } 1308 } 1309 1310 /** 1311 * A class to listen for and act on device presence updates from ddmlib 1312 */ 1313 private class ManagedDeviceListener implements IDeviceChangeListener { 1314 1315 /** 1316 * {@inheritDoc} 1317 */ 1318 @Override deviceChanged(IDevice idevice, int changeMask)1319 public void deviceChanged(IDevice idevice, int changeMask) { 1320 if ((changeMask & IDevice.CHANGE_STATE) != 0) { 1321 IManagedTestDevice testDevice = mManagedDeviceList.findOrCreate(idevice); 1322 if (testDevice == null) { 1323 return; 1324 } 1325 TestDeviceState newState = TestDeviceState.getStateByDdms(idevice.getState()); 1326 testDevice.setDeviceState(newState); 1327 if (newState == TestDeviceState.ONLINE) { 1328 DeviceEventResponse r = mManagedDeviceList.handleDeviceEvent(testDevice, 1329 DeviceEvent.STATE_CHANGE_ONLINE); 1330 if (r.stateChanged && r.allocationState == 1331 DeviceAllocationState.Checking_Availability) { 1332 checkAndAddAvailableDevice(testDevice); 1333 } 1334 } else if (DeviceState.OFFLINE.equals(idevice.getState()) || 1335 DeviceState.UNAUTHORIZED.equals(idevice.getState())) { 1336 // handle device changing to offline or unauthorized. 1337 mManagedDeviceList.handleDeviceEvent(testDevice, 1338 DeviceEvent.STATE_CHANGE_OFFLINE); 1339 } 1340 } 1341 } 1342 1343 /** 1344 * {@inheritDoc} 1345 */ 1346 @Override deviceConnected(IDevice idevice)1347 public void deviceConnected(IDevice idevice) { 1348 CLog.d("Detected device connect %s, id %d", idevice.getSerialNumber(), 1349 idevice.hashCode()); 1350 String threadName = String.format("Connected device %s", idevice.getSerialNumber()); 1351 Runnable connectedRunnable = 1352 new Runnable() { 1353 @Override 1354 public void run() { 1355 IManagedTestDevice testDevice = 1356 mManagedDeviceList.findOrCreate(idevice); 1357 if (testDevice == null) { 1358 return; 1359 } 1360 // DDMS will allocate a new IDevice, so need 1361 // to update the TestDevice record with the new device 1362 CLog.d("Updating IDevice for device %s", idevice.getSerialNumber()); 1363 testDevice.setIDevice(idevice); 1364 TestDeviceState newState = 1365 TestDeviceState.getStateByDdms(idevice.getState()); 1366 testDevice.setDeviceState(newState); 1367 if (newState == TestDeviceState.ONLINE) { 1368 DeviceEventResponse r = 1369 mManagedDeviceList.handleDeviceEvent( 1370 testDevice, DeviceEvent.CONNECTED_ONLINE); 1371 if (r.stateChanged 1372 && r.allocationState 1373 == DeviceAllocationState.Checking_Availability) { 1374 checkAndAddAvailableDevice(testDevice); 1375 } 1376 logDeviceEvent( 1377 EventType.DEVICE_CONNECTED, testDevice.getSerialNumber()); 1378 } else if (DeviceState.OFFLINE.equals(idevice.getState()) 1379 || DeviceState.UNAUTHORIZED.equals(idevice.getState())) { 1380 mManagedDeviceList.handleDeviceEvent( 1381 testDevice, DeviceEvent.CONNECTED_OFFLINE); 1382 logDeviceEvent( 1383 EventType.DEVICE_CONNECTED_OFFLINE, 1384 testDevice.getSerialNumber()); 1385 } 1386 mFirstDeviceAdded.countDown(); 1387 } 1388 }; 1389 1390 if (mSynchronousMode) { 1391 connectedRunnable.run(); 1392 } else { 1393 // Device creation step can take a little bit of time, so do it in a thread to 1394 // avoid blocking following events of new devices 1395 Thread checkThread = new Thread(connectedRunnable, threadName); 1396 // Device checking threads shouldn't hold the JVM open 1397 checkThread.setDaemon(true); 1398 checkThread.start(); 1399 } 1400 } 1401 1402 /** 1403 * {@inheritDoc} 1404 */ 1405 @Override deviceDisconnected(IDevice disconnectedDevice)1406 public void deviceDisconnected(IDevice disconnectedDevice) { 1407 IManagedTestDevice d = mManagedDeviceList.find(disconnectedDevice.getSerialNumber()); 1408 if (d != null) { 1409 mManagedDeviceList.handleDeviceEvent(d, DeviceEvent.DISCONNECTED); 1410 d.setDeviceState(TestDeviceState.NOT_AVAILABLE); 1411 logDeviceEvent(EventType.DEVICE_DISCONNECTED, disconnectedDevice.getSerialNumber()); 1412 } 1413 } 1414 } 1415 1416 @VisibleForTesting logDeviceEvent(EventType event, String serial)1417 void logDeviceEvent(EventType event, String serial) { 1418 Map<String, String> args = new HashMap<>(); 1419 args.put("serial", serial); 1420 LogRegistry.getLogRegistry().logEvent(LogLevel.DEBUG, event, args); 1421 } 1422 1423 /** {@inheritDoc} */ 1424 @Override waitForFirstDeviceAdded(long timeout)1425 public boolean waitForFirstDeviceAdded(long timeout) { 1426 try { 1427 return mFirstDeviceAdded.await(timeout, TimeUnit.MILLISECONDS); 1428 } catch (InterruptedException e) { 1429 throw new RuntimeException(e); 1430 } 1431 } 1432 1433 /** 1434 * {@inheritDoc} 1435 */ 1436 @Override addFastbootListener(IFastbootListener listener)1437 public void addFastbootListener(IFastbootListener listener) { 1438 checkInit(); 1439 if (mFastbootEnabled) { 1440 mFastbootListeners.add(listener); 1441 } else { 1442 throw new UnsupportedOperationException("fastboot is not enabled"); 1443 } 1444 } 1445 1446 /** 1447 * {@inheritDoc} 1448 */ 1449 @Override removeFastbootListener(IFastbootListener listener)1450 public void removeFastbootListener(IFastbootListener listener) { 1451 checkInit(); 1452 if (mFastbootEnabled) { 1453 mFastbootListeners.remove(listener); 1454 } 1455 } 1456 1457 /** 1458 * A class to monitor and update fastboot state of devices. 1459 */ 1460 private class FastbootMonitor extends Thread { 1461 1462 private boolean mQuit = false; 1463 FastbootMonitor()1464 FastbootMonitor() { 1465 super("FastbootMonitor"); 1466 setDaemon(true); 1467 } 1468 1469 @Override interrupt()1470 public void interrupt() { 1471 mQuit = true; 1472 super.interrupt(); 1473 } 1474 1475 @Override run()1476 public void run() { 1477 final FastbootHelper fastboot = new FastbootHelper(getRunUtil(), getFastbootPath()); 1478 while (!mQuit) { 1479 Map<String, Boolean> serialAndMode = fastboot.getBootloaderAndFastbootdDevices(); 1480 1481 serialAndMode.putAll( 1482 fastboot.getBootloaderAndFastbootdTcpDevices( 1483 mMonitoringTcpFastbootDevices)); 1484 1485 if (serialAndMode != null) { 1486 // Update known bootloader devices state 1487 Set<String> bootloader = new HashSet<>(); 1488 Set<String> fastbootd = new HashSet<>(); 1489 for (Entry<String, Boolean> entry : serialAndMode.entrySet()) { 1490 if (entry.getValue() && getHostOptions().isFastbootdEnable()) { 1491 fastbootd.add(entry.getKey()); 1492 } else { 1493 bootloader.add(entry.getKey()); 1494 } 1495 } 1496 mManagedDeviceList.updateFastbootStates(bootloader, false); 1497 if (!fastbootd.isEmpty()) { 1498 mManagedDeviceList.updateFastbootStates(fastbootd, true); 1499 } 1500 // Add new fastboot devices. 1501 for (String serial : serialAndMode.keySet()) { 1502 FastbootDevice d = new FastbootDevice(serial); 1503 if (fastbootd.contains(serial)) { 1504 d.setFastbootd(true); 1505 } 1506 if (mGlobalDeviceFilter != null && mGlobalDeviceFilter.matches(d)) { 1507 addFastbootDevice(d); 1508 } 1509 } 1510 } 1511 if (!mFastbootListeners.isEmpty()) { 1512 // create a copy of listeners for notification to prevent deadlocks 1513 Collection<IFastbootListener> listenersCopy = 1514 new ArrayList<IFastbootListener>(mFastbootListeners.size()); 1515 listenersCopy.addAll(mFastbootListeners); 1516 for (IFastbootListener listener : listenersCopy) { 1517 listener.stateUpdated(); 1518 } 1519 } 1520 getRunUtil().sleep(FASTBOOT_POLL_WAIT_TIME); 1521 } 1522 } 1523 } 1524 1525 /** 1526 * A class for a thread which performs periodic device recovery operations. 1527 */ 1528 private class DeviceRecoverer extends Thread { 1529 1530 private boolean mQuit = false; 1531 private List<IMultiDeviceRecovery> mMultiDeviceRecoverers; 1532 DeviceRecoverer(List<IMultiDeviceRecovery> multiDeviceRecoverers)1533 public DeviceRecoverer(List<IMultiDeviceRecovery> multiDeviceRecoverers) { 1534 super("DeviceRecoverer"); 1535 mMultiDeviceRecoverers = multiDeviceRecoverers; 1536 // Ensure that this thread doesn't prevent TF from terminating 1537 setDaemon(true); 1538 } 1539 1540 @Override run()1541 public void run() { 1542 while (!mQuit) { 1543 getRunUtil().sleep(mDeviceRecoveryInterval); 1544 if (mQuit) { 1545 // After the sleep time, we check if we should run or not. 1546 return; 1547 } 1548 CLog.d("Running DeviceRecoverer ..."); 1549 if (mMultiDeviceRecoverers != null && !mMultiDeviceRecoverers.isEmpty()) { 1550 for (IMultiDeviceRecovery m : mMultiDeviceRecoverers) { 1551 CLog.d( 1552 "Triggering IMultiDeviceRecovery class %s ...", 1553 m.getClass().getSimpleName()); 1554 try { 1555 m.recoverDevices(getDeviceList()); 1556 } catch (RuntimeException e) { 1557 CLog.e("Exception during %s recovery:", m.getClass().getSimpleName()); 1558 CLog.e(e); 1559 // TODO: Log this to the history events. 1560 } 1561 } 1562 } 1563 } 1564 } 1565 terminate()1566 public void terminate() { 1567 mQuit = true; 1568 interrupt(); 1569 } 1570 } 1571 1572 @VisibleForTesting getDeviceList()1573 List<IManagedTestDevice> getDeviceList() { 1574 return mManagedDeviceList.getCopy(); 1575 } 1576 1577 @VisibleForTesting setMaxEmulators(int numEmulators)1578 void setMaxEmulators(int numEmulators) { 1579 mNumEmulatorSupported = numEmulators; 1580 } 1581 1582 @VisibleForTesting setMaxNullDevices(int nullDevices)1583 void setMaxNullDevices(int nullDevices) { 1584 mNumNullDevicesSupported = nullDevices; 1585 } 1586 1587 @VisibleForTesting setMaxGceDevices(int gceDevices)1588 void setMaxGceDevices(int gceDevices) { 1589 mNumGceDevicesSupported = gceDevices; 1590 } 1591 1592 @VisibleForTesting setMaxRemoteDevices(int remoteDevices)1593 void setMaxRemoteDevices(int remoteDevices) { 1594 mNumRemoteDevicesSupported = remoteDevices; 1595 } 1596 1597 @Override isNullDevice(String serial)1598 public boolean isNullDevice(String serial) { 1599 return serial.startsWith(NULL_DEVICE_SERIAL_PREFIX); 1600 } 1601 1602 @Override isEmulator(String serial)1603 public boolean isEmulator(String serial) { 1604 return serial.startsWith(EMULATOR_SERIAL_PREFIX); 1605 } 1606 1607 @Override addDeviceMonitor(IDeviceMonitor mon)1608 public void addDeviceMonitor(IDeviceMonitor mon) { 1609 mDvcMon.addMonitor(mon); 1610 } 1611 1612 @Override removeDeviceMonitor(IDeviceMonitor mon)1613 public void removeDeviceMonitor(IDeviceMonitor mon) { 1614 mDvcMon.removeMonitor(mon); 1615 } 1616 1617 @Override getAdbPath()1618 public String getAdbPath() { 1619 return mAdbPath; 1620 } 1621 1622 @Override getFastbootPath()1623 public String getFastbootPath() { 1624 if (mUnpackedFastboot != null) { 1625 return mUnpackedFastboot.getAbsolutePath(); 1626 } 1627 // Support default fastboot in PATH variable 1628 if (new File("fastboot").equals(mFastbootFile)) { 1629 return "fastboot"; 1630 } 1631 return mFastbootFile.getAbsolutePath(); 1632 } 1633 1634 /** {@inheritDoc} */ 1635 @Override getAdbVersion()1636 public String getAdbVersion() { 1637 return mAdbBridge.getAdbVersion(mAdbPath); 1638 } 1639 1640 /** {@inheritDoc} */ 1641 @Override addMonitoringTcpFastbootDevice(String serial, String fastboot_serial)1642 public void addMonitoringTcpFastbootDevice(String serial, String fastboot_serial) { 1643 mMonitoringTcpFastbootDevices.put(serial, fastboot_serial); 1644 } 1645 1646 /** {@inheritDoc} */ 1647 @Override isFileSystemMountCheckEnabled()1648 public boolean isFileSystemMountCheckEnabled() { 1649 return mMountFileSystemCheckEnabled; 1650 } 1651 } 1652