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 5import logging 6import time 7import sys 8 9from multiprocessing import Process 10from autotest_lib.client.bin import utils 11from autotest_lib.client.common_lib import error 12from autotest_lib.client.cros.faft.utils import shell_wrapper 13 14class ConnectionError(Exception): 15 """Raised on an error of connecting DUT.""" 16 pass 17 18 19class _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 58class _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 122class _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 168class _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 time.sleep(self.faft_config.firmware_screen) 268 self.servo.set_nocheck('volume_up_hold', 100) 269 time.sleep(self.faft_config.confirm_screen) 270 self.servo.set_nocheck('volume_up_hold', 100) 271 time.sleep(self.faft_config.confirm_screen) 272 self.set_button('volume_up_hold', 100, ('Selecting power ' 273 'as enter key to select Developer Options')) 274 self.servo.power_short_press() 275 276 277 def trigger_rec_to_dev(self): 278 """Trigger to the dev mode from the rec screen using vol up button. 279 280 On tablets/ detachables, recovery entered by pressing pwr, vol up 281 & vol down buttons for 10s. 282 Menu options seen in RECOVERY screen: 283 Enable Developer Mode 284 Show Debug Info 285 Power off* 286 Language 287 Menu options seen in TO_DEV screen: 288 Confirm enabling developer mode 289 Cancel 290 Power off* 291 Language 292 Vol up button selects previous item, vol down button selects 293 next item and pwr button selects current activated item. 294 """ 295 time.sleep(self.faft_config.firmware_screen) 296 self.servo.set_nocheck('volume_up_hold', 100) 297 time.sleep(self.faft_config.confirm_screen) 298 self.set_button('volume_up_hold', 100, ('Selecting power as ' 299 'enter key to select Enable Developer Mode')) 300 self.servo.power_short_press() 301 logging.info('Transitioning from REC to TO_DEV screen.') 302 time.sleep(self.faft_config.confirm_screen) 303 self.servo.set_nocheck('volume_up_hold', 100) 304 time.sleep(self.faft_config.confirm_screen) 305 self.set_button('volume_up_hold', 100, ('Selecting power as ' 306 'enter key to select Confirm enabling ' 307 'developer mode')) 308 self.servo.power_short_press() 309 time.sleep(self.faft_config.firmware_screen) 310 311 312 def trigger_dev_to_normal(self): 313 """Trigger to the normal mode from the dev screen. 314 315 Menu options seen in DEVELOPER WARNING screen: 316 Developer Options 317 Show Debug Info 318 Enable Root Verification 319 Power Off* 320 Language 321 Menu options seen in TO_NORM screen: 322 Confirm Enabling Verified Boot 323 Cancel 324 Power off* 325 Language 326 Vol up button selects previous item, vol down button selects 327 next item and pwr button selects current activated item. 328 """ 329 time.sleep(self.faft_config.firmware_screen) 330 self.set_button('volume_up_hold', 100, ('Selecting ' 331 'Enable Root Verification using pwr ' 332 'button to enter TO_NORM screen')) 333 self.servo.power_short_press() 334 time.sleep(self.faft_config.firmware_screen) 335 self.servo.set_nocheck('volume_up_hold', 100) 336 time.sleep(self.faft_config.confirm_screen) 337 self.set_button('volume_up_hold', 100, ('Selecting Confirm ' 338 'Enabling Verified Boot using pwr ' 339 'button in TO_NORM screen')) 340 self.servo.power_short_press() 341 342 343def _create_fw_bypasser(faft_framework): 344 """Creates a proper firmware bypasser. 345 346 @param faft_framework: The main FAFT framework object. 347 """ 348 bypasser_type = faft_framework.faft_config.fw_bypasser_type 349 if bypasser_type == 'ctrl_d_bypasser': 350 logging.info('Create a CtrlDBypasser') 351 return _CtrlDBypasser(faft_framework) 352 elif bypasser_type == 'jetstream_bypasser': 353 logging.info('Create a JetstreamBypasser') 354 return _JetstreamBypasser(faft_framework) 355 elif bypasser_type == 'ryu_bypasser': 356 # FIXME Create an RyuBypasser 357 logging.info('Create a CtrlDBypasser') 358 return _CtrlDBypasser(faft_framework) 359 elif bypasser_type == 'tablet_detachable_bypasser': 360 logging.info('Create a TabletDetachableBypasser') 361 return _TabletDetachableBypasser(faft_framework) 362 else: 363 raise NotImplementedError('Not supported fw_bypasser_type: %s', 364 bypasser_type) 365 366 367class _BaseModeSwitcher(object): 368 """Base class that controls firmware mode switching.""" 369 370 def __init__(self, faft_framework): 371 self.faft_framework = faft_framework 372 self.client_host = faft_framework._client 373 self.faft_client = faft_framework.faft_client 374 self.servo = faft_framework.servo 375 self.faft_config = faft_framework.faft_config 376 self.checkers = faft_framework.checkers 377 self.bypasser = _create_fw_bypasser(faft_framework) 378 self._backup_mode = None 379 380 381 def setup_mode(self, mode): 382 """Setup for the requested mode. 383 384 It makes sure the system in the requested mode. If not, it tries to 385 do so. 386 387 @param mode: A string of mode, one of 'normal', 'dev', or 'rec'. 388 """ 389 if not self.checkers.mode_checker(mode): 390 logging.info('System not in expected %s mode. Reboot into it.', 391 mode) 392 if self._backup_mode is None: 393 # Only resume to normal/dev mode after test, not recovery. 394 self._backup_mode = 'dev' if mode == 'normal' else 'normal' 395 self.reboot_to_mode(mode) 396 397 398 def restore_mode(self): 399 """Restores original dev mode status if it has changed.""" 400 if self._backup_mode is not None: 401 self.reboot_to_mode(self._backup_mode) 402 403 404 def reboot_to_mode(self, to_mode, from_mode=None, sync_before_boot=True, 405 wait_for_dut_up=True): 406 """Reboot and execute the mode switching sequence. 407 408 @param to_mode: The target mode, one of 'normal', 'dev', or 'rec'. 409 @param from_mode: The original mode, optional, one of 'normal, 'dev', 410 or 'rec'. 411 @param sync_before_boot: True to sync to disk before booting. 412 @param wait_for_dut_up: True to wait DUT online again. False to do the 413 reboot and mode switching sequence only and may 414 need more operations to pass the firmware 415 screen. 416 """ 417 logging.info('-[ModeSwitcher]-[ start reboot_to_mode(%r, %r, %r) ]-', 418 to_mode, from_mode, wait_for_dut_up) 419 if sync_before_boot: 420 self.faft_framework.blocking_sync() 421 if to_mode == 'rec': 422 self._enable_rec_mode_and_reboot(usb_state='dut') 423 if wait_for_dut_up: 424 self.wait_for_client() 425 426 elif to_mode == 'dev': 427 self._enable_dev_mode_and_reboot() 428 if wait_for_dut_up: 429 self.bypass_dev_mode() 430 self.wait_for_client() 431 432 elif to_mode == 'normal': 433 self._enable_normal_mode_and_reboot() 434 if wait_for_dut_up: 435 self.wait_for_client() 436 437 else: 438 raise NotImplementedError( 439 'Not supported mode switching from %s to %s' % 440 (str(from_mode), to_mode)) 441 logging.info('-[ModeSwitcher]-[ end reboot_to_mode(%r, %r, %r) ]-', 442 to_mode, from_mode, wait_for_dut_up) 443 444 def simple_reboot(self, reboot_type='warm', sync_before_boot=True): 445 """Simple reboot method 446 447 Just reboot the DUT using either cold or warm reset. Does not wait for 448 DUT to come back online. Will wait for test to handle this. 449 450 @param reboot_type: A string of reboot type, 'warm' or 'cold'. 451 If reboot_type != warm/cold, raise exception. 452 @param sync_before_boot: True to sync to disk before booting. 453 If sync_before_boot=False, DUT offline before 454 calling mode_aware_reboot. 455 """ 456 if reboot_type == 'warm': 457 reboot_method = self.servo.get_power_state_controller().warm_reset 458 elif reboot_type == 'cold': 459 reboot_method = self.servo.get_power_state_controller().reset 460 else: 461 raise NotImplementedError('Not supported reboot_type: %s', 462 reboot_type) 463 if sync_before_boot: 464 boot_id = self.faft_framework.get_bootid() 465 self.faft_framework.blocking_sync() 466 logging.info("-[ModeSwitcher]-[ start simple_reboot(%r) ]-", 467 reboot_type) 468 reboot_method() 469 if sync_before_boot: 470 self.wait_for_client_offline(orig_boot_id=boot_id) 471 logging.info("-[ModeSwitcher]-[ end simple_reboot(%r) ]-", 472 reboot_type) 473 474 def mode_aware_reboot(self, reboot_type=None, reboot_method=None, 475 sync_before_boot=True, wait_for_dut_up=True): 476 """Uses a mode-aware way to reboot DUT. 477 478 For example, if DUT is in dev mode, it requires pressing Ctrl-D to 479 bypass the developer screen. 480 481 @param reboot_type: A string of reboot type, one of 'warm', 'cold', or 482 'custom'. Default is a warm reboot. 483 @param reboot_method: A custom method to do the reboot. Only use it if 484 reboot_type='custom'. 485 @param sync_before_boot: True to sync to disk before booting. 486 If sync_before_boot=False, DUT offline before 487 calling mode_aware_reboot. 488 @param wait_for_dut_up: True to wait DUT online again. False to do the 489 reboot only. 490 """ 491 if reboot_type is None or reboot_type == 'warm': 492 reboot_method = self.servo.get_power_state_controller().warm_reset 493 elif reboot_type == 'cold': 494 reboot_method = self.servo.get_power_state_controller().reset 495 elif reboot_type != 'custom': 496 raise NotImplementedError('Not supported reboot_type: %s', 497 reboot_type) 498 499 logging.info("-[ModeSwitcher]-[ start mode_aware_reboot(%r, %s, ..) ]-", 500 reboot_type, reboot_method.__name__) 501 is_dev = False 502 if sync_before_boot: 503 is_dev = self.checkers.mode_checker('dev') 504 boot_id = self.faft_framework.get_bootid() 505 self.faft_framework.blocking_sync() 506 logging.info("-[mode_aware_reboot]-[ is_dev=%s ]-", is_dev); 507 reboot_method() 508 if sync_before_boot: 509 self.wait_for_client_offline(orig_boot_id=boot_id) 510 # Encapsulating the behavior of skipping dev firmware screen, 511 # hitting ctrl-D 512 # Note that if booting from recovery mode, will not 513 # call bypass_dev_mode because can't determine prior to 514 # reboot if we're going to boot up in dev or normal mode. 515 if is_dev: 516 self.bypass_dev_mode() 517 if wait_for_dut_up: 518 self.wait_for_client() 519 logging.info("-[ModeSwitcher]-[ end mode_aware_reboot(%r, %s, ..) ]-", 520 reboot_type, reboot_method.__name__) 521 522 523 def _enable_rec_mode_and_reboot(self, usb_state=None): 524 """Switch to rec mode and reboot. 525 526 This method emulates the behavior of the old physical recovery switch, 527 i.e. switch ON + reboot + switch OFF, and the new keyboard controlled 528 recovery mode, i.e. just press Power + Esc + Refresh. 529 530 @param usb_state: A string, one of 'dut', 'host', or 'off'. 531 """ 532 psc = self.servo.get_power_state_controller() 533 psc.power_off() 534 if usb_state: 535 self.servo.switch_usbkey(usb_state) 536 psc.power_on(psc.REC_ON) 537 538 539 def _disable_rec_mode_and_reboot(self, usb_state=None): 540 """Disable the rec mode and reboot. 541 542 It is achieved by calling power state controller to do a normal 543 power on. 544 """ 545 psc = self.servo.get_power_state_controller() 546 psc.power_off() 547 time.sleep(self.faft_config.ec_boot_to_pwr_button) 548 psc.power_on(psc.REC_OFF) 549 550 551 def _enable_dev_mode_and_reboot(self): 552 """Switch to developer mode and reboot.""" 553 raise NotImplementedError 554 555 556 def _enable_normal_mode_and_reboot(self): 557 """Switch to normal mode and reboot.""" 558 raise NotImplementedError 559 560 561 # Redirects the following methods to FwBypasser 562 def bypass_dev_mode(self): 563 """Bypass the dev mode firmware logic to boot internal image.""" 564 logging.info("-[bypass_dev_mode]-") 565 self.bypasser.bypass_dev_mode() 566 567 568 def bypass_dev_boot_usb(self): 569 """Bypass the dev mode firmware logic to boot USB.""" 570 logging.info("-[bypass_dev_boot_usb]-") 571 self.bypasser.bypass_dev_boot_usb() 572 573 574 def bypass_rec_mode(self): 575 """Bypass the rec mode firmware logic to boot USB.""" 576 logging.info("-[bypass_rec_mode]-") 577 self.bypasser.bypass_rec_mode() 578 579 580 def trigger_dev_to_rec(self): 581 """Trigger to the rec mode from the dev screen.""" 582 self.bypasser.trigger_dev_to_rec() 583 584 585 def trigger_rec_to_dev(self): 586 """Trigger to the dev mode from the rec screen.""" 587 self.bypasser.trigger_rec_to_dev() 588 589 590 def trigger_dev_to_normal(self): 591 """Trigger to the normal mode from the dev screen.""" 592 self.bypasser.trigger_dev_to_normal() 593 594 595 def wait_for_client(self, timeout=180): 596 """Wait for the client to come back online. 597 598 New remote processes will be launched if their used flags are enabled. 599 600 @param timeout: Time in seconds to wait for the client SSH daemon to 601 come up. 602 @raise ConnectionError: Failed to connect DUT. 603 """ 604 logging.info("-[FAFT]-[ start wait_for_client ]---") 605 # Wait for the system to respond to ping before attempting ssh 606 if not self.client_host.ping_wait_up(timeout): 607 logging.warning("-[FAFT]-[ system did not respond to ping ]") 608 if self.client_host.wait_up(timeout): 609 # Check the FAFT client is avaiable. 610 self.faft_client.system.is_available() 611 # Stop update-engine as it may change firmware/kernel. 612 self.faft_framework._stop_service('update-engine') 613 else: 614 logging.error('wait_for_client() timed out.') 615 raise ConnectionError() 616 logging.info("-[FAFT]-[ end wait_for_client ]-----") 617 618 619 def wait_for_client_offline(self, timeout=60, orig_boot_id=None): 620 """Wait for the client to come offline. 621 622 @param timeout: Time in seconds to wait the client to come offline. 623 @param orig_boot_id: A string containing the original boot id. 624 @raise ConnectionError: Failed to wait DUT offline. 625 """ 626 # When running against panther, we see that sometimes 627 # ping_wait_down() does not work correctly. There needs to 628 # be some investigation to the root cause. 629 # If we sleep for 120s before running get_boot_id(), it 630 # does succeed. But if we change this to ping_wait_down() 631 # there are implications on the wait time when running 632 # commands at the fw screens. 633 if not self.client_host.ping_wait_down(timeout): 634 if orig_boot_id and self.client_host.get_boot_id() != orig_boot_id: 635 logging.warn('Reboot done very quickly.') 636 return 637 raise ConnectionError() 638 639 640class _PhysicalButtonSwitcher(_BaseModeSwitcher): 641 """Class that switches firmware mode via physical button.""" 642 643 def _enable_dev_mode_and_reboot(self): 644 """Switch to developer mode and reboot.""" 645 self.servo.enable_development_mode() 646 self.faft_client.system.run_shell_command( 647 'chromeos-firmwareupdate --mode todev && reboot') 648 649 650 def _enable_normal_mode_and_reboot(self): 651 """Switch to normal mode and reboot.""" 652 self.servo.disable_development_mode() 653 self.faft_client.system.run_shell_command( 654 'chromeos-firmwareupdate --mode tonormal && reboot') 655 656 657class _KeyboardDevSwitcher(_BaseModeSwitcher): 658 """Class that switches firmware mode via keyboard combo.""" 659 660 def _enable_dev_mode_and_reboot(self): 661 """Switch to developer mode and reboot.""" 662 logging.info("Enabling keyboard controlled developer mode") 663 # Rebooting EC with rec mode on. Should power on AP. 664 # Plug out USB disk for preventing recovery boot without warning 665 self._enable_rec_mode_and_reboot(usb_state='host') 666 self.wait_for_client_offline() 667 self.bypasser.trigger_rec_to_dev() 668 669 670 def _enable_normal_mode_and_reboot(self): 671 """Switch to normal mode and reboot.""" 672 logging.info("Disabling keyboard controlled developer mode") 673 self._disable_rec_mode_and_reboot() 674 self.wait_for_client_offline() 675 self.bypasser.trigger_dev_to_normal() 676 677 678class _JetstreamSwitcher(_BaseModeSwitcher): 679 """Class that switches firmware mode in Jetstream devices.""" 680 681 def _enable_dev_mode_and_reboot(self): 682 """Switch to developer mode and reboot.""" 683 logging.info("Enabling Jetstream developer mode") 684 self._enable_rec_mode_and_reboot(usb_state='host') 685 self.wait_for_client_offline() 686 self.bypasser.trigger_rec_to_dev() 687 688 689 def _enable_normal_mode_and_reboot(self): 690 """Switch to normal mode and reboot.""" 691 logging.info("Disabling Jetstream developer mode") 692 self.servo.disable_development_mode() 693 self._enable_rec_mode_and_reboot(usb_state='host') 694 time.sleep(self.faft_config.firmware_screen) 695 self._disable_rec_mode_and_reboot(usb_state='host') 696 697 698class _TabletDetachableSwitcher(_BaseModeSwitcher): 699 """Class that switches fw mode in tablets/detachables with fw menu UI.""" 700 701 def _enable_dev_mode_and_reboot(self): 702 """Switch to developer mode and reboot. 703 704 On tablets/ detachables, recovery entered by pressing pwr, vol up 705 & vol down buttons for 10s. 706 Menu options seen in RECOVERY screen: 707 Enable Developer Mode 708 Show Debug Info 709 Power off* 710 Language 711 """ 712 logging.info('Enabling tablets/detachable recovery mode') 713 self._enable_rec_mode_and_reboot(usb_state='host') 714 self.wait_for_client_offline() 715 self.bypasser.trigger_rec_to_dev() 716 717 718 def _enable_normal_mode_and_reboot(self): 719 """Switch to normal mode and reboot. 720 721 Menu options seen in DEVELOPER WARNING screen: 722 Developer Options 723 Show Debug Info 724 Enable Root Verification 725 Power Off* 726 Language 727 Menu options seen in TO_NORM screen: 728 Confirm Enabling Verified Boot 729 Cancel 730 Power off* 731 Language 732 Vol up button selects previous item, vol down button selects 733 next item and pwr button selects current activated item. 734 """ 735 self._disable_rec_mode_and_reboot() 736 self.wait_for_client_offline() 737 self.bypasser.trigger_dev_to_normal() 738 739 740class _RyuSwitcher(_BaseModeSwitcher): 741 """Class that switches firmware mode via physical button.""" 742 743 FASTBOOT_OEM_DELAY = 10 744 RECOVERY_TIMEOUT = 2400 745 RECOVERY_SETUP = 60 746 ANDROID_BOOTUP = 600 747 FWTOOL_STARTUP_DELAY = 30 748 749 def wait_for_client(self, timeout=180): 750 """Wait for the client to come back online. 751 752 New remote processes will be launched if their used flags are enabled. 753 754 @param timeout: Time in seconds to wait for the client SSH daemon to 755 come up. 756 @raise ConnectionError: Failed to connect DUT. 757 """ 758 if not self.faft_client.system.wait_for_client(timeout): 759 raise ConnectionError() 760 761 # there's a conflict between fwtool and crossystem trying to access 762 # the nvram after the OS boots up. Temporarily put a hard wait of 763 # 30 seconds to try to wait for fwtool to finish up. 764 time.sleep(self.FWTOOL_STARTUP_DELAY) 765 766 767 def wait_for_client_offline(self, timeout=60, orig_boot_id=None): 768 """Wait for the client to come offline. 769 770 @param timeout: Time in seconds to wait the client to come offline. 771 @param orig_boot_id: A string containing the original boot id. 772 @raise ConnectionError: Failed to wait DUT offline. 773 """ 774 # TODO: Add a way to check orig_boot_id 775 if not self.faft_client.system.wait_for_client_offline(timeout): 776 raise ConnectionError() 777 778 def print_recovery_warning(self): 779 """Print recovery warning""" 780 logging.info("***") 781 logging.info("*** Entering recovery mode. This may take awhile ***") 782 logging.info("***") 783 # wait a minute for DUT to get settled into wipe stage 784 time.sleep(self.RECOVERY_SETUP) 785 786 def is_fastboot_mode(self): 787 """Return True if DUT in fastboot mode, False otherwise""" 788 result = self.faft_client.host.run_shell_command_get_output( 789 'fastboot devices') 790 if not result: 791 return False 792 else: 793 return True 794 795 def wait_for_client_fastboot(self, timeout=30): 796 """Wait for the client to come online in fastboot mode 797 798 @param timeout: Time in seconds to wait the client 799 @raise ConnectionError: Failed to wait DUT offline. 800 """ 801 utils.wait_for_value(self.is_fastboot_mode, True, timeout_sec=timeout) 802 803 def _run_cmd(self, args): 804 """Wrapper for run_shell_command 805 806 For Process creation 807 """ 808 return self.faft_client.host.run_shell_command(args) 809 810 def _enable_dev_mode_and_reboot(self): 811 """Switch to developer mode and reboot.""" 812 logging.info("Entering RyuSwitcher: _enable_dev_mode_and_reboot") 813 try: 814 self.faft_client.system.run_shell_command('reboot bootloader') 815 self.wait_for_client_fastboot() 816 817 process = Process( 818 target=self._run_cmd, 819 args=('fastboot oem unlock',)) 820 process.start() 821 822 # need a slight delay to give the ap time to get into valid state 823 time.sleep(self.FASTBOOT_OEM_DELAY) 824 self.servo.power_key(self.faft_config.hold_pwr_button_poweron) 825 process.join() 826 827 self.print_recovery_warning() 828 self.wait_for_client_fastboot(self.RECOVERY_TIMEOUT) 829 self.faft_client.host.run_shell_command('fastboot continue') 830 self.wait_for_client(self.ANDROID_BOOTUP) 831 832 # need to reset DUT into clean state 833 except shell_wrapper.ShellError: 834 raise error.TestError('Error executing shell command') 835 except ConnectionError: 836 raise error.TestError('Timed out waiting for DUT to exit recovery') 837 except: 838 raise error.TestError('Unexpected Exception: %s' % sys.exc_info()[0]) 839 logging.info("Exiting RyuSwitcher: _enable_dev_mode_and_reboot") 840 841 def _enable_normal_mode_and_reboot(self): 842 """Switch to normal mode and reboot.""" 843 try: 844 self.faft_client.system.run_shell_command('reboot bootloader') 845 self.wait_for_client_fastboot() 846 847 process = Process( 848 target=self._run_cmd, 849 args=('fastboot oem lock',)) 850 process.start() 851 852 # need a slight delay to give the ap time to get into valid state 853 time.sleep(self.FASTBOOT_OEM_DELAY) 854 self.servo.power_key(self.faft_config.hold_pwr_button_poweron) 855 process.join() 856 857 self.print_recovery_warning() 858 self.wait_for_client_fastboot(self.RECOVERY_TIMEOUT) 859 self.faft_client.host.run_shell_command('fastboot continue') 860 self.wait_for_client(self.ANDROID_BOOTUP) 861 862 # need to reset DUT into clean state 863 except shell_wrapper.ShellError: 864 raise error.TestError('Error executing shell command') 865 except ConnectionError: 866 raise error.TestError('Timed out waiting for DUT to exit recovery') 867 except: 868 raise error.TestError('Unexpected Exception: %s' % sys.exc_info()[0]) 869 logging.info("Exiting RyuSwitcher: _enable_normal_mode_and_reboot") 870 871def create_mode_switcher(faft_framework): 872 """Creates a proper mode switcher. 873 874 @param faft_framework: The main FAFT framework object. 875 """ 876 switcher_type = faft_framework.faft_config.mode_switcher_type 877 if switcher_type == 'physical_button_switcher': 878 logging.info('Create a PhysicalButtonSwitcher') 879 return _PhysicalButtonSwitcher(faft_framework) 880 elif switcher_type == 'keyboard_dev_switcher': 881 logging.info('Create a KeyboardDevSwitcher') 882 return _KeyboardDevSwitcher(faft_framework) 883 elif switcher_type == 'jetstream_switcher': 884 logging.info('Create a JetstreamSwitcher') 885 return _JetstreamSwitcher(faft_framework) 886 elif switcher_type == 'ryu_switcher': 887 logging.info('Create a RyuSwitcher') 888 return _RyuSwitcher(faft_framework) 889 elif switcher_type == 'tablet_detachable_switcher': 890 logging.info('Create a TabletDetachableSwitcher') 891 return _TabletDetachableSwitcher(faft_framework) 892 else: 893 raise NotImplementedError('Not supported mode_switcher_type: %s', 894 switcher_type) 895