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 5import logging 6 7from autotest_lib.client.common_lib import error 8from autotest_lib.client.cros import constants 9from autotest_lib.server import autotest 10 11POWER_DIR = '/var/lib/power_manager' 12TMP_POWER_DIR = '/tmp/power_manager' 13POWER_DEFAULTS = '/usr/share/power_manager/board_specific' 14 15RESUME_CTRL_RETRIES = 3 16RESUME_GRACE_PERIOD = 10 17XMLRPC_BRINGUP_TIMEOUT_SECONDS = 60 18 19 20class 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 59class 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 175def _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