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