firmware_DevScreenTimeout: Add support for tablets/detachables devices
am: bed4308505

Change-Id: I286937602a1bea25f7f2e45b570a38b754da5f9b
diff --git a/server/cros/faft/utils/mode_switcher.py b/server/cros/faft/utils/mode_switcher.py
index 579e104..618fe79 100644
--- a/server/cros/faft/utils/mode_switcher.py
+++ b/server/cros/faft/utils/mode_switcher.py
@@ -19,6 +19,10 @@
 class _BaseFwBypasser(object):
     """Base class that controls bypass logic for firmware screens."""
 
+    # Duration of holding Volume down button to quickly bypass the developer
+    # warning screen in tablets/detachables.
+    HOLD_VOL_DOWN_BUTTON_BYPASS = 3
+
     def __init__(self, faft_framework):
         self.servo = faft_framework.servo
         self.faft_config = faft_framework.faft_config
@@ -59,9 +63,21 @@
     """Controls bypass logic via Ctrl-D combo."""
 
     def bypass_dev_mode(self):
-        """Bypass the dev mode firmware logic to boot internal image."""
-        time.sleep(self.faft_config.firmware_screen)
-        self.servo.ctrl_d()
+        """Bypass the dev mode firmware logic to boot internal image.
+
+        Press Ctrl-D repeatedly. To obtain a low firmware boot time, pressing
+        Ctrl+D for every half second until firmware_screen delay has been
+        reached.
+        """
+        logging.info("Pressing Ctrl-D.")
+        # At maximum, device waits for twice of firmware_screen delay to
+        # bypass the Dev screen.
+        timeout = time.time() + (self.faft_config.firmware_screen * 2)
+        while time.time() < timeout:
+            self.servo.ctrl_d()
+            time.sleep(0.5)
+            if self.client_host.ping_wait_up(timeout=0.1):
+                break
 
 
     def bypass_dev_boot_usb(self):
@@ -217,31 +233,29 @@
 
 
     def bypass_dev_mode(self):
-        """Bypass the dev mode firmware logic to boot internal image
+        """Bypass the developer warning screen immediately to boot into
+        internal disk.
 
-        On tablets/ detachables, recovery entered by pressing pwr, vol up
-        & vol down buttons for 10s.
-           Menu options seen in DEVELOPER WARNING screen:
-                 Developer Options
-                 Show Debug Info
-                 Enable Root Verification
-                 Power Off*
-                 Language
-           Menu options seen in DEV screen:
-                 Boot legacy BIOS
-                 Boot USB image
-                 Boot developer image*
-                 Cancel
-                 Power off
-                 Language
-        Vol up button selects previous item, vol down button selects
-        next item and pwr button selects current activated item.
+        On tablets/detachables, press & holding the Volume down button for
+        3-seconds will quickly bypass the developer warning screen.
         """
-        self.trigger_dev_screen()
-        time.sleep(self.faft_config.firmware_screen)
-        logging.info('Selecting power as enter key to select '
-                     'Boot Developer Image')
-        self.servo.power_short_press()
+        # Unit for the "volume_down_hold" console command is msec.
+        duration = (self.HOLD_VOL_DOWN_BUTTON_BYPASS + 0.1) * 1000
+        logging.info("Press and hold volume down button for %.1f seconds to "
+                     "immediately bypass the Developer warning screen.",
+                     self.HOLD_VOL_DOWN_BUTTON_BYPASS + 0.1)
+        # At maximum, device waits for twice of firmware_screen delay to
+        # bypass the Dev screen.
+        timeout = time.time() + (self.faft_config.firmware_screen * 2)
+        # To obtain a low firmware boot time, volume_down button pressed for
+        # every 3.1 seconds until firmware_screen delay has been reached.
+        while time.time() < timeout:
+            self.servo.set_nocheck('volume_down_hold', duration)
+            # After pressing 'volume_down_hold' button, wait for 0.2 seconds
+            # before start pressing the button for next iteration.
+            time.sleep(self.HOLD_VOL_DOWN_BUTTON_BYPASS + 0.2)
+            if self.client_host.ping_wait_up(timeout=0.1):
+                break
 
 
     def trigger_dev_screen(self):
@@ -386,6 +400,8 @@
 class _BaseModeSwitcher(object):
     """Base class that controls firmware mode switching."""
 
+    HOLD_VOL_DOWN_BUTTON_BYPASS = _BaseFwBypasser.HOLD_VOL_DOWN_BUTTON_BYPASS
+
     def __init__(self, faft_framework):
         self.faft_framework = faft_framework
         self.client_host = faft_framework._client
diff --git a/server/site_tests/firmware_DevScreenTimeout/firmware_DevScreenTimeout.py b/server/site_tests/firmware_DevScreenTimeout/firmware_DevScreenTimeout.py
index 772dd74..114c59f 100644
--- a/server/site_tests/firmware_DevScreenTimeout/firmware_DevScreenTimeout.py
+++ b/server/site_tests/firmware_DevScreenTimeout/firmware_DevScreenTimeout.py
@@ -18,7 +18,7 @@
     it will boot to developer mode. This test is to verify the timeout period.
 
     This test tries to boot the system in developer mode twice.
-    The first one will repeatly press Ctrl-D on booting in order to reduce
+    The first one will repeatedly press Ctrl-D on booting in order to reduce
     the time on developer warning screen. The second one will do nothing and
     wait the developer screen timeout. The time difference of these two boots
     is close to the developer screen timeout.
@@ -32,28 +32,16 @@
 
     fw_time_record = {}
 
-    def ctrl_d_repeatedly(self):
-        """Press Ctrl-D repeatedly. We want to be aggressive and obtain a
-        low firmware boot time when developer mode is enabled, so spam the
-        AP console with ctrl-D every half second until the firmware_screen
-        delay has been reached.
-        """
-        for _ in range(self.faft_config.firmware_screen * 2):
-            self.servo.ctrl_d()
-            time.sleep(0.5)
 
     def record_fw_boot_time(self, tag):
         """Record the current firmware boot time with the tag.
 
-        Args:
-          tag: A tag about this boot.
-
-        Raises:
-          error.TestError: If the firmware-boot-time file does not exist.
+        @param tag: A tag about this boot.
+        @raise TestError: If the firmware-boot-time file does not exist.
         """
         [fw_time] = self.faft_client.system.run_shell_command_get_output(
                 'cat /tmp/firmware-boot-time')
-        logging.info('Got firmware boot time: %s', fw_time)
+        logging.info('Got firmware boot time [%s]: %s', tag, fw_time)
         if fw_time:
             self.fw_time_record[tag] = float(fw_time)
         else:
@@ -62,13 +50,21 @@
     def check_timeout_period(self):
         """Check the firmware screen timeout period matches our spec.
 
-        Raises:
-          error.TestFail: If the timeout period does not match our spec.
+        @raise TestFail: If the timeout period does not match our spec.
         """
-        # Record the boot time of firmware screen timeout.
-        self.record_fw_boot_time('timeout_boot')
+        # On tablets/detachables, eliminate the time taken for pressing volume
+        # down button (HOLD_VOL_DOWN_BUTTON_BYPASS + 0.1) to calculate the
+        # firmware boot time of quick dev boot.
+        if self.faft_config.fw_bypasser_type == 'tablet_detachable_bypasser':
+            self.fw_time_record['quick_bypass_boot'] -= \
+                            (self.switcher.HOLD_VOL_DOWN_BUTTON_BYPASS + 0.1)
+            logging.info("Firmware boot time [quick_bypass_boot] after "
+                         "eliminating the volume down button press time "
+                         "(%.1f seconds): %s",
+                         self.switcher.HOLD_VOL_DOWN_BUTTON_BYPASS + 0.1,
+                         self.fw_time_record['quick_bypass_boot'])
         got_timeout = (self.fw_time_record['timeout_boot'] -
-                       self.fw_time_record['ctrl_d_boot'])
+                       self.fw_time_record['quick_bypass_boot'])
         logging.info('Estimated developer firmware timeout: %s', got_timeout)
 
         if (abs(got_timeout - self.faft_config.dev_screen_timeout) >
@@ -82,8 +78,10 @@
     def initialize(self, host, cmdline_args):
         super(firmware_DevScreenTimeout, self).initialize(host, cmdline_args)
         # NA error check point for this test
-        if self.faft_config.fw_bypasser_type != 'ctrl_d_bypasser':
-            raise error.TestNAError("This test is only valid on devices with screens.")
+        if (self.faft_config.fw_bypasser_type != 'ctrl_d_bypasser' and
+                self.faft_config.fw_bypasser_type != 'tablet_detachable_bypasser'):
+            raise error.TestNAError("This test is only valid on devices with "
+                                    "screens.")
         # This test is run on developer mode only.
         self.switcher.setup_mode('dev')
         self.setup_usbkey(usbkey=False)
@@ -100,17 +98,21 @@
         # to avoid TPM reset too long
         self.switcher.simple_reboot()
         self.switcher.wait_for_client()
-        logging.info("Reboot and press Ctrl-D repeatedly.")
+        logging.info("Reboot and bypass the Developer warning screen "
+                     "immediately.")
         self.switcher.simple_reboot()
-        self.ctrl_d_repeatedly()
+        self.switcher.bypass_dev_mode()
         self.switcher.wait_for_client()
+        logging.info("Record the firmware boot time without waiting for "
+                     "firmware screen.")
+        self.record_fw_boot_time('quick_bypass_boot')
 
-        logging.info("Record the firmware boot time without waiting "
-                     "firmware screen; on next reboot, do nothing and wait the "
-                     "screen timeout.")
-        self.record_fw_boot_time('ctrl_d_boot')
+        logging.info("Reboot the device, do nothing and wait for screen "
+                     "timeout. And then record the firmware boot time.")
         self.switcher.simple_reboot()
         self.switcher.wait_for_client()
+        # Record the boot time of firmware screen timeout.
+        self.record_fw_boot_time('timeout_boot')
 
         logging.info("Check the firmware screen timeout matches our spec.")
         self.check_timeout_period()