Check Android devices exist before calling commands on them. (#743)

`AndroidDevice` instantiation calls `adb root` on devices if the device
is rootable. And today, we instantiate the device objects before we
check if the device exists at all, which is logically incorrect.
diff --git a/mobly/controllers/android_device.py b/mobly/controllers/android_device.py
index 0438cba..d7a7813 100644
--- a/mobly/controllers/android_device.py
+++ b/mobly/controllers/android_device.py
@@ -110,13 +110,6 @@
     ads = get_instances(configs)
   else:
     raise Error('No valid config found in: %s' % configs)
-  valid_ad_identifiers = list_adb_devices() + list_adb_devices_by_usb_id()
-
-  for ad in ads:
-    if ad.serial not in valid_ad_identifiers:
-      raise DeviceError(
-          ad, 'Android device is specified in config but is not '
-          'attached.')
   _start_services_on_ads(ads)
   return ads
 
@@ -146,6 +139,20 @@
   return [ad.device_info for ad in ads]
 
 
+def _validate_device_existence(serials):
+  """Validate that all the devices specified by the configs can be reached.
+
+  Args:
+    serials: list of strings, the serials of all the devices that are expected
+      to exist.
+  """
+  valid_ad_identifiers = list_adb_devices() + list_adb_devices_by_usb_id()
+  for serial in serials:
+    if serial not in valid_ad_identifiers:
+      raise Error(f'Android device serial "{serial}" is specified in '
+                  'config but is not reachable.')
+
+
 def _start_services_on_ads(ads):
   """Starts long running services on multiple AndroidDevice objects.
 
@@ -247,6 +254,8 @@
   Returns:
     A list of AndroidDevice objects.
   """
+  _validate_device_existence(serials)
+
   results = []
   for s in serials:
     results.append(AndroidDevice(s))
@@ -265,13 +274,19 @@
   Returns:
     A list of AndroidDevice objects.
   """
-  results = []
+  # First make sure each config contains a serial, and all the serials'
+  # corresponding devices exist.
+  serials = []
   for c in configs:
     try:
       serial = c.pop('serial')
+      serials.append(serial)
     except KeyError:
       raise Error(
           'Required value "serial" is missing in AndroidDevice config %s.' % c)
+  _validate_device_existence(serials)
+  results = []
+  for c in configs:
     is_required = c.get(KEY_DEVICE_REQUIRED, True)
     try:
       ad = AndroidDevice(serial)
diff --git a/tests/mobly/controllers/android_device_test.py b/tests/mobly/controllers/android_device_test.py
index c64c826..1c4be14 100755
--- a/tests/mobly/controllers/android_device_test.py
+++ b/tests/mobly/controllers/android_device_test.py
@@ -27,6 +27,7 @@
 from mobly import runtime_test_info
 from mobly.controllers import android_device
 from mobly.controllers.android_device_lib import adb
+from mobly.controllers.android_device_lib import errors
 from mobly.controllers.android_device_lib import snippet_client
 from mobly.controllers.android_device_lib.services import base_service
 from mobly.controllers.android_device_lib.services import logcat
@@ -136,6 +137,61 @@
     with self.assertRaisesRegex(android_device.Error, expected_msg):
       android_device.create([1])
 
+  @mock.patch('mobly.controllers.android_device.list_adb_devices')
+  @mock.patch('mobly.controllers.android_device.list_adb_devices_by_usb_id')
+  @mock.patch('mobly.controllers.android_device.AndroidDevice')
+  def test_get_instances(self, mock_ad_class, mock_list_adb_usb, mock_list_adb):
+    mock_list_adb.return_value = ['1']
+    mock_list_adb_usb.return_value = []
+    android_device.get_instances(['1'])
+    mock_ad_class.assert_called_with('1')
+
+  @mock.patch('mobly.controllers.android_device.list_adb_devices')
+  @mock.patch('mobly.controllers.android_device.list_adb_devices_by_usb_id')
+  @mock.patch('mobly.controllers.android_device.AndroidDevice')
+  def test_get_instances_do_not_exist(self, mock_ad_class, mock_list_adb_usb,
+                                      mock_list_adb):
+    mock_list_adb.return_value = []
+    mock_list_adb_usb.return_value = []
+    with self.assertRaisesRegex(
+        errors.Error,
+        'Android device serial "1" is specified in config but is not reachable'
+    ):
+      android_device.get_instances(['1'])
+
+  @mock.patch('mobly.controllers.android_device.list_adb_devices')
+  @mock.patch('mobly.controllers.android_device.list_adb_devices_by_usb_id')
+  @mock.patch('mobly.controllers.android_device.AndroidDevice')
+  def test_get_instances_with_configs(self, mock_ad_class, mock_list_adb_usb,
+                                      mock_list_adb):
+    mock_list_adb.return_value = ['1']
+    mock_list_adb_usb.return_value = []
+    config = {'serial': '1'}
+    android_device.get_instances_with_configs([config])
+    mock_ad_class.assert_called_with('1')
+
+  def test_get_instances_with_configs_invalid_config(self):
+    config = {'something': 'random'}
+    with self.assertRaisesRegex(
+        errors.Error,
+        f'Required value "serial" is missing in AndroidDevice config {config}'):
+      android_device.get_instances_with_configs([config])
+
+  @mock.patch('mobly.controllers.android_device.list_adb_devices')
+  @mock.patch('mobly.controllers.android_device.list_adb_devices_by_usb_id')
+  @mock.patch('mobly.controllers.android_device.AndroidDevice')
+  def test_get_instances_with_configsdo_not_exist(self, mock_ad_class,
+                                                  mock_list_adb_usb,
+                                                  mock_list_adb):
+    mock_list_adb.return_value = []
+    mock_list_adb_usb.return_value = []
+    config = {'serial': '1'}
+    with self.assertRaisesRegex(
+        errors.Error,
+        'Android device serial "1" is specified in config but is not reachable'
+    ):
+      android_device.get_instances_with_configs([config])
+
   def test_get_devices_success_with_extra_field(self):
     ads = mock_android_device.get_mock_ads(5)
     expected_label = 'selected'