def RecoverDevice(device, denylist, should_reboot=lambda device: True):
  if device_status.IsDenylisted(device.adb.GetDeviceSerial(), denylist):
    logger.debug('%s is denylisted, skipping recovery.', str(device))
    return

  if device.adb.GetState() == 'unauthorized' and TryAuth(device):
    logger.info('Successfully authed device %s!', str(device))
    return

  if should_reboot(device):
    should_restore_root = device.HasRoot()
    try:
      device.WaitUntilFullyBooted(retries=0)
    except (device_errors.CommandTimeoutError, device_errors.CommandFailedError,
            device_errors.DeviceUnreachableError):
      logger.exception(
          'Failure while waiting for %s. '
          'Attempting to recover.', str(device))
    try:
      try:
        device.Reboot(block=False, timeout=5, retries=0)
      except device_errors.CommandTimeoutError:
        logger.warning(
            'Timed out while attempting to reboot %s normally.'
            'Attempting alternative reboot.', str(device))
        # The device drops offline before we can grab the exit code, so
        # we don't check for status.
        try:
          device.adb.Root()
        finally:
          # We are already in a failure mode, attempt to reboot regardless of
          # what device.adb.Root() returns. If the sysrq reboot fails an
          # exception willbe thrown at that level.
          device.adb.Shell(
              'echo b > /proc/sysrq-trigger',
              expect_status=None,
              timeout=5,
              retries=0)
    except (device_errors.CommandFailedError,
            device_errors.DeviceUnreachableError):
      logger.exception('Failed to reboot %s.', str(device))
      if denylist:
        denylist.Extend([device.adb.GetDeviceSerial()], reason='reboot_failure')
    except device_errors.CommandTimeoutError:
      logger.exception('Timed out while rebooting %s.', str(device))
      if denylist:
        denylist.Extend([device.adb.GetDeviceSerial()], reason='reboot_timeout')

    try:
      device.WaitUntilFullyBooted(
          retries=0, timeout=device.REBOOT_DEFAULT_TIMEOUT)
      if should_restore_root:
        device.EnableRoot()
    except (device_errors.CommandFailedError,
            device_errors.DeviceUnreachableError):
      logger.exception('Failure while waiting for %s.', str(device))
      if denylist:
        denylist.Extend([device.adb.GetDeviceSerial()], reason='reboot_failure')
    except device_errors.CommandTimeoutError:
      logger.exception('Timed out while waiting for %s.', str(device))
      if denylist:
        denylist.Extend([device.adb.GetDeviceSerial()], reason='reboot_timeout')