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.Log.LogLevel; 24 import com.android.ddmlib.PropertyFetcher; 25 import com.android.tradefed.command.remote.DeviceDescriptor; 26 import com.android.tradefed.config.GlobalConfiguration; 27 import com.android.tradefed.config.IGlobalConfiguration; 28 import com.android.tradefed.config.Option; 29 import com.android.tradefed.config.OptionClass; 30 import com.android.tradefed.device.IDeviceMonitor.DeviceLister; 31 import com.android.tradefed.device.IManagedTestDevice.DeviceEventResponse; 32 import com.android.tradefed.device.cloud.VmRemoteDevice; 33 import com.android.tradefed.host.IHostOptions; 34 import com.android.tradefed.invoker.tracing.CloseableTraceScope; 35 import com.android.tradefed.log.ILogRegistry.EventType; 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 Map<String, List<String>> preconfigureHostUsers = new HashMap<>(); 541 for (String preconfigureDevice : 542 getGlobalConfig().getHostOptions().getKnownPreconfigureVirtualDevicePool()) { 543 // Expect the preconfigureDevice string in a certain format($hostname:$user). 544 // hostname.google.com:vsoc-1 545 String[] parts = preconfigureDevice.split(":", 2); 546 preconfigureHostUsers.putIfAbsent(parts[0], new ArrayList<>()); 547 preconfigureHostUsers.get(parts[0]).add(parts.length > 1 ? parts[1] : null); 548 } 549 for (Map.Entry<String, List<String>> hostUsers : preconfigureHostUsers.entrySet()) { 550 for (int i = 0; i < hostUsers.getValue().size(); i++) { 551 String user = hostUsers.getValue().get(i); 552 String serial = 553 String.format("%s-%s-%d", GCE_DEVICE_SERIAL_PREFIX, hostUsers.getKey(), i); 554 if (user != null) { 555 serial += "-" + user; 556 } 557 addAvailableDevice(new RemoteAvdIDevice(serial, hostUsers.getKey(), user, i)); 558 } 559 } 560 561 for (String ip : getGlobalConfig().getHostOptions().getKnownRemoteDeviceIpPool()) { 562 addAvailableDevice( 563 new VmRemoteDevice( 564 String.format("%s-%s", REMOTE_DEVICE_SERIAL_PREFIX, ip), ip)); 565 } 566 } 567 addLocalVirtualDevices()568 private void addLocalVirtualDevices() { 569 for (int i = 0; i < mNumLocalVirtualDevicesSupported; i++) { 570 addAvailableDevice( 571 new StubLocalAndroidVirtualDevice( 572 String.format("%s-%s", LOCAL_VIRTUAL_DEVICE_SERIAL_PREFIX, i), i)); 573 } 574 } 575 addFastbootDevice(FastbootDevice fastbootDevice)576 public void addFastbootDevice(FastbootDevice fastbootDevice) { 577 IManagedTestDevice d = mManagedDeviceList.findOrCreateFastboot(fastbootDevice); 578 if (d != null) { 579 mManagedDeviceList.handleDeviceEvent(d, DeviceEvent.FASTBOOT_DETECTED); 580 } else { 581 CLog.e("Could not create stub device"); 582 } 583 } 584 addAvailableDevice(IDevice stubDevice)585 public void addAvailableDevice(IDevice stubDevice) { 586 IManagedTestDevice d = mManagedDeviceList.findOrCreate(stubDevice); 587 if (d != null) { 588 mManagedDeviceList.handleDeviceEvent(d, DeviceEvent.FORCE_AVAILABLE); 589 } else { 590 CLog.e("Could not create stub device"); 591 } 592 } 593 594 /** Representation of a device in Fastboot mode. */ 595 public static class FastbootDevice extends StubDevice { 596 597 private boolean mIsFastbootd = false; 598 FastbootDevice(String serial)599 public FastbootDevice(String serial) { 600 super(serial, false); 601 } 602 setFastbootd(boolean isFastbootd)603 public void setFastbootd(boolean isFastbootd) { 604 mIsFastbootd = isFastbootd; 605 } 606 isFastbootD()607 public boolean isFastbootD() { 608 return mIsFastbootd; 609 } 610 } 611 612 /** Represents a 'stub' unlaunched emulator */ 613 private static class EmulatorDevice extends StubDevice { 614 615 private final int mPort; 616 EmulatorDevice(int port)617 public EmulatorDevice(int port) { 618 super(String.format("emulator-%d", port), true); 619 mPort = port; 620 } 621 EmulatorDevice(String serial)622 public EmulatorDevice(String serial) { 623 super(serial, true); 624 mPort = Integer.valueOf(serial.substring("emulator-".length())); 625 } 626 } 627 628 /** 629 * Creates a {@link IDeviceStateMonitor} to use. 630 * <p/> 631 * Exposed so unit tests can mock 632 */ createStateMonitor(IDevice device)633 IDeviceStateMonitor createStateMonitor(IDevice device) { 634 return new DeviceStateMonitor(this, device, mFastbootEnabled); 635 } 636 637 /** 638 * {@inheritDoc} 639 */ 640 @Override allocateDevice()641 public ITestDevice allocateDevice() { 642 return allocateDevice(ANY_DEVICE_OPTIONS, false); 643 } 644 645 /** 646 * {@inheritDoc} 647 */ 648 @Override allocateDevice(IDeviceSelection options)649 public ITestDevice allocateDevice(IDeviceSelection options) { 650 return allocateDevice(options, false); 651 } 652 653 /** {@inheritDoc} */ 654 @Override allocateDevice(IDeviceSelection options, boolean isTemporary)655 public ITestDevice allocateDevice(IDeviceSelection options, boolean isTemporary) { 656 checkInit(); 657 if (isTemporary) { 658 String rand = UUID.randomUUID().toString(); 659 String serial = String.format("%s%s", NullDevice.TEMP_NULL_DEVICE_PREFIX, rand); 660 addAvailableDevice(new NullDevice(serial, true)); 661 options.setSerial(serial); 662 } 663 ITestDevice device = mManagedDeviceList.allocate(options); 664 int maxRetry = 6; 665 while (device == null 666 && System.getenv(TradefedSandbox.SANDBOX_ENABLED) != null 667 && maxRetry != 0) { 668 RunUtil.getDefault().sleep(500); // Give up to 30 seconds to detect a device in sandbox 669 device = mManagedDeviceList.allocate(options); 670 maxRetry--; 671 } 672 return device; 673 } 674 675 /** 676 * {@inheritDoc} 677 */ 678 @Override forceAllocateDevice(String serial)679 public ITestDevice forceAllocateDevice(String serial) { 680 checkInit(); 681 IManagedTestDevice d = mManagedDeviceList.forceAllocate(serial); 682 if (d != null) { 683 DeviceEventResponse r = d.handleAllocationEvent(DeviceEvent.FORCE_ALLOCATE_REQUEST); 684 if (r.stateChanged && r.allocationState == DeviceAllocationState.Allocated) { 685 // Wait for the fastboot state to be updated once to update the IDevice. 686 d.getMonitor().waitForDeviceBootloaderStateUpdate(); 687 return d; 688 } 689 } 690 return null; 691 } 692 693 /** 694 * Creates the {@link IAndroidDebugBridge} to use. 695 * <p/> 696 * Exposed so tests can mock this. 697 * @return the {@link IAndroidDebugBridge} 698 */ createAdbBridge()699 synchronized IAndroidDebugBridge createAdbBridge() { 700 return new AndroidDebugBridgeWrapper(); 701 } 702 703 /** 704 * {@inheritDoc} 705 */ 706 @Override freeDevice(ITestDevice device, FreeDeviceState deviceState)707 public void freeDevice(ITestDevice device, FreeDeviceState deviceState) { 708 checkInit(); 709 IManagedTestDevice managedDevice = (IManagedTestDevice) device; 710 // Reset fastboot path to original one no matter what 711 managedDevice.setFastbootPath(getFastbootPath()); 712 // force stop capturing logcat just to be sure 713 managedDevice.stopLogcat(); 714 IDevice ideviceToReturn = device.getIDevice(); 715 if (ideviceToReturn instanceof NullDevice) { 716 NullDevice nullDevice = (NullDevice) ideviceToReturn; 717 if (nullDevice.isTemporary()) { 718 DeviceEventResponse r = 719 mManagedDeviceList.handleDeviceEvent( 720 managedDevice, DeviceEvent.FREE_UNKNOWN); 721 CLog.d( 722 "Temporary device '%s' final allocation state: '%s'", 723 device.getSerialNumber(), r.allocationState.toString()); 724 return; 725 } 726 } 727 // don't kill emulator if it wasn't launched by launchEmulator (ie emulatorProcess is null). 728 if (ideviceToReturn.isEmulator() && managedDevice.getEmulatorProcess() != null) { 729 try { 730 killEmulator(device); 731 // stop emulator output log 732 device.stopEmulatorOutput(); 733 // emulator killed - return a stub device 734 ideviceToReturn = device.getIDevice(); 735 deviceState = FreeDeviceState.AVAILABLE; 736 } catch (DeviceNotAvailableException e) { 737 CLog.e(e); 738 deviceState = FreeDeviceState.UNAVAILABLE; 739 } 740 } 741 if (ideviceToReturn instanceof RemoteAvdIDevice 742 || ideviceToReturn instanceof VmRemoteDevice 743 || ideviceToReturn instanceof StubLocalAndroidVirtualDevice) { 744 // Make sure the device goes back to the original state. 745 managedDevice.setDeviceState(TestDeviceState.NOT_AVAILABLE); 746 } 747 DeviceEventResponse r = mManagedDeviceList.handleDeviceEvent(managedDevice, 748 getEventFromFree(managedDevice, deviceState)); 749 if (r != null && !r.stateChanged) { 750 CLog.e("Device %s was in unexpected state %s when freeing", device.getSerialNumber(), 751 r.allocationState.toString()); 752 } 753 } 754 755 /** 756 * Helper method to convert from a {@link com.android.tradefed.device.FreeDeviceState} to a 757 * {@link com.android.tradefed.device.DeviceEvent} 758 * 759 * @param managedDevice 760 */ getEventFromFree( IManagedTestDevice managedDevice, FreeDeviceState deviceState)761 private DeviceEvent getEventFromFree( 762 IManagedTestDevice managedDevice, FreeDeviceState deviceState) { 763 switch (deviceState) { 764 case UNRESPONSIVE: 765 return DeviceEvent.FREE_UNRESPONSIVE; 766 case AVAILABLE: 767 return DeviceEvent.FREE_AVAILABLE; 768 case UNAVAILABLE: 769 // We double check if device is still showing in adb or not to confirm the 770 // connection is gone. 771 if (TestDeviceState.NOT_AVAILABLE.equals(managedDevice.getDeviceState())) { 772 String devices = executeGlobalAdbCommand("devices"); 773 Pattern p = 774 Pattern.compile( 775 String.format( 776 DEVICE_LIST_PATTERN, managedDevice.getSerialNumber())); 777 if (devices == null || !p.matcher(devices).find()) { 778 return DeviceEvent.FREE_UNKNOWN; 779 } 780 } 781 return DeviceEvent.FREE_UNAVAILABLE; 782 case IGNORE: 783 return DeviceEvent.FREE_UNKNOWN; 784 } 785 throw new IllegalStateException("unknown FreeDeviceState"); 786 } 787 788 /** {@inheritDoc} */ 789 @Override executeCmdOnAvailableDevice( String serial, String command, long timeout, TimeUnit timeUnit)790 public synchronized CommandResult executeCmdOnAvailableDevice( 791 String serial, String command, long timeout, TimeUnit timeUnit) { 792 if (timeUnit.toMillis(timeout) > AVAILABLE_DEV_TIMEOUT_MAX_MS) { 793 // Fail when user tries to execute long run command. 794 CommandResult result = new CommandResult(CommandStatus.FAILED); 795 result.setStderr( 796 "The maximum timeout value is " 797 + AVAILABLE_DEV_TIMEOUT_MAX_MS 798 + " ms, but got " 799 + timeUnit.toMillis(timeout) 800 + " ms."); 801 return result; 802 } 803 IManagedTestDevice device = mManagedDeviceList.find(serial); 804 if (device == null) { 805 CommandResult result = new CommandResult(CommandStatus.FAILED); 806 result.setStderr("Can not find the device with serial " + serial); 807 return result; 808 } 809 synchronized (device) { 810 if (!device.getAllocationState().equals(DeviceAllocationState.Available)) { 811 CommandResult result = new CommandResult(CommandStatus.FAILED); 812 result.setStderr( 813 String.format( 814 "The device '%s' is not available to execute the command", serial)); 815 return result; 816 } 817 if (!TestDeviceState.ONLINE.equals(device.getDeviceState())) { 818 CommandResult result = new CommandResult(CommandStatus.FAILED); 819 result.setStderr( 820 String.format( 821 "The device '%s' is not online to execute the command", serial)); 822 return result; 823 } 824 try { 825 return device.executeShellV2Command(command, timeout, timeUnit); 826 } catch (DeviceNotAvailableException e) { 827 CommandResult result = new CommandResult(CommandStatus.FAILED); 828 result.setStderr(e.getMessage()); 829 return result; 830 } 831 } 832 } 833 834 /** 835 * {@inheritDoc} 836 */ 837 @Override launchEmulator(ITestDevice device, long bootTimeout, IRunUtil runUtil, List<String> emulatorArgs)838 public void launchEmulator(ITestDevice device, long bootTimeout, IRunUtil runUtil, 839 List<String> emulatorArgs) 840 throws DeviceNotAvailableException { 841 if (!(device.getIDevice() instanceof EmulatorDevice)) { 842 throw new IllegalStateException( 843 String.format( 844 "Device %s is not stub emulator device", device.getSerialNumber())); 845 } 846 if (!device.getDeviceState().equals(TestDeviceState.NOT_AVAILABLE)) { 847 throw new IllegalStateException(String.format( 848 "Emulator device %s is in state %s. Expected: %s", device.getSerialNumber(), 849 device.getDeviceState(), TestDeviceState.NOT_AVAILABLE)); 850 } 851 List<String> fullArgs = new ArrayList<String>(emulatorArgs); 852 EmulatorDevice emulatorDevice = (EmulatorDevice) device.getIDevice(); 853 fullArgs.add("-port"); 854 fullArgs.add(Integer.toString(emulatorDevice.mPort)); 855 856 try { 857 CLog.i("launching emulator with %s", fullArgs.toString()); 858 SizeLimitedOutputStream emulatorOutput = new SizeLimitedOutputStream( 859 MAX_EMULATOR_OUTPUT, EMULATOR_OUTPUT, ".txt"); 860 Process p = runUtil.runCmdInBackground(fullArgs, emulatorOutput); 861 // sleep a small amount to wait for process to start successfully 862 getRunUtil().sleep(500); 863 assertEmulatorProcessAlive(p, device); 864 TestDevice testDevice = (TestDevice) device; 865 testDevice.setEmulatorProcess(p); 866 testDevice.setEmulatorOutputStream(emulatorOutput); 867 } catch (IOException e) { 868 // TODO: is this the most appropriate exception to throw? 869 throw new DeviceNotAvailableException("Failed to start emulator process", e, 870 device.getSerialNumber()); 871 } 872 873 device.waitForDeviceAvailable(bootTimeout); 874 } 875 assertEmulatorProcessAlive(Process p, ITestDevice device)876 private void assertEmulatorProcessAlive(Process p, ITestDevice device) 877 throws DeviceNotAvailableException { 878 if (!p.isAlive()) { 879 try { 880 CLog.e("Emulator process has died . stdout: '%s', stderr: '%s'", 881 StreamUtil.getStringFromStream(p.getInputStream()), 882 StreamUtil.getStringFromStream(p.getErrorStream())); 883 } catch (IOException e) { 884 // ignore 885 } 886 throw new DeviceNotAvailableException("emulator died after launch", 887 device.getSerialNumber()); 888 } 889 } 890 891 /** 892 * {@inheritDoc} 893 */ 894 @Override killEmulator(ITestDevice device)895 public void killEmulator(ITestDevice device) throws DeviceNotAvailableException { 896 try { 897 device.executeAdbCommand("emu", "kill"); 898 899 // check and wait for device to become not avail 900 device.waitForDeviceNotAvailable(10 * 1000); 901 // lets ensure process is killed too - fall through 902 903 // lets try killing the process 904 Process emulatorProcess = ((IManagedTestDevice) device).getEmulatorProcess(); 905 if (emulatorProcess != null) { 906 emulatorProcess.destroy(); 907 if (emulatorProcess.isAlive()) { 908 CLog.w( 909 "Emulator process still running after destroy for %s", 910 device.getSerialNumber()); 911 forceKillProcess(emulatorProcess, device.getSerialNumber()); 912 } 913 } 914 if (!device.waitForDeviceNotAvailable(20 * 1000)) { 915 throw new DeviceNotAvailableException( 916 String.format("Failed to kill emulator %s", device.getSerialNumber()), 917 device.getSerialNumber()); 918 } 919 } finally { 920 // TODO: a more robust solution might be to have the DeviceManager 921 // do this when deviceDisconnected event is received 922 ((IManagedTestDevice) device).setIDevice(new EmulatorDevice(device.getSerialNumber())); 923 } 924 } 925 926 /** 927 * Disgusting hack alert! Attempt to force kill given process. 928 * Relies on implementation details. Only works on linux 929 * 930 * @param emulatorProcess the {@link Process} to kill 931 * @param emulatorSerial the serial number of emulator. Only used for logging 932 */ forceKillProcess(Process emulatorProcess, String emulatorSerial)933 private void forceKillProcess(Process emulatorProcess, String emulatorSerial) { 934 if (emulatorProcess.getClass().getName().equals("java.lang.UNIXProcess")) { 935 try { 936 CLog.i("Attempting to force kill emulator process for %s", emulatorSerial); 937 Field f = emulatorProcess.getClass().getDeclaredField("pid"); 938 f.setAccessible(true); 939 Integer pid = (Integer)f.get(emulatorProcess); 940 if (pid != null) { 941 RunUtil.getDefault().runTimedCmd(5 * 1000, "kill", "-9", pid.toString()); 942 } 943 } catch (NoSuchFieldException e) { 944 CLog.d("got NoSuchFieldException when attempting to read process pid"); 945 } catch (IllegalAccessException e) { 946 CLog.d("got IllegalAccessException when attempting to read process pid"); 947 } 948 } 949 } 950 951 /** 952 * {@inheritDoc} 953 */ 954 @Override connectToTcpDevice(String ipAndPort)955 public ITestDevice connectToTcpDevice(String ipAndPort) { 956 IManagedTestDevice tcpDevice = mManagedDeviceList.findOrCreate(new StubDevice(ipAndPort)); 957 if (tcpDevice == null) { 958 return null; 959 } 960 DeviceEventResponse r = tcpDevice.handleAllocationEvent(DeviceEvent.FORCE_ALLOCATE_REQUEST); 961 if (r.stateChanged && r.allocationState == DeviceAllocationState.Allocated) { 962 // Wait for the fastboot state to be updated once to update the IDevice. 963 tcpDevice.getMonitor().waitForDeviceBootloaderStateUpdate(); 964 } else { 965 return null; 966 } 967 if (doAdbConnect(ipAndPort)) { 968 try { 969 tcpDevice.setRecovery(new WaitDeviceRecovery()); 970 tcpDevice.waitForDeviceOnline(); 971 return tcpDevice; 972 } catch (DeviceNotAvailableException e) { 973 CLog.w("Device with tcp serial %s did not come online", ipAndPort); 974 } 975 } 976 freeDevice(tcpDevice, FreeDeviceState.IGNORE); 977 return null; 978 } 979 980 /** 981 * {@inheritDoc} 982 */ 983 @Override reconnectDeviceToTcp(ITestDevice usbDevice)984 public ITestDevice reconnectDeviceToTcp(ITestDevice usbDevice) 985 throws DeviceNotAvailableException { 986 CLog.i("Reconnecting device %s to adb over tcpip", usbDevice.getSerialNumber()); 987 ITestDevice tcpDevice = null; 988 if (usbDevice instanceof IManagedTestDevice) { 989 IManagedTestDevice managedUsbDevice = (IManagedTestDevice) usbDevice; 990 String ipAndPort = managedUsbDevice.switchToAdbTcp(); 991 if (ipAndPort != null) { 992 CLog.d("Device %s was switched to adb tcp on %s", usbDevice.getSerialNumber(), 993 ipAndPort); 994 tcpDevice = connectToTcpDevice(ipAndPort); 995 if (tcpDevice == null) { 996 // ruh roh, could not connect to device 997 // Try to re-establish connection back to usb device 998 managedUsbDevice.recoverDevice(); 999 } 1000 } 1001 } else { 1002 CLog.e("reconnectDeviceToTcp: unrecognized device type."); 1003 } 1004 return tcpDevice; 1005 } 1006 1007 @Override disconnectFromTcpDevice(ITestDevice tcpDevice)1008 public boolean disconnectFromTcpDevice(ITestDevice tcpDevice) { 1009 CLog.i("Disconnecting and freeing tcp device %s", tcpDevice.getSerialNumber()); 1010 boolean result = false; 1011 try { 1012 result = tcpDevice.switchToAdbUsb(); 1013 } catch (DeviceNotAvailableException e) { 1014 CLog.w("Failed to switch device %s to usb mode: %s", tcpDevice.getSerialNumber(), 1015 e.getMessage()); 1016 } 1017 freeDevice(tcpDevice, FreeDeviceState.IGNORE); 1018 return result; 1019 } 1020 doAdbConnect(String ipAndPort)1021 private boolean doAdbConnect(String ipAndPort) { 1022 final String resultSuccess = String.format("connected to %s", ipAndPort); 1023 for (int i = 1; i <= 3; i++) { 1024 String adbConnectResult = executeGlobalAdbCommand("connect", ipAndPort); 1025 // runcommand "adb connect ipAndPort" 1026 if (adbConnectResult != null && adbConnectResult.startsWith(resultSuccess)) { 1027 return true; 1028 } 1029 CLog.w("Failed to connect to device on %s, attempt %d of 3. Response: %s.", 1030 ipAndPort, i, adbConnectResult); 1031 getRunUtil().sleep(5 * 1000); 1032 } 1033 return false; 1034 } 1035 1036 /** 1037 * Execute a adb command not targeted to a particular device eg. 'adb connect' 1038 * 1039 * @param cmdArgs 1040 * @return std output if the command succeedm null otherwise. 1041 */ executeGlobalAdbCommand(String... cmdArgs)1042 public String executeGlobalAdbCommand(String... cmdArgs) { 1043 String[] fullCmd = ArrayUtil.buildArray(new String[] {getAdbPath()}, cmdArgs); 1044 CommandResult result = getRunUtil().runTimedCmd(FASTBOOT_CMD_TIMEOUT, fullCmd); 1045 if (CommandStatus.SUCCESS.equals(result.getStatus())) { 1046 return result.getStdout(); 1047 } 1048 CLog.w("adb %s failed", cmdArgs[0]); 1049 return null; 1050 } 1051 1052 /** 1053 * {@inheritDoc} 1054 */ 1055 @Override terminate()1056 public synchronized void terminate() { 1057 checkInit(); 1058 if (!mIsTerminated) { 1059 mIsTerminated = true; 1060 stopAdbBridgeAndDependentServices(); 1061 // We are not terminating mFastbootMonitor here since it is a daemon thread. 1062 // Early terminating it can cause other threads to be blocked if they check 1063 // fastboot state of a device. 1064 if (mGlobalHostMonitors != null ) { 1065 for (IHostMonitor hm : mGlobalHostMonitors) { 1066 hm.terminate(); 1067 } 1068 } 1069 } 1070 FileUtil.recursiveDelete(mUnpackedFastbootDir); 1071 } 1072 1073 /** Stop adb bridge and services depending on adb connection. */ stopAdbBridgeAndDependentServices()1074 private synchronized void stopAdbBridgeAndDependentServices() { 1075 terminateDeviceRecovery(); 1076 mAdbBridge.removeDeviceChangeListener(mManagedDeviceListener); 1077 mAdbBridge.terminate(); 1078 } 1079 1080 /** {@inheritDoc} */ 1081 @Override stopAdbBridge()1082 public synchronized void stopAdbBridge() { 1083 stopAdbBridgeAndDependentServices(); 1084 mAdbBridgeNeedRestart = true; 1085 } 1086 1087 /** {@inheritDoc} */ 1088 @Override terminateDeviceRecovery()1089 public synchronized void terminateDeviceRecovery() { 1090 if (mDeviceRecoverer != null) { 1091 mDeviceRecoverer.terminate(); 1092 } 1093 } 1094 1095 /** {@inheritDoc} */ 1096 @Override terminateDeviceMonitor()1097 public synchronized void terminateDeviceMonitor() { 1098 mDvcMon.stop(); 1099 mDvcMonRunning = false; 1100 } 1101 1102 /** {@inheritDoc} */ 1103 @Override terminateHard()1104 public synchronized void terminateHard() { 1105 terminateHard("No reason given."); 1106 } 1107 1108 /** {@inheritDoc} */ 1109 @Override terminateHard(String reason)1110 public void terminateHard(String reason) { 1111 checkInit(); 1112 if (!mIsTerminated ) { 1113 for (IManagedTestDevice device : mManagedDeviceList) { 1114 device.setRecovery(new AbortRecovery(reason)); 1115 } 1116 mAdbBridge.disconnectBridge(); 1117 terminate(); 1118 } 1119 } 1120 1121 private static class AbortRecovery implements IDeviceRecovery { 1122 1123 private String mMessage; 1124 AbortRecovery(String reason)1125 AbortRecovery(String reason) { 1126 mMessage = "aborted test session: " + reason; 1127 } 1128 1129 /** 1130 * {@inheritDoc} 1131 */ 1132 @Override recoverDevice(IDeviceStateMonitor monitor, boolean recoverUntilOnline)1133 public void recoverDevice(IDeviceStateMonitor monitor, boolean recoverUntilOnline) 1134 throws DeviceNotAvailableException { 1135 throw new DeviceNotAvailableException( 1136 mMessage, monitor.getSerialNumber(), InfraErrorIdentifier.INVOCATION_CANCELLED); 1137 } 1138 1139 /** 1140 * {@inheritDoc} 1141 */ 1142 @Override recoverDeviceBootloader(IDeviceStateMonitor monitor)1143 public void recoverDeviceBootloader(IDeviceStateMonitor monitor) 1144 throws DeviceNotAvailableException { 1145 throw new DeviceNotAvailableException( 1146 mMessage, monitor.getSerialNumber(), InfraErrorIdentifier.INVOCATION_CANCELLED); 1147 } 1148 1149 /** 1150 * {@inheritDoc} 1151 */ 1152 @Override recoverDeviceRecovery(IDeviceStateMonitor monitor)1153 public void recoverDeviceRecovery(IDeviceStateMonitor monitor) 1154 throws DeviceNotAvailableException { 1155 throw new DeviceNotAvailableException( 1156 mMessage, monitor.getSerialNumber(), InfraErrorIdentifier.INVOCATION_CANCELLED); 1157 } 1158 1159 /** {@inheritDoc} */ 1160 @Override recoverDeviceFastbootd(IDeviceStateMonitor monitor)1161 public void recoverDeviceFastbootd(IDeviceStateMonitor monitor) 1162 throws DeviceNotAvailableException { 1163 throw new DeviceNotAvailableException( 1164 mMessage, monitor.getSerialNumber(), InfraErrorIdentifier.INVOCATION_CANCELLED); 1165 } 1166 } 1167 1168 @Override listAllDevices(boolean shortDescriptor)1169 public List<DeviceDescriptor> listAllDevices(boolean shortDescriptor) { 1170 final List<DeviceDescriptor> serialStates = new ArrayList<DeviceDescriptor>(); 1171 if (mAdbBridgeNeedRestart) { 1172 return serialStates; 1173 } 1174 for (IManagedTestDevice d : mManagedDeviceList) { 1175 if (d == null) { 1176 continue; 1177 } 1178 DeviceDescriptor desc = d.getCachedDeviceDescriptor(shortDescriptor); 1179 if (desc != null) { 1180 serialStates.add(desc); 1181 } 1182 } 1183 return serialStates; 1184 } 1185 1186 /** {@inheritDoc} */ 1187 @Override listAllDevices()1188 public List<DeviceDescriptor> listAllDevices() { 1189 return listAllDevices(false); 1190 } 1191 1192 /** {@inheritDoc} */ 1193 @Override getDeviceDescriptor(String serial)1194 public DeviceDescriptor getDeviceDescriptor(String serial) { 1195 IManagedTestDevice device = mManagedDeviceList.find(serial); 1196 if (device == null) { 1197 return null; 1198 } 1199 return device.getDeviceDescriptor(false); 1200 } 1201 1202 @Override displayDevicesInfo(PrintWriter stream, boolean includeStub)1203 public void displayDevicesInfo(PrintWriter stream, boolean includeStub) { 1204 List<List<String>> displayRows = new ArrayList<List<String>>(); 1205 List<String> headers = 1206 new ArrayList<>( 1207 Arrays.asList( 1208 "Serial", 1209 "State", 1210 "Allocation", 1211 "Product", 1212 "Variant", 1213 "Build", 1214 "Battery")); 1215 if (includeStub) { 1216 headers.add("class"); 1217 headers.add("TestDeviceState"); 1218 } 1219 displayRows.add(headers); 1220 List<DeviceDescriptor> deviceList = listAllDevices(); 1221 sortDeviceList(deviceList); 1222 addDevicesInfo(displayRows, deviceList, includeStub); 1223 new TableFormatter().displayTable(displayRows, stream); 1224 } 1225 1226 /** 1227 * Sorts list by state, then by serial. 1228 */ 1229 @VisibleForTesting sortDeviceList(List<DeviceDescriptor> deviceList)1230 static List<DeviceDescriptor> sortDeviceList(List<DeviceDescriptor> deviceList) { 1231 1232 Comparator<DeviceDescriptor> c = new Comparator<DeviceDescriptor>() { 1233 1234 @Override 1235 public int compare(DeviceDescriptor o1, DeviceDescriptor o2) { 1236 if (o1.getState() != o2.getState()) { 1237 // sort by state 1238 return o1.getState().toString() 1239 .compareTo(o2.getState().toString()); 1240 } 1241 // states are equal, sort by serial 1242 return o1.getSerial().compareTo(o2.getSerial()); 1243 } 1244 1245 }; 1246 Collections.sort(deviceList, c); 1247 return deviceList; 1248 } 1249 1250 /** 1251 * Get the {@link IDeviceSelection} to use to display device info 1252 * 1253 * <p>Exposed for unit testing. 1254 */ getDeviceSelectionOptions()1255 IDeviceSelection getDeviceSelectionOptions() { 1256 if (mDeviceSelectionOptions == null) { 1257 mDeviceSelectionOptions = new DeviceSelectionOptions(); 1258 } 1259 return mDeviceSelectionOptions; 1260 } 1261 addDevicesInfo( List<List<String>> displayRows, List<DeviceDescriptor> sortedDeviceList, boolean includeStub)1262 private void addDevicesInfo( 1263 List<List<String>> displayRows, 1264 List<DeviceDescriptor> sortedDeviceList, 1265 boolean includeStub) { 1266 for (DeviceDescriptor desc : sortedDeviceList) { 1267 if (!includeStub) { 1268 if (desc.isStubDevice() && desc.getState() != DeviceAllocationState.Allocated) { 1269 // don't add placeholder devices 1270 continue; 1271 } 1272 } 1273 String serial = desc.getSerial(); 1274 if (desc.getDisplaySerial() != null) { 1275 serial = desc.getDisplaySerial(); 1276 } 1277 List<String> infos = 1278 new ArrayList<>( 1279 Arrays.asList( 1280 serial, 1281 desc.getDeviceState().toString(), 1282 desc.getState().toString(), 1283 desc.getProduct(), 1284 desc.getProductVariant(), 1285 desc.getBuildId(), 1286 desc.getBatteryLevel())); 1287 if (includeStub) { 1288 infos.add(desc.getDeviceClass()); 1289 infos.add(desc.getTestDeviceState().toString()); 1290 } 1291 displayRows.add(infos); 1292 } 1293 } 1294 1295 /** 1296 * A class to listen for and act on device presence updates from ddmlib 1297 */ 1298 private class ManagedDeviceListener implements IDeviceChangeListener { 1299 1300 /** 1301 * {@inheritDoc} 1302 */ 1303 @Override deviceChanged(IDevice idevice, int changeMask)1304 public void deviceChanged(IDevice idevice, int changeMask) { 1305 if ((changeMask & IDevice.CHANGE_STATE) != 0) { 1306 IManagedTestDevice testDevice = mManagedDeviceList.findOrCreate(idevice); 1307 if (testDevice == null) { 1308 return; 1309 } 1310 TestDeviceState newState = TestDeviceState.getStateByDdms(idevice.getState()); 1311 testDevice.setDeviceState(newState); 1312 if (newState == TestDeviceState.ONLINE) { 1313 DeviceEventResponse r = mManagedDeviceList.handleDeviceEvent(testDevice, 1314 DeviceEvent.STATE_CHANGE_ONLINE); 1315 if (r.stateChanged && r.allocationState == 1316 DeviceAllocationState.Checking_Availability) { 1317 checkAndAddAvailableDevice(testDevice); 1318 } 1319 } else if (DeviceState.OFFLINE.equals(idevice.getState()) || 1320 DeviceState.UNAUTHORIZED.equals(idevice.getState())) { 1321 // handle device changing to offline or unauthorized. 1322 mManagedDeviceList.handleDeviceEvent(testDevice, 1323 DeviceEvent.STATE_CHANGE_OFFLINE); 1324 } 1325 } 1326 } 1327 1328 /** 1329 * {@inheritDoc} 1330 */ 1331 @Override deviceConnected(IDevice idevice)1332 public void deviceConnected(IDevice idevice) { 1333 CLog.d("Detected device connect %s, id %d", idevice.getSerialNumber(), 1334 idevice.hashCode()); 1335 String threadName = String.format("Connected device %s", idevice.getSerialNumber()); 1336 Runnable connectedRunnable = 1337 new Runnable() { 1338 @Override 1339 public void run() { 1340 IManagedTestDevice testDevice = 1341 mManagedDeviceList.findOrCreate(idevice); 1342 if (testDevice == null) { 1343 return; 1344 } 1345 // DDMS will allocate a new IDevice, so need 1346 // to update the TestDevice record with the new device 1347 CLog.d("Updating IDevice for device %s", idevice.getSerialNumber()); 1348 testDevice.setIDevice(idevice); 1349 TestDeviceState newState = 1350 TestDeviceState.getStateByDdms(idevice.getState()); 1351 testDevice.setDeviceState(newState); 1352 if (newState == TestDeviceState.ONLINE) { 1353 DeviceEventResponse r = 1354 mManagedDeviceList.handleDeviceEvent( 1355 testDevice, DeviceEvent.CONNECTED_ONLINE); 1356 if (r.stateChanged 1357 && r.allocationState 1358 == DeviceAllocationState.Checking_Availability) { 1359 checkAndAddAvailableDevice(testDevice); 1360 } 1361 logDeviceEvent( 1362 EventType.DEVICE_CONNECTED, testDevice.getSerialNumber()); 1363 } else if (DeviceState.OFFLINE.equals(idevice.getState()) 1364 || DeviceState.UNAUTHORIZED.equals(idevice.getState())) { 1365 mManagedDeviceList.handleDeviceEvent( 1366 testDevice, DeviceEvent.CONNECTED_OFFLINE); 1367 logDeviceEvent( 1368 EventType.DEVICE_CONNECTED_OFFLINE, 1369 testDevice.getSerialNumber()); 1370 } 1371 mFirstDeviceAdded.countDown(); 1372 } 1373 }; 1374 1375 if (mSynchronousMode) { 1376 connectedRunnable.run(); 1377 } else { 1378 // Device creation step can take a little bit of time, so do it in a thread to 1379 // avoid blocking following events of new devices 1380 Thread checkThread = new Thread(connectedRunnable, threadName); 1381 // Device checking threads shouldn't hold the JVM open 1382 checkThread.setDaemon(true); 1383 checkThread.start(); 1384 } 1385 } 1386 1387 /** 1388 * {@inheritDoc} 1389 */ 1390 @Override deviceDisconnected(IDevice disconnectedDevice)1391 public void deviceDisconnected(IDevice disconnectedDevice) { 1392 IManagedTestDevice d = mManagedDeviceList.find(disconnectedDevice.getSerialNumber()); 1393 if (d != null) { 1394 mManagedDeviceList.handleDeviceEvent(d, DeviceEvent.DISCONNECTED); 1395 d.setDeviceState(TestDeviceState.NOT_AVAILABLE); 1396 logDeviceEvent(EventType.DEVICE_DISCONNECTED, disconnectedDevice.getSerialNumber()); 1397 } 1398 } 1399 } 1400 1401 @VisibleForTesting logDeviceEvent(EventType event, String serial)1402 void logDeviceEvent(EventType event, String serial) { 1403 Map<String, String> args = new HashMap<>(); 1404 args.put("serial", serial); 1405 LogRegistry.getLogRegistry().logEvent(LogLevel.DEBUG, event, args); 1406 } 1407 1408 /** {@inheritDoc} */ 1409 @Override waitForFirstDeviceAdded(long timeout)1410 public boolean waitForFirstDeviceAdded(long timeout) { 1411 try { 1412 return mFirstDeviceAdded.await(timeout, TimeUnit.MILLISECONDS); 1413 } catch (InterruptedException e) { 1414 throw new RuntimeException(e); 1415 } 1416 } 1417 1418 /** 1419 * {@inheritDoc} 1420 */ 1421 @Override addFastbootListener(IFastbootListener listener)1422 public void addFastbootListener(IFastbootListener listener) { 1423 checkInit(); 1424 if (mFastbootEnabled) { 1425 mFastbootListeners.add(listener); 1426 } else { 1427 throw new UnsupportedOperationException("fastboot is not enabled"); 1428 } 1429 } 1430 1431 /** 1432 * {@inheritDoc} 1433 */ 1434 @Override removeFastbootListener(IFastbootListener listener)1435 public void removeFastbootListener(IFastbootListener listener) { 1436 checkInit(); 1437 if (mFastbootEnabled) { 1438 mFastbootListeners.remove(listener); 1439 } 1440 } 1441 1442 /** 1443 * A class to monitor and update fastboot state of devices. 1444 */ 1445 private class FastbootMonitor extends Thread { 1446 1447 private boolean mQuit = false; 1448 FastbootMonitor()1449 FastbootMonitor() { 1450 super("FastbootMonitor"); 1451 setDaemon(true); 1452 } 1453 1454 @Override interrupt()1455 public void interrupt() { 1456 mQuit = true; 1457 super.interrupt(); 1458 } 1459 1460 @Override run()1461 public void run() { 1462 final FastbootHelper fastboot = new FastbootHelper(getRunUtil(), getFastbootPath()); 1463 while (!mQuit) { 1464 Map<String, Boolean> serialAndMode = fastboot.getBootloaderAndFastbootdDevices(); 1465 1466 serialAndMode.putAll( 1467 fastboot.getBootloaderAndFastbootdTcpDevices( 1468 mMonitoringTcpFastbootDevices)); 1469 1470 if (serialAndMode != null) { 1471 // Update known bootloader devices state 1472 Set<String> bootloader = new HashSet<>(); 1473 Set<String> fastbootd = new HashSet<>(); 1474 for (Entry<String, Boolean> entry : serialAndMode.entrySet()) { 1475 if (entry.getValue() && getHostOptions().isFastbootdEnable()) { 1476 fastbootd.add(entry.getKey()); 1477 } else { 1478 bootloader.add(entry.getKey()); 1479 } 1480 } 1481 mManagedDeviceList.updateFastbootStates(bootloader, false); 1482 if (!fastbootd.isEmpty()) { 1483 mManagedDeviceList.updateFastbootStates(fastbootd, true); 1484 } 1485 // Add new fastboot devices. 1486 for (String serial : serialAndMode.keySet()) { 1487 FastbootDevice d = new FastbootDevice(serial); 1488 if (fastbootd.contains(serial)) { 1489 d.setFastbootd(true); 1490 } 1491 if (mGlobalDeviceFilter != null && mGlobalDeviceFilter.matches(d)) { 1492 addFastbootDevice(d); 1493 } 1494 } 1495 } 1496 if (!mFastbootListeners.isEmpty()) { 1497 // create a copy of listeners for notification to prevent deadlocks 1498 Collection<IFastbootListener> listenersCopy = 1499 new ArrayList<IFastbootListener>(mFastbootListeners.size()); 1500 listenersCopy.addAll(mFastbootListeners); 1501 for (IFastbootListener listener : listenersCopy) { 1502 listener.stateUpdated(); 1503 } 1504 } 1505 getRunUtil().sleep(FASTBOOT_POLL_WAIT_TIME); 1506 } 1507 } 1508 } 1509 1510 /** 1511 * A class for a thread which performs periodic device recovery operations. 1512 */ 1513 private class DeviceRecoverer extends Thread { 1514 1515 private boolean mQuit = false; 1516 private List<IMultiDeviceRecovery> mMultiDeviceRecoverers; 1517 DeviceRecoverer(List<IMultiDeviceRecovery> multiDeviceRecoverers)1518 public DeviceRecoverer(List<IMultiDeviceRecovery> multiDeviceRecoverers) { 1519 super("DeviceRecoverer"); 1520 mMultiDeviceRecoverers = multiDeviceRecoverers; 1521 // Ensure that this thread doesn't prevent TF from terminating 1522 setDaemon(true); 1523 } 1524 1525 @Override run()1526 public void run() { 1527 while (!mQuit) { 1528 getRunUtil().sleep(mDeviceRecoveryInterval); 1529 if (mQuit) { 1530 // After the sleep time, we check if we should run or not. 1531 return; 1532 } 1533 CLog.d("Running DeviceRecoverer ..."); 1534 if (mMultiDeviceRecoverers != null && !mMultiDeviceRecoverers.isEmpty()) { 1535 for (IMultiDeviceRecovery m : mMultiDeviceRecoverers) { 1536 CLog.d( 1537 "Triggering IMultiDeviceRecovery class %s ...", 1538 m.getClass().getSimpleName()); 1539 try { 1540 m.recoverDevices(getDeviceList()); 1541 } catch (RuntimeException e) { 1542 CLog.e("Exception during %s recovery:", m.getClass().getSimpleName()); 1543 CLog.e(e); 1544 // TODO: Log this to the history events. 1545 } 1546 } 1547 } 1548 } 1549 } 1550 terminate()1551 public void terminate() { 1552 mQuit = true; 1553 interrupt(); 1554 } 1555 } 1556 1557 @VisibleForTesting getDeviceList()1558 List<IManagedTestDevice> getDeviceList() { 1559 return mManagedDeviceList.getCopy(); 1560 } 1561 1562 @VisibleForTesting setMaxEmulators(int numEmulators)1563 void setMaxEmulators(int numEmulators) { 1564 mNumEmulatorSupported = numEmulators; 1565 } 1566 1567 @VisibleForTesting setMaxNullDevices(int nullDevices)1568 void setMaxNullDevices(int nullDevices) { 1569 mNumNullDevicesSupported = nullDevices; 1570 } 1571 1572 @VisibleForTesting setMaxGceDevices(int gceDevices)1573 void setMaxGceDevices(int gceDevices) { 1574 mNumGceDevicesSupported = gceDevices; 1575 } 1576 1577 @VisibleForTesting setMaxRemoteDevices(int remoteDevices)1578 void setMaxRemoteDevices(int remoteDevices) { 1579 mNumRemoteDevicesSupported = remoteDevices; 1580 } 1581 1582 @Override isNullDevice(String serial)1583 public boolean isNullDevice(String serial) { 1584 return serial.startsWith(NULL_DEVICE_SERIAL_PREFIX); 1585 } 1586 1587 @Override isEmulator(String serial)1588 public boolean isEmulator(String serial) { 1589 return serial.startsWith(EMULATOR_SERIAL_PREFIX); 1590 } 1591 1592 @Override addDeviceMonitor(IDeviceMonitor mon)1593 public void addDeviceMonitor(IDeviceMonitor mon) { 1594 mDvcMon.addMonitor(mon); 1595 } 1596 1597 @Override removeDeviceMonitor(IDeviceMonitor mon)1598 public void removeDeviceMonitor(IDeviceMonitor mon) { 1599 mDvcMon.removeMonitor(mon); 1600 } 1601 1602 @Override getAdbPath()1603 public String getAdbPath() { 1604 return mAdbPath; 1605 } 1606 1607 @Override getFastbootPath()1608 public String getFastbootPath() { 1609 if (mUnpackedFastboot != null) { 1610 return mUnpackedFastboot.getAbsolutePath(); 1611 } 1612 // Support default fastboot in PATH variable 1613 if (new File("fastboot").equals(mFastbootFile)) { 1614 return "fastboot"; 1615 } 1616 return mFastbootFile.getAbsolutePath(); 1617 } 1618 1619 /** {@inheritDoc} */ 1620 @Override getAdbVersion()1621 public String getAdbVersion() { 1622 return mAdbBridge.getAdbVersion(mAdbPath); 1623 } 1624 1625 /** {@inheritDoc} */ 1626 @Override addMonitoringTcpFastbootDevice(String serial, String fastboot_serial)1627 public void addMonitoringTcpFastbootDevice(String serial, String fastboot_serial) { 1628 mMonitoringTcpFastbootDevices.put(serial, fastboot_serial); 1629 } 1630 1631 /** {@inheritDoc} */ 1632 @Override isFileSystemMountCheckEnabled()1633 public boolean isFileSystemMountCheckEnabled() { 1634 return mMountFileSystemCheckEnabled; 1635 } 1636 } 1637