• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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