Home | History | Annotate | Download | only in utils
      1 # Copyright 2015 The Chromium OS Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 import logging
      6 import time
      7 import sys
      8 
      9 from multiprocessing import Process
     10 from autotest_lib.client.bin import utils
     11 from autotest_lib.client.common_lib import error
     12 from autotest_lib.client.cros.faft.utils import shell_wrapper
     13 
     14 class ConnectionError(Exception):
     15     """Raised on an error of connecting DUT."""
     16     pass
     17 
     18 
     19 class _BaseFwBypasser(object):
     20     """Base class that controls bypass logic for firmware screens."""
     21 
     22     def __init__(self, faft_framework):
     23         self.servo = faft_framework.servo
     24         self.faft_config = faft_framework.faft_config
     25         self.client_host = faft_framework._client
     26 
     27 
     28     def bypass_dev_mode(self):
     29         """Bypass the dev mode firmware logic to boot internal image."""
     30         raise NotImplementedError
     31 
     32 
     33     def bypass_dev_boot_usb(self):
     34         """Bypass the dev mode firmware logic to boot USB."""
     35         raise NotImplementedError
     36 
     37 
     38     def bypass_rec_mode(self):
     39         """Bypass the rec mode firmware logic to boot USB."""
     40         raise NotImplementedError
     41 
     42 
     43     def trigger_dev_to_rec(self):
     44         """Trigger to the rec mode from the dev screen."""
     45         raise NotImplementedError
     46 
     47 
     48     def trigger_rec_to_dev(self):
     49         """Trigger to the dev mode from the rec screen."""
     50         raise NotImplementedError
     51 
     52 
     53     def trigger_dev_to_normal(self):
     54         """Trigger to the normal mode from the dev screen."""
     55         raise NotImplementedError
     56 
     57 
     58 class _CtrlDBypasser(_BaseFwBypasser):
     59     """Controls bypass logic via Ctrl-D combo."""
     60 
     61     def bypass_dev_mode(self):
     62         """Bypass the dev mode firmware logic to boot internal image."""
     63         time.sleep(self.faft_config.firmware_screen)
     64         self.servo.ctrl_d()
     65 
     66 
     67     def bypass_dev_boot_usb(self):
     68         """Bypass the dev mode firmware logic to boot USB."""
     69         time.sleep(self.faft_config.firmware_screen)
     70         self.servo.ctrl_u()
     71 
     72 
     73     def bypass_rec_mode(self):
     74         """Bypass the rec mode firmware logic to boot USB."""
     75         self.servo.switch_usbkey('host')
     76         time.sleep(self.faft_config.usb_plug)
     77         self.servo.switch_usbkey('dut')
     78         logging.info('Enabled dut_sees_usb')
     79         if not self.client_host.ping_wait_up(
     80                 timeout=self.faft_config.delay_reboot_to_ping):
     81             logging.info('ping timed out, try REC_ON')
     82             psc = self.servo.get_power_state_controller()
     83             psc.power_on(psc.REC_ON)
     84 
     85 
     86     def trigger_dev_to_rec(self):
     87         """Trigger to the rec mode from the dev screen."""
     88         time.sleep(self.faft_config.firmware_screen)
     89 
     90         # Pressing Enter for too long triggers a second key press.
     91         # Let's press it without delay
     92         self.servo.enter_key(press_secs=0)
     93 
     94         # For Alex/ZGB, there is a dev warning screen in text mode.
     95         # Skip it by pressing Ctrl-D.
     96         if self.faft_config.need_dev_transition:
     97             time.sleep(self.faft_config.legacy_text_screen)
     98             self.servo.ctrl_d()
     99 
    100 
    101     def trigger_rec_to_dev(self):
    102         """Trigger to the dev mode from the rec screen."""
    103         time.sleep(self.faft_config.firmware_screen)
    104         self.servo.ctrl_d()
    105         time.sleep(self.faft_config.confirm_screen)
    106         if self.faft_config.rec_button_dev_switch:
    107             logging.info('RECOVERY button pressed to switch to dev mode')
    108             self.servo.toggle_recovery_switch()
    109         else:
    110             logging.info('ENTER pressed to switch to dev mode')
    111             self.servo.enter_key()
    112 
    113 
    114     def trigger_dev_to_normal(self):
    115         """Trigger to the normal mode from the dev screen."""
    116         time.sleep(self.faft_config.firmware_screen)
    117         self.servo.enter_key()
    118         time.sleep(self.faft_config.confirm_screen)
    119         self.servo.enter_key()
    120 
    121 
    122 class _JetstreamBypasser(_BaseFwBypasser):
    123     """Controls bypass logic of Jetstream devices."""
    124 
    125     def bypass_dev_mode(self):
    126         """Bypass the dev mode firmware logic to boot internal image."""
    127         # Jetstream does nothing to bypass.
    128         pass
    129 
    130 
    131     def bypass_dev_boot_usb(self):
    132         """Bypass the dev mode firmware logic to boot USB."""
    133         self.servo.switch_usbkey('dut')
    134         time.sleep(self.faft_config.firmware_screen)
    135         self.servo.toggle_development_switch()
    136 
    137 
    138     def bypass_rec_mode(self):
    139         """Bypass the rec mode firmware logic to boot USB."""
    140         self.servo.switch_usbkey('host')
    141         time.sleep(self.faft_config.usb_plug)
    142         self.servo.switch_usbkey('dut')
    143         if not self.client_host.ping_wait_up(
    144                 timeout=self.faft_config.delay_reboot_to_ping):
    145             psc = self.servo.get_power_state_controller()
    146             psc.power_on(psc.REC_ON)
    147 
    148 
    149     def trigger_dev_to_rec(self):
    150         """Trigger to the rec mode from the dev screen."""
    151         # Jetstream does not have this triggering logic.
    152         raise NotImplementedError
    153 
    154 
    155     def trigger_rec_to_dev(self):
    156         """Trigger to the dev mode from the rec screen."""
    157         self.servo.disable_development_mode()
    158         time.sleep(self.faft_config.firmware_screen)
    159         self.servo.toggle_development_switch()
    160 
    161 
    162     def trigger_dev_to_normal(self):
    163         """Trigger to the normal mode from the dev screen."""
    164         # Jetstream does not have this triggering logic.
    165         raise NotImplementedError
    166 
    167 
    168 class _TabletDetachableBypasser(_BaseFwBypasser):
    169     """Controls bypass logic of tablet/ detachable chromebook devices."""
    170 
    171     def set_button(self, button, duration, info):
    172         """Helper method that sets the button hold time for UI selections"""
    173         self.servo.set_nocheck(button, duration)
    174         time.sleep(self.faft_config.confirm_screen)
    175         logging.info(info)
    176 
    177 
    178     def bypass_dev_boot_usb(self):
    179         """Bypass the dev mode firmware logic to boot USB.
    180 
    181         On tablets/ detachables, recovery entered by pressing pwr, vol up
    182         & vol down buttons for 10s.
    183            Menu options seen in DEVELOPER WARNING screen:
    184                  Developer Options
    185                  Show Debug Info
    186                  Enable Root Verification
    187                  Power Off*
    188                  Language
    189            Menu options seen in DEV screen:
    190                  Boot legacy BIOS
    191                  Boot USB image
    192                  Boot developer image*
    193                  Cancel
    194                  Power off
    195                  Language
    196         Vol up button selects previous item, vol down button selects
    197         next item and pwr button selects current activated item.
    198         """
    199         self.trigger_dev_screen()
    200         time.sleep(self.faft_config.firmware_screen)
    201         self.set_button('volume_up_hold', 100, ('Selecting power as'
    202                         ' enter key to select Boot USB Image'))
    203         self.servo.power_short_press()
    204 
    205 
    206     def bypass_rec_mode(self):
    207         """Bypass the rec mode firmware logic to boot USB."""
    208         self.servo.switch_usbkey('host')
    209         time.sleep(self.faft_config.usb_plug)
    210         self.servo.switch_usbkey('dut')
    211         logging.info('Enabled dut_sees_usb')
    212         if not self.client_host.ping_wait_up(
    213                 timeout=self.faft_config.delay_reboot_to_ping):
    214             logging.info('ping timed out, try REC_ON')
    215             psc = self.servo.get_power_state_controller()
    216             psc.power_on(psc.REC_ON)
    217 
    218 
    219     def bypass_dev_mode(self):
    220         """Bypass the dev mode firmware logic to boot internal image
    221 
    222         On tablets/ detachables, recovery entered by pressing pwr, vol up
    223         & vol down buttons for 10s.
    224            Menu options seen in DEVELOPER WARNING screen:
    225                  Developer Options
    226                  Show Debug Info
    227                  Enable Root Verification
    228                  Power Off*
    229                  Language
    230            Menu options seen in DEV screen:
    231                  Boot legacy BIOS
    232                  Boot USB image
    233                  Boot developer image*
    234                  Cancel
    235                  Power off
    236                  Language
    237         Vol up button selects previous item, vol down button selects
    238         next item and pwr button selects current activated item.
    239         """
    240         self.trigger_dev_screen()
    241         time.sleep(self.faft_config.firmware_screen)
    242         logging.info('Selecting power as enter key to select '
    243                      'Boot Developer Image')
    244         self.servo.power_short_press()
    245 
    246 
    247     def trigger_dev_screen(self):
    248         """Helper method that transitions from DEVELOPER WARNING to DEV screen
    249 
    250            Menu options seen in DEVELOPER WARNING screen:
    251                  Developer Options
    252                  Show Debug Info
    253                  Enable Root Verification
    254                  Power Off*
    255                  Language
    256            Menu options seen in DEV screen:
    257                  Boot legacy BIOS
    258                  Boot USB image
    259                  Boot developer image*
    260                  Cancel
    261                  Power off
    262                  Language
    263         Vol up button selects previous item, vol down button selects
    264         next item and pwr button selects current activated item.
    265         """
    266         time.sleep(self.faft_config.firmware_screen)
    267         self.servo.set_nocheck('volume_up_hold', 100)
    268         time.sleep(self.faft_config.confirm_screen)
    269         self.servo.set_nocheck('volume_up_hold', 100)
    270         time.sleep(self.faft_config.confirm_screen)
    271         self.set_button('volume_up_hold', 100, ('Selecting power '
    272                         'as enter key to select Developer Options'))
    273         self.servo.power_short_press()
    274 
    275 
    276     def trigger_rec_to_dev(self):
    277         """Trigger to the dev mode from the rec screen using vol up button.
    278 
    279         On tablets/ detachables, recovery entered by pressing pwr, vol up
    280         & vol down buttons for 10s. TO_DEV screen is entered by pressing
    281         vol up & vol down buttons together on the INSERT screen.
    282            Menu options seen in TO_DEV screen:
    283                  Confirm enabling developer mode
    284                  Cancel*
    285                  Power off
    286                  Language
    287         Vol up button selects previous item, vol down button selects
    288         next item and pwr button selects current activated item.
    289         """
    290         time.sleep(self.faft_config.firmware_screen)
    291         self.set_button('volume_up_down_hold', 100, ('Enter Recovery Menu.'))
    292         time.sleep(self.faft_config.confirm_screen)
    293         self.set_button('volume_up_hold', 100, ('Selecting power as '
    294                         'enter key to select Confirm Enabling Developer Mode'))
    295         self.servo.power_short_press()
    296         time.sleep(self.faft_config.firmware_screen)
    297 
    298 
    299     def trigger_dev_to_normal(self):
    300         """Trigger to the normal mode from the dev screen.
    301 
    302            Menu options seen in DEVELOPER WARNING screen:
    303                  Developer Options
    304                  Show Debug Info
    305                  Enable Root Verification
    306                  Power Off*
    307                  Language
    308            Menu options seen in TO_NORM screen:
    309                  Confirm Enabling Verified Boot*
    310                  Cancel
    311                  Power off
    312                  Language
    313         Vol up button selects previous item, vol down button selects
    314         next item and pwr button selects current activated item.
    315         """
    316         time.sleep(self.faft_config.firmware_screen)
    317         self.set_button('volume_up_hold', 100, ('Selecting '
    318                         'Enable Root Verification using pwr '
    319                         'button to enter TO_NORM screen'))
    320         self.servo.power_short_press()
    321         logging.info('Transitioning from DEV to TO_NORM screen.')
    322         time.sleep(self.faft_config.firmware_screen)
    323         logging.info('Selecting Confirm Enabling Verified '
    324                         'Boot using pwr button in '
    325                         'TO_NORM screen')
    326         self.servo.power_short_press()
    327 
    328     def trigger_dev_to_rec(self):
    329         """Trigger to the TO_NORM screen from the dev screen.
    330            Menu options seen in DEVELOPER WARNING screen:
    331                  Developer Options
    332                  Show Debug Info
    333                  Enable Root Verification
    334                  Power Off*
    335                  Language
    336            Menu options seen in TO_NORM screen:
    337                  Confirm Enabling Verified Boot*
    338                  Cancel
    339                  Power off
    340                  Language
    341         Vol up button selects previous item, vol down button selects
    342         next item and pwr button selects current activated item.
    343         """
    344         time.sleep(self.faft_config.firmware_screen)
    345         self.set_button('volume_up_hold', 100, ('Selecting '
    346                         'Enable Root Verification using pwr '
    347                         'button to enter TO_NORM screen'))
    348         self.servo.power_short_press()
    349         logging.info('Transitioning from DEV to TO_NORM screen.')
    350         time.sleep(self.faft_config.firmware_screen)
    351 
    352         # In firmware_FwScreenPressPower, test will power off the DUT using
    353         # Power button in second screen (TO_NORM screen) so scrolling to
    354         # Power-off is necessary in this case. Hence scroll to Power-off as
    355         # a generic action and wait for next action of either Lid close or
    356         # power button press.
    357         self.servo.set_nocheck('volume_down_hold', 100)
    358         time.sleep(self.faft_config.confirm_screen)
    359         self.servo.set_nocheck('volume_down_hold', 100)
    360         time.sleep(self.faft_config.confirm_screen)
    361 
    362 def _create_fw_bypasser(faft_framework):
    363     """Creates a proper firmware bypasser.
    364 
    365     @param faft_framework: The main FAFT framework object.
    366     """
    367     bypasser_type = faft_framework.faft_config.fw_bypasser_type
    368     if bypasser_type == 'ctrl_d_bypasser':
    369         logging.info('Create a CtrlDBypasser')
    370         return _CtrlDBypasser(faft_framework)
    371     elif bypasser_type == 'jetstream_bypasser':
    372         logging.info('Create a JetstreamBypasser')
    373         return _JetstreamBypasser(faft_framework)
    374     elif bypasser_type == 'ryu_bypasser':
    375         # FIXME Create an RyuBypasser
    376         logging.info('Create a CtrlDBypasser')
    377         return _CtrlDBypasser(faft_framework)
    378     elif bypasser_type == 'tablet_detachable_bypasser':
    379         logging.info('Create a TabletDetachableBypasser')
    380         return _TabletDetachableBypasser(faft_framework)
    381     else:
    382         raise NotImplementedError('Not supported fw_bypasser_type: %s',
    383                                   bypasser_type)
    384 
    385 
    386 class _BaseModeSwitcher(object):
    387     """Base class that controls firmware mode switching."""
    388 
    389     def __init__(self, faft_framework):
    390         self.faft_framework = faft_framework
    391         self.client_host = faft_framework._client
    392         self.faft_client = faft_framework.faft_client
    393         self.servo = faft_framework.servo
    394         self.faft_config = faft_framework.faft_config
    395         self.checkers = faft_framework.checkers
    396         self.bypasser = _create_fw_bypasser(faft_framework)
    397         self._backup_mode = None
    398 
    399 
    400     def setup_mode(self, mode):
    401         """Setup for the requested mode.
    402 
    403         It makes sure the system in the requested mode. If not, it tries to
    404         do so.
    405 
    406         @param mode: A string of mode, one of 'normal', 'dev', or 'rec'.
    407         @raise TestFail: If the system not switched to expected mode after
    408                          reboot_to_mode.
    409 
    410         """
    411         if not self.checkers.mode_checker(mode):
    412             logging.info('System not in expected %s mode. Reboot into it.',
    413                          mode)
    414             if self._backup_mode is None:
    415                 # Only resume to normal/dev mode after test, not recovery.
    416                 self._backup_mode = 'dev' if mode == 'normal' else 'normal'
    417             self.reboot_to_mode(mode)
    418             if not self.checkers.mode_checker(mode):
    419                 raise error.TestFail('System not switched to expected %s'
    420                         ' mode after setup_mode.' % mode)
    421 
    422     def restore_mode(self):
    423         """Restores original dev mode status if it has changed.
    424 
    425         @raise TestFail: If the system not restored to expected mode.
    426         """
    427         if (self._backup_mode is not None and
    428             not self.checkers.mode_checker(self._backup_mode)):
    429             self.reboot_to_mode(self._backup_mode)
    430             if not self.checkers.mode_checker(self._backup_mode):
    431                 raise error.TestFail('System not restored to expected %s'
    432                         ' mode in cleanup.' % self._backup_mode)
    433 
    434 
    435 
    436     def reboot_to_mode(self, to_mode, from_mode=None, sync_before_boot=True,
    437                        wait_for_dut_up=True):
    438         """Reboot and execute the mode switching sequence.
    439 
    440         @param to_mode: The target mode, one of 'normal', 'dev', or 'rec'.
    441         @param from_mode: The original mode, optional, one of 'normal, 'dev',
    442                           or 'rec'.
    443         @param sync_before_boot: True to sync to disk before booting.
    444         @param wait_for_dut_up: True to wait DUT online again. False to do the
    445                                 reboot and mode switching sequence only and may
    446                                 need more operations to pass the firmware
    447                                 screen.
    448         """
    449         logging.info('-[ModeSwitcher]-[ start reboot_to_mode(%r, %r, %r) ]-',
    450                      to_mode, from_mode, wait_for_dut_up)
    451         if sync_before_boot:
    452             self.faft_framework.blocking_sync()
    453         if to_mode == 'rec':
    454             self._enable_rec_mode_and_reboot(usb_state='dut')
    455             if wait_for_dut_up:
    456                 self.wait_for_client()
    457 
    458         elif to_mode == 'dev':
    459             self._enable_dev_mode_and_reboot()
    460             if wait_for_dut_up:
    461                 self.bypass_dev_mode()
    462                 self.wait_for_client()
    463 
    464         elif to_mode == 'normal':
    465             self._enable_normal_mode_and_reboot()
    466             if wait_for_dut_up:
    467                 self.wait_for_client()
    468 
    469         else:
    470             raise NotImplementedError(
    471                     'Not supported mode switching from %s to %s' %
    472                      (str(from_mode), to_mode))
    473         logging.info('-[ModeSwitcher]-[ end reboot_to_mode(%r, %r, %r) ]-',
    474                      to_mode, from_mode, wait_for_dut_up)
    475 
    476     def simple_reboot(self, reboot_type='warm', sync_before_boot=True):
    477         """Simple reboot method
    478 
    479         Just reboot the DUT using either cold or warm reset.  Does not wait for
    480         DUT to come back online.  Will wait for test to handle this.
    481 
    482         @param reboot_type: A string of reboot type, 'warm' or 'cold'.
    483                             If reboot_type != warm/cold, raise exception.
    484         @param sync_before_boot: True to sync to disk before booting.
    485                                  If sync_before_boot=False, DUT offline before
    486                                  calling mode_aware_reboot.
    487         """
    488         if reboot_type == 'warm':
    489             reboot_method = self.servo.get_power_state_controller().warm_reset
    490         elif reboot_type == 'cold':
    491             reboot_method = self.servo.get_power_state_controller().reset
    492         else:
    493             raise NotImplementedError('Not supported reboot_type: %s',
    494                                       reboot_type)
    495         if sync_before_boot:
    496             boot_id = self.faft_framework.get_bootid()
    497             self.faft_framework.blocking_sync()
    498         logging.info("-[ModeSwitcher]-[ start simple_reboot(%r) ]-",
    499                      reboot_type)
    500         reboot_method()
    501         if sync_before_boot:
    502             self.wait_for_client_offline(orig_boot_id=boot_id)
    503         logging.info("-[ModeSwitcher]-[ end simple_reboot(%r) ]-",
    504                      reboot_type)
    505 
    506     def mode_aware_reboot(self, reboot_type=None, reboot_method=None,
    507                           sync_before_boot=True, wait_for_dut_up=True):
    508         """Uses a mode-aware way to reboot DUT.
    509 
    510         For example, if DUT is in dev mode, it requires pressing Ctrl-D to
    511         bypass the developer screen.
    512 
    513         @param reboot_type: A string of reboot type, one of 'warm', 'cold', or
    514                             'custom'. Default is a warm reboot.
    515         @param reboot_method: A custom method to do the reboot. Only use it if
    516                               reboot_type='custom'.
    517         @param sync_before_boot: True to sync to disk before booting.
    518                                  If sync_before_boot=False, DUT offline before
    519                                  calling mode_aware_reboot.
    520         @param wait_for_dut_up: True to wait DUT online again. False to do the
    521                                 reboot only.
    522         """
    523         if reboot_type is None or reboot_type == 'warm':
    524             reboot_method = self.servo.get_power_state_controller().warm_reset
    525         elif reboot_type == 'cold':
    526             reboot_method = self.servo.get_power_state_controller().reset
    527         elif reboot_type != 'custom':
    528             raise NotImplementedError('Not supported reboot_type: %s',
    529                                       reboot_type)
    530 
    531         logging.info("-[ModeSwitcher]-[ start mode_aware_reboot(%r, %s, ..) ]-",
    532                      reboot_type, reboot_method.__name__)
    533         is_dev = is_rec = is_devsw_boot = False
    534         if sync_before_boot:
    535             is_dev = self.checkers.mode_checker('dev')
    536             is_rec = self.checkers.mode_checker('rec')
    537             is_devsw_boot = self.checkers.crossystem_checker(
    538                                                {'devsw_boot': '1'}, True)
    539             boot_id = self.faft_framework.get_bootid()
    540             self.faft_framework.blocking_sync()
    541         if is_rec:
    542             logging.info("-[mode_aware_reboot]-[ is_rec=%s is_dev_switch=%s ]-",
    543                          is_rec, is_devsw_boot)
    544         else:
    545             logging.info("-[mode_aware_reboot]-[ is_dev=%s ]-", is_dev)
    546         reboot_method()
    547         if sync_before_boot:
    548             self.wait_for_client_offline(orig_boot_id=boot_id)
    549         # Encapsulating the behavior of skipping dev firmware screen,
    550         # hitting ctrl-D
    551         # Note that if booting from recovery mode, we can predict the next
    552         # boot based on the developer switch position at boot (devsw_boot).
    553         # If devsw_boot is True, we will call bypass_dev_mode after reboot.
    554         if is_dev or is_devsw_boot:
    555             self.bypass_dev_mode()
    556         if wait_for_dut_up:
    557             self.wait_for_client()
    558         logging.info("-[ModeSwitcher]-[ end mode_aware_reboot(%r, %s, ..) ]-",
    559                      reboot_type, reboot_method.__name__)
    560 
    561 
    562     def _enable_rec_mode_and_reboot(self, usb_state=None):
    563         """Switch to rec mode and reboot.
    564 
    565         This method emulates the behavior of the old physical recovery switch,
    566         i.e. switch ON + reboot + switch OFF, and the new keyboard controlled
    567         recovery mode, i.e. just press Power + Esc + Refresh.
    568 
    569         @param usb_state: A string, one of 'dut', 'host', or 'off'.
    570         """
    571         psc = self.servo.get_power_state_controller()
    572         psc.power_off()
    573         if usb_state:
    574             self.servo.switch_usbkey(usb_state)
    575         psc.power_on(psc.REC_ON)
    576 
    577 
    578     def _disable_rec_mode_and_reboot(self, usb_state=None):
    579         """Disable the rec mode and reboot.
    580 
    581         It is achieved by calling power state controller to do a normal
    582         power on.
    583         """
    584         psc = self.servo.get_power_state_controller()
    585         psc.power_off()
    586         time.sleep(self.faft_config.ec_boot_to_pwr_button)
    587         psc.power_on(psc.REC_OFF)
    588 
    589 
    590     def _enable_dev_mode_and_reboot(self):
    591         """Switch to developer mode and reboot."""
    592         raise NotImplementedError
    593 
    594 
    595     def _enable_normal_mode_and_reboot(self):
    596         """Switch to normal mode and reboot."""
    597         raise NotImplementedError
    598 
    599 
    600     # Redirects the following methods to FwBypasser
    601     def bypass_dev_mode(self):
    602         """Bypass the dev mode firmware logic to boot internal image."""
    603         logging.info("-[bypass_dev_mode]-")
    604         self.bypasser.bypass_dev_mode()
    605 
    606 
    607     def bypass_dev_boot_usb(self):
    608         """Bypass the dev mode firmware logic to boot USB."""
    609         logging.info("-[bypass_dev_boot_usb]-")
    610         self.bypasser.bypass_dev_boot_usb()
    611 
    612 
    613     def bypass_rec_mode(self):
    614         """Bypass the rec mode firmware logic to boot USB."""
    615         logging.info("-[bypass_rec_mode]-")
    616         self.bypasser.bypass_rec_mode()
    617 
    618 
    619     def trigger_dev_to_rec(self):
    620         """Trigger to the rec mode from the dev screen."""
    621         self.bypasser.trigger_dev_to_rec()
    622 
    623 
    624     def trigger_rec_to_dev(self):
    625         """Trigger to the dev mode from the rec screen."""
    626         self.bypasser.trigger_rec_to_dev()
    627 
    628 
    629     def trigger_dev_to_normal(self):
    630         """Trigger to the normal mode from the dev screen."""
    631         self.bypasser.trigger_dev_to_normal()
    632 
    633 
    634     def wait_for_client(self, timeout=180):
    635         """Wait for the client to come back online.
    636 
    637         New remote processes will be launched if their used flags are enabled.
    638 
    639         @param timeout: Time in seconds to wait for the client SSH daemon to
    640                         come up.
    641         @raise ConnectionError: Failed to connect DUT.
    642         """
    643         logging.info("-[FAFT]-[ start wait_for_client ]---")
    644         # Wait for the system to respond to ping before attempting ssh
    645         if not self.client_host.ping_wait_up(timeout):
    646             logging.warning("-[FAFT]-[ system did not respond to ping ]")
    647         if self.client_host.wait_up(timeout):
    648             # Check the FAFT client is avaiable.
    649             self.faft_client.system.is_available()
    650             # Stop update-engine as it may change firmware/kernel.
    651             self.faft_framework.faft_client.updater.stop_daemon()
    652         else:
    653             logging.error('wait_for_client() timed out.')
    654             raise ConnectionError('DUT is still down unexpectedly')
    655         logging.info("-[FAFT]-[ end wait_for_client ]-----")
    656 
    657 
    658     def wait_for_client_offline(self, timeout=60, orig_boot_id=None):
    659         """Wait for the client to come offline.
    660 
    661         @param timeout: Time in seconds to wait the client to come offline.
    662         @param orig_boot_id: A string containing the original boot id.
    663         @raise ConnectionError: Failed to wait DUT offline.
    664         """
    665         # When running against panther, we see that sometimes
    666         # ping_wait_down() does not work correctly. There needs to
    667         # be some investigation to the root cause.
    668         # If we sleep for 120s before running get_boot_id(), it
    669         # does succeed. But if we change this to ping_wait_down()
    670         # there are implications on the wait time when running
    671         # commands at the fw screens.
    672         if not self.client_host.ping_wait_down(timeout):
    673             if orig_boot_id and self.client_host.get_boot_id() != orig_boot_id:
    674                 logging.warn('Reboot done very quickly.')
    675                 return
    676             raise ConnectionError('DUT is still up unexpectedly')
    677 
    678 
    679 class _PhysicalButtonSwitcher(_BaseModeSwitcher):
    680     """Class that switches firmware mode via physical button."""
    681 
    682     def _enable_dev_mode_and_reboot(self):
    683         """Switch to developer mode and reboot."""
    684         self.servo.enable_development_mode()
    685         self.faft_client.system.run_shell_command(
    686                 'chromeos-firmwareupdate --mode todev && reboot')
    687 
    688 
    689     def _enable_normal_mode_and_reboot(self):
    690         """Switch to normal mode and reboot."""
    691         self.servo.disable_development_mode()
    692         self.faft_client.system.run_shell_command(
    693                 'chromeos-firmwareupdate --mode tonormal && reboot')
    694 
    695 
    696 class _KeyboardDevSwitcher(_BaseModeSwitcher):
    697     """Class that switches firmware mode via keyboard combo."""
    698 
    699     def _enable_dev_mode_and_reboot(self):
    700         """Switch to developer mode and reboot."""
    701         logging.info("Enabling keyboard controlled developer mode")
    702         # Rebooting EC with rec mode on. Should power on AP.
    703         # Plug out USB disk for preventing recovery boot without warning
    704         self._enable_rec_mode_and_reboot(usb_state='host')
    705         self.wait_for_client_offline()
    706         self.bypasser.trigger_rec_to_dev()
    707 
    708 
    709     def _enable_normal_mode_and_reboot(self):
    710         """Switch to normal mode and reboot."""
    711         logging.info("Disabling keyboard controlled developer mode")
    712         self._disable_rec_mode_and_reboot()
    713         self.wait_for_client_offline()
    714         self.bypasser.trigger_dev_to_normal()
    715 
    716 
    717 class _JetstreamSwitcher(_BaseModeSwitcher):
    718     """Class that switches firmware mode in Jetstream devices."""
    719 
    720     def _enable_dev_mode_and_reboot(self):
    721         """Switch to developer mode and reboot."""
    722         logging.info("Enabling Jetstream developer mode")
    723         self._enable_rec_mode_and_reboot(usb_state='host')
    724         self.wait_for_client_offline()
    725         self.bypasser.trigger_rec_to_dev()
    726 
    727 
    728     def _enable_normal_mode_and_reboot(self):
    729         """Switch to normal mode and reboot."""
    730         logging.info("Disabling Jetstream developer mode")
    731         self.servo.disable_development_mode()
    732         self._enable_rec_mode_and_reboot(usb_state='host')
    733         time.sleep(self.faft_config.firmware_screen)
    734         self._disable_rec_mode_and_reboot(usb_state='host')
    735 
    736 
    737 class _TabletDetachableSwitcher(_BaseModeSwitcher):
    738     """Class that switches fw mode in tablets/detachables with fw menu UI."""
    739 
    740     def _enable_dev_mode_and_reboot(self):
    741         """Switch to developer mode and reboot.
    742 
    743         On tablets/ detachables, recovery entered by pressing pwr, vol up
    744         & vol down buttons for 10s.
    745            Menu options seen in RECOVERY screen:
    746                  Enable Developer Mode
    747                  Show Debug Info
    748                  Power off*
    749                  Language
    750         """
    751         logging.info('Enabling tablets/detachable recovery mode')
    752         self._enable_rec_mode_and_reboot(usb_state='host')
    753         self.wait_for_client_offline()
    754         self.bypasser.trigger_rec_to_dev()
    755 
    756 
    757     def _enable_normal_mode_and_reboot(self):
    758         """Switch to normal mode and reboot.
    759 
    760            Menu options seen in DEVELOPER WARNING screen:
    761                  Developer Options
    762                  Show Debug Info
    763                  Enable Root Verification
    764                  Power Off*
    765                  Language
    766            Menu options seen in TO_NORM screen:
    767                  Confirm Enabling Verified Boot
    768                  Cancel
    769                  Power off*
    770                  Language
    771         Vol up button selects previous item, vol down button selects
    772         next item and pwr button selects current activated item.
    773         """
    774         self._disable_rec_mode_and_reboot()
    775         self.wait_for_client_offline()
    776         self.bypasser.trigger_dev_to_normal()
    777 
    778 
    779 class _RyuSwitcher(_BaseModeSwitcher):
    780     """Class that switches firmware mode via physical button."""
    781 
    782     FASTBOOT_OEM_DELAY = 10
    783     RECOVERY_TIMEOUT = 2400
    784     RECOVERY_SETUP = 60
    785     ANDROID_BOOTUP = 600
    786     FWTOOL_STARTUP_DELAY = 30
    787 
    788     def wait_for_client(self, timeout=180):
    789         """Wait for the client to come back online.
    790 
    791         New remote processes will be launched if their used flags are enabled.
    792 
    793         @param timeout: Time in seconds to wait for the client SSH daemon to
    794                         come up.
    795         @raise ConnectionError: Failed to connect DUT.
    796         """
    797         if not self.faft_client.system.wait_for_client(timeout):
    798             raise ConnectionError('DUT is still down unexpectedly')
    799 
    800         # there's a conflict between fwtool and crossystem trying to access
    801         # the nvram after the OS boots up.  Temporarily put a hard wait of
    802         # 30 seconds to try to wait for fwtool to finish up.
    803         time.sleep(self.FWTOOL_STARTUP_DELAY)
    804 
    805 
    806     def wait_for_client_offline(self, timeout=60, orig_boot_id=None):
    807         """Wait for the client to come offline.
    808 
    809         @param timeout: Time in seconds to wait the client to come offline.
    810         @param orig_boot_id: A string containing the original boot id.
    811         @raise ConnectionError: Failed to wait DUT offline.
    812         """
    813         # TODO: Add a way to check orig_boot_id
    814         if not self.faft_client.system.wait_for_client_offline(timeout):
    815             raise ConnectionError('DUT is still up unexpectedly')
    816 
    817     def print_recovery_warning(self):
    818         """Print recovery warning"""
    819         logging.info("***")
    820         logging.info("*** Entering recovery mode.  This may take awhile ***")
    821         logging.info("***")
    822         # wait a minute for DUT to get settled into wipe stage
    823         time.sleep(self.RECOVERY_SETUP)
    824 
    825     def is_fastboot_mode(self):
    826         """Return True if DUT in fastboot mode, False otherwise"""
    827         result = self.faft_client.host.run_shell_command_get_output(
    828             'fastboot devices')
    829         if not result:
    830             return False
    831         else:
    832             return True
    833 
    834     def wait_for_client_fastboot(self, timeout=30):
    835         """Wait for the client to come online in fastboot mode
    836 
    837         @param timeout: Time in seconds to wait the client
    838         @raise ConnectionError: Failed to wait DUT offline.
    839         """
    840         utils.wait_for_value(self.is_fastboot_mode, True, timeout_sec=timeout)
    841 
    842     def _run_cmd(self, args):
    843         """Wrapper for run_shell_command
    844 
    845         For Process creation
    846         """
    847         return self.faft_client.host.run_shell_command(args)
    848 
    849     def _enable_dev_mode_and_reboot(self):
    850         """Switch to developer mode and reboot."""
    851         logging.info("Entering RyuSwitcher: _enable_dev_mode_and_reboot")
    852         try:
    853             self.faft_client.system.run_shell_command('reboot bootloader')
    854             self.wait_for_client_fastboot()
    855 
    856             process = Process(
    857                 target=self._run_cmd,
    858                 args=('fastboot oem unlock',))
    859             process.start()
    860 
    861             # need a slight delay to give the ap time to get into valid state
    862             time.sleep(self.FASTBOOT_OEM_DELAY)
    863             self.servo.power_key(self.faft_config.hold_pwr_button_poweron)
    864             process.join()
    865 
    866             self.print_recovery_warning()
    867             self.wait_for_client_fastboot(self.RECOVERY_TIMEOUT)
    868             self.faft_client.host.run_shell_command('fastboot continue')
    869             self.wait_for_client(self.ANDROID_BOOTUP)
    870 
    871         # need to reset DUT into clean state
    872         except shell_wrapper.ShellError:
    873             raise error.TestError('Error executing shell command')
    874         except ConnectionError:
    875             raise error.TestError('Timed out waiting for DUT to exit recovery')
    876         except:
    877             raise error.TestError('Unexpected Exception: %s' % sys.exc_info()[0])
    878         logging.info("Exiting RyuSwitcher: _enable_dev_mode_and_reboot")
    879 
    880     def _enable_normal_mode_and_reboot(self):
    881         """Switch to normal mode and reboot."""
    882         try:
    883             self.faft_client.system.run_shell_command('reboot bootloader')
    884             self.wait_for_client_fastboot()
    885 
    886             process = Process(
    887                 target=self._run_cmd,
    888                 args=('fastboot oem lock',))
    889             process.start()
    890 
    891             # need a slight delay to give the ap time to get into valid state
    892             time.sleep(self.FASTBOOT_OEM_DELAY)
    893             self.servo.power_key(self.faft_config.hold_pwr_button_poweron)
    894             process.join()
    895 
    896             self.print_recovery_warning()
    897             self.wait_for_client_fastboot(self.RECOVERY_TIMEOUT)
    898             self.faft_client.host.run_shell_command('fastboot continue')
    899             self.wait_for_client(self.ANDROID_BOOTUP)
    900 
    901         # need to reset DUT into clean state
    902         except shell_wrapper.ShellError:
    903             raise error.TestError('Error executing shell command')
    904         except ConnectionError:
    905             raise error.TestError('Timed out waiting for DUT to exit recovery')
    906         except:
    907             raise error.TestError('Unexpected Exception: %s' % sys.exc_info()[0])
    908         logging.info("Exiting RyuSwitcher: _enable_normal_mode_and_reboot")
    909 
    910 def create_mode_switcher(faft_framework):
    911     """Creates a proper mode switcher.
    912 
    913     @param faft_framework: The main FAFT framework object.
    914     """
    915     switcher_type = faft_framework.faft_config.mode_switcher_type
    916     if switcher_type == 'physical_button_switcher':
    917         logging.info('Create a PhysicalButtonSwitcher')
    918         return _PhysicalButtonSwitcher(faft_framework)
    919     elif switcher_type == 'keyboard_dev_switcher':
    920         logging.info('Create a KeyboardDevSwitcher')
    921         return _KeyboardDevSwitcher(faft_framework)
    922     elif switcher_type == 'jetstream_switcher':
    923         logging.info('Create a JetstreamSwitcher')
    924         return _JetstreamSwitcher(faft_framework)
    925     elif switcher_type == 'ryu_switcher':
    926         logging.info('Create a RyuSwitcher')
    927         return _RyuSwitcher(faft_framework)
    928     elif switcher_type == 'tablet_detachable_switcher':
    929         logging.info('Create a TabletDetachableSwitcher')
    930         return _TabletDetachableSwitcher(faft_framework)
    931     else:
    932         raise NotImplementedError('Not supported mode_switcher_type: %s',
    933                                   switcher_type)
    934