autotest: Support adb host without serial

The current code assumes `adb devices` will always return valid serial
numbers, but there are cases where serial numbers are not available.

We have one such device where this is often the case, particularly when
we re-flash the firmware.

This should not prevent the tests from running, particularly when there
is only a single device attached, in which case `adb shell` command does
not really require a `-s` parameter with the serial number.

Tested on a device without serial number:

  $ adb devices
  List of devices attached
  (no serial number)      device

Autotest did not fail with error.AutoservError('No ADB devices attached.')

The code also checks for whether a device without serial number is
present when there are multiple devices attached and raises an exception
in that case.  Right now the code would raise an exception anyways,
error.AutoservError('Multiple ADB devices attached.'), but once that
check is lifted and we support multiple adb devices attached to the same
host, we will want to check for that particular case.

BUG=None
TEST=Tested on a device without serial number.

Change-Id: I9f456d222b5eef226c4f343491aac4645dad3e68
Reviewed-on: https://chromium-review.googlesource.com/293552
Reviewed-by: Christopher Wiley <wiley@chromium.org>
Reviewed-by: Shelley Chen <shchen@chromium.org>
Reviewed-by: Fang Deng <fdeng@chromium.org>
Commit-Queue: Filipe Brandenburger <filbranden@chromium.org>
Tested-by: Filipe Brandenburger <filbranden@chromium.org>
diff --git a/server/hosts/adb_host.py b/server/hosts/adb_host.py
index 6b58d25..e8a7998 100644
--- a/server/hosts/adb_host.py
+++ b/server/hosts/adb_host.py
@@ -14,12 +14,18 @@
 
 
 SHELL_CMD = 'shell'
+# Some devices have no serial, then `adb serial` has output such as:
+# (no serial number)  device
+# ??????????          device
+DEVICE_NO_SERIAL_MSG = '(no serial number)'
+DEVICE_NO_SERIAL_TAG = '<NO_SERIAL>'
 # Regex to find an adb device. Examples:
 # 0146B5580B01801B    device
 # 018e0ecb20c97a62    device
 # 172.22.75.141:5555  device
-DEVICE_FINDER_REGEX = ('^(?P<SERIAL>([\w]+)|(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}))'
-                       '([:]5555)?[ \t]+device')
+DEVICE_FINDER_REGEX = ('^(?P<SERIAL>([\w]+)|(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})|' +
+                       re.escape(DEVICE_NO_SERIAL_MSG) +
+                       ')([:]5555)?[ \t]+device')
 CMD_OUTPUT_PREFIX = 'ADB_CMD_OUTPUT'
 CMD_OUTPUT_REGEX = ('(?P<OUTPUT>[\s\S]*)%s:(?P<EXIT_CODE>\d{1,3})' %
                     CMD_OUTPUT_PREFIX)
@@ -163,7 +169,7 @@
         cmd = 'adb '
         if self._use_tcpip and not use_serial:
             cmd += '-s %s:5555 ' % self._device_hostname
-        elif self._serial:
+        elif self._serial and self._serial != DEVICE_NO_SERIAL_TAG:
             cmd += '-s %s ' % self._serial
         if shell:
             cmd += '%s ' % SHELL_CMD
@@ -361,8 +367,15 @@
             match = re.search(DEVICE_FINDER_REGEX,
                               line)
             if match:
-                logging.debug('Found Device: %s', match.group('SERIAL'))
-                devices.append(match.group('SERIAL'))
+                serial = match.group('SERIAL')
+                if serial == DEVICE_NO_SERIAL_MSG or re.match(r'^\?+$', serial):
+                    serial = DEVICE_NO_SERIAL_TAG
+                logging.debug('Found Device: %s', serial)
+                devices.append(serial)
+        if len(devices) != 1 and DEVICE_NO_SERIAL_TAG in devices:
+            raise error.AutoservError(
+                'Multiple ADB devices attached '
+                '*and* devices without serial number are present.')
         return devices