xref: /aosp_15_r20/external/autotest/client/site_tests/power_IdleSuspend/power_IdleSuspend.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1# Lint as: python2, python3
2# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6import logging, os, tempfile, threading
7from autotest_lib.client.bin import test, utils
8from autotest_lib.client.common_lib import error
9from autotest_lib.client.common_lib.cros import chrome
10
11POWER_MANAGER_SETTINGS = {
12    'plugged_dim_ms': 1000,
13    'plugged_off_ms': 5000,
14    'plugged_suspend_ms': 10000,
15    'unplugged_dim_ms': 1000,
16    'unplugged_off_ms': 5000,
17    'unplugged_suspend_ms': 10000,
18    'disable_idle_suspend': 0,
19    'ignore_external_policy': 1,
20}
21
22SUSPEND_TIMEOUT_MS = 30000
23
24
25class power_IdleSuspend(test.test):
26    """
27    Verify power manager tries to suspend while idle.
28
29    This test does not actually allow the system to suspend. Instead,
30    it replaces /sys/power/state with a pipe and waits until "mem" is
31    written to it. Such a write would normally cause suspend.
32    """
33    version = 1
34    mounts = ()
35
36    def initialize(self):
37        super(power_IdleSuspend, self).initialize()
38        self.mounts = []
39
40
41    def setup_power_manager(self):
42        """Configures powerd for the test."""
43        # create directory for temporary settings
44        self.tempdir = tempfile.mkdtemp(prefix='IdleSuspend.')
45        logging.info('using temporary directory %s', self.tempdir)
46
47        # override power manager settings
48        for key, val in list(POWER_MANAGER_SETTINGS.items()):
49            logging.info('overriding %s to %s', key, val)
50            tmp_path = '%s/%s' % (self.tempdir, key)
51            mount_path = '/usr/share/power_manager/%s' % key
52            utils.write_one_line(tmp_path, str(val))
53            utils.run('mount --bind %s %s' % (tmp_path, mount_path))
54            self.mounts.append(mount_path)
55
56        # override /sys/power/state with fifo
57        fifo_path = '%s/sys_power_state' % self.tempdir
58        os.mkfifo(fifo_path)
59        utils.run('mount --bind %s /sys/power/state' % fifo_path)
60        self.mounts.append('/sys/power/state')
61
62
63    def wait_for_suspend(self):
64        """Thread callback to watch for powerd to announce idle transition."""
65        # block reading new power state from /sys/power/state
66        sys_power_state = open('/sys/power/state')
67        self.new_power_state = sys_power_state.read()
68        logging.info('new power state: %s', self.new_power_state)
69
70
71    def run_once(self):
72        with chrome.Chrome():
73            # stop power manager before reconfiguring
74            # TODO: Consider checking to see if powerd is running.
75            #       If it isn't, the test currently fails here.
76            logging.info('stopping powerd')
77            utils.run('stop powerd')
78
79            # override power manager settings
80            self.setup_power_manager()
81
82            # start thread to wait for suspend
83            self.new_power_state = None
84            thread = threading.Thread(target=self.wait_for_suspend)
85            thread.start()
86
87            # touch OOBE completed file so powerd won't ignore idle state.
88            utils.run('touch /home/chronos/.oobe_completed')
89
90            # restart powerd to pick up new settings
91            logging.info('restarting powerd')
92            utils.run('start powerd')
93
94            # wait for idle suspend
95            thread.join(SUSPEND_TIMEOUT_MS / 1000.)
96
97            if thread.is_alive():
98                # join timed out - powerd didn't write to /sys/power/state
99                raise error.TestFail('timed out waiting for suspend')
100
101            if self.new_power_state is None:
102                # probably an exception in the thread, check the log
103                raise error.TestError('reader thread crashed')
104
105            if self.new_power_state.strip() not in ['mem', 'freeze']:
106                err_str = 'bad power state written to /sys/power/state'
107                raise error.TestFail(err_str)
108
109
110    def cleanup(self):
111        # restore original power manager settings
112        for mount in self.mounts:
113            logging.info('restoring %s', mount)
114            utils.run('umount -l %s' % mount)
115
116        # restart powerd to pick up original settings
117        logging.info('restarting powerd')
118        utils.run('restart powerd')
119
120        super(power_IdleSuspend, self).cleanup()
121