Home | History | Annotate | Download | only in cros
      1 # Copyright 2014 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 
      7 from autotest_lib.client.common_lib import error
      8 from autotest_lib.client.cros import constants
      9 from autotest_lib.server import autotest
     10 
     11 POWER_DIR = '/var/lib/power_manager'
     12 TMP_POWER_DIR = '/tmp/power_manager'
     13 POWER_DEFAULTS = '/usr/share/power_manager/board_specific'
     14 
     15 RESUME_CTRL_RETRIES = 3
     16 RESUME_GRACE_PERIOD = 10
     17 XMLRPC_BRINGUP_TIMEOUT_SECONDS = 60
     18 
     19 
     20 class DarkResumeSuspend(object):
     21     """Context manager which exposes the dark resume-specific suspend
     22     functionality.
     23 
     24     This is required because using the RTC for a dark resume test will
     25     cause the system to wake up in dark resume and resuspend, which is
     26     not what we want. Instead, we suspend indefinitely, but make sure we
     27     don't leave the DUT asleep by always running code to wake it up via
     28     servo.
     29     """
     30 
     31 
     32     def __init__(self, proxy, host, suspend_for):
     33         """Set up for a dark-resume-ready suspend to be carried out using
     34         |proxy| and for the subsequent wakeup to be carried out using
     35         |host|.
     36 
     37         @param proxy: a dark resume xmlrpc server proxy object for the DUT
     38         @param host: a servo host connected to the DUT
     39         @param suspend_for : If not 0, sets a rtc alarm to wake the system after
     40             |suspend_for| secs.
     41         """
     42         self._client_proxy = proxy
     43         self._host = host
     44         self._suspend_for = suspend_for
     45 
     46 
     47     def __enter__(self):
     48         """Suspend the DUT."""
     49         logging.info('Suspending DUT (in background)...')
     50         self._client_proxy.suspend_bg_for_dark_resume(self._suspend_for)
     51 
     52 
     53     def __exit__(self, exception, value, traceback):
     54         """Wake up the DUT."""
     55         logging.info('Waking DUT from server.')
     56         _wake_dut(self._host)
     57 
     58 
     59 class DarkResumeUtils(object):
     60     """Class containing common functionality for tests which exercise dark
     61     resume pathways. We set up powerd to allow dark resume and also configure
     62     the suspended devices so that the backchannel can stay up. We can also
     63     check for the number of dark resumes that have happened in a particular
     64     suspend request.
     65     """
     66 
     67 
     68     def __init__(self, host, duration=0):
     69         """Set up powerd preferences so we will properly go into dark resume,
     70         and still be able to communicate with the DUT.
     71 
     72         @param host: the DUT to set up dark resume for
     73 
     74         """
     75         self._host = host
     76         logging.info('Setting up dark resume preferences')
     77 
     78         # Make temporary directory, which will be used to hold
     79         # temporary preferences. We want to avoid writing into
     80         # /var/lib so we don't have to save any state.
     81         logging.debug('Creating temporary powerd prefs at %s', TMP_POWER_DIR)
     82         host.run('mkdir -p %s' % TMP_POWER_DIR)
     83 
     84         logging.debug('Enabling dark resume')
     85         host.run('echo 0 > %s/disable_dark_resume' % TMP_POWER_DIR)
     86 
     87         # bind the tmp directory to the power preference directory
     88         host.run('mount --bind %s %s' % (TMP_POWER_DIR, POWER_DIR))
     89 
     90         logging.debug('Restarting powerd with new settings')
     91         host.run('stop powerd; start powerd')
     92 
     93         logging.debug('Starting XMLRPC session to watch for dark resumes')
     94         self._client_proxy = self._get_xmlrpc_proxy()
     95 
     96 
     97     def teardown(self):
     98         """Clean up changes made by DarkResumeUtils."""
     99 
    100         logging.info('Tearing down dark resume preferences')
    101 
    102         logging.debug('Cleaning up temporary powerd bind mounts')
    103         self._host.run('umount %s' % POWER_DIR)
    104 
    105         logging.debug('Restarting powerd to revert to old settings')
    106         self._host.run('stop powerd; start powerd')
    107 
    108 
    109     def suspend(self, suspend_for=0):
    110         """
    111         Returns a DarkResumeSuspend context manager that allows safe
    112         suspending of the DUT.
    113         @param suspend_for : If not 0, sets a rtc alarm to wake the system after
    114             |suspend_for| secs.
    115         """
    116         return DarkResumeSuspend(self._client_proxy, self._host, suspend_for)
    117 
    118 
    119     def stop_resuspend_on_dark_resume(self, stop_resuspend=True):
    120         """
    121         If |stop_resuspend| is True, stops re-suspend on seeing a dark resume.
    122         """
    123         self._client_proxy.set_stop_resuspend(stop_resuspend)
    124 
    125 
    126     def count_dark_resumes(self):
    127         """Return the number of dark resumes that have occurred since the beginning
    128         of the test. This will wake up the DUT, so make sure to put it back to
    129         sleep if you need to keep it suspended for some reason.
    130 
    131         This method will raise an error if the DUT does not wake up.
    132 
    133         @return the number of dark resumes counted by this DarkResumeUtils
    134 
    135         """
    136         _wake_dut(self._host)
    137 
    138         return self._client_proxy.get_dark_resume_count()
    139 
    140 
    141 
    142     def host_has_lid(self):
    143         """Returns True if the DUT has a lid."""
    144         return self._client_proxy.has_lid()
    145 
    146 
    147     def _get_xmlrpc_proxy(self):
    148         """Get a dark resume XMLRPC proxy for the host this DarkResumeUtils is
    149         attached to.
    150 
    151         The returned object has no particular type.  Instead, when you call
    152         a method on the object, it marshalls the objects passed as arguments
    153         and uses them to make RPCs on the remote server.  Thus, you should
    154         read dark_resume_xmlrpc_server.py to find out what methods are supported.
    155 
    156         @return proxy object for remote XMLRPC server.
    157 
    158         """
    159         # Make sure the client library is on the device so that the proxy
    160         # code is there when we try to call it.
    161         client_at = autotest.Autotest(self._host)
    162         client_at.install()
    163         # Start up the XMLRPC proxy on the client
    164         proxy = self._host.rpc_server_tracker.xmlrpc_connect(
    165                 constants.DARK_RESUME_XMLRPC_SERVER_COMMAND,
    166                 constants.DARK_RESUME_XMLRPC_SERVER_PORT,
    167                 command_name=
    168                     constants.DARK_RESUME_XMLRPC_SERVER_CLEANUP_PATTERN,
    169                 ready_test_name=
    170                     constants.DARK_RESUME_XMLRPC_SERVER_READY_METHOD,
    171                 timeout_seconds=XMLRPC_BRINGUP_TIMEOUT_SECONDS)
    172         return proxy
    173 
    174 
    175 def _wake_dut(host):
    176     """
    177     Make sure |host| is up by pressing power button.
    178 
    179     @raises error.TestFail: If we cannot wake the |host| up. This means the
    180             DUT has to be woken up manually. Should not happen mostly.
    181     """
    182     woken = False
    183     for i in range(RESUME_CTRL_RETRIES):
    184         # Check before pressing the power button. Or you might suspend/shutdown
    185         # the system if already in S0.
    186         if host.wait_up(timeout=RESUME_GRACE_PERIOD):
    187             woken = True
    188             break
    189         logging.debug('Wake attempt #%d ', i+1)
    190         host.servo.power_short_press()
    191 
    192     if not woken:
    193         logging.warning('DUT did not wake -- trouble ahead')
    194         raise error.TestFail('DUT did not wake')
    195