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