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