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