• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
7import os
8import subprocess
9import tempfile
10import threading
11import time
12
13from autotest_lib.client.bin import test, utils
14from autotest_lib.client.common_lib import error
15from autotest_lib.client.common_lib.cros import chrome
16from autotest_lib.client.cros import rtc
17from autotest_lib.client.cros.audio import audio_helper
18from autotest_lib.client.cros.power import power_utils
19
20class power_AudioDetector(test.test):
21    """Verifies that audio playback prevents powerd from suspending."""
22    version = 1
23
24    def initialize(self):
25        self._pref_change = None
26
27
28    def run_once(self, run_time_sec=60):
29        if run_time_sec < 10:
30            raise error.TestFail('Must run for at least 10 seconds')
31
32        with chrome.Chrome():
33            # Audio loop time should be significantly shorter than
34            # |run_time_sec| time, so that the total playback time doesn't
35            # exceed it by much.
36            audio_loop_time_sec = min(10, run_time_sec / 10 + 0.5)
37
38            # Set a low audio volume to avoid annoying people during tests.
39            audio_helper.set_volume_levels(10, 100)
40
41            # Start a subprocess that uses dbus-monitor to listen for suspend
42            # announcements from powerd and writes the output to a log.
43            dbus_log_fd, dbus_log_name = tempfile.mkstemp()
44            os.unlink(dbus_log_name)
45            dbus_log = os.fdopen(dbus_log_fd)
46            dbus_proc = subprocess.Popen(
47                'dbus-monitor --monitor --system ' +
48                '"type=\'signal\',interface=\'org.chromium.PowerManager\',' +
49                'member=\'SuspendImminent\'"', shell=True, stdout=dbus_log)
50
51            # Start playing audio file.
52            self._enable_audio_playback = True
53            thread = threading.Thread(target=self._play_audio,
54                                      args=(audio_loop_time_sec,))
55            thread.start()
56
57            # Restart powerd with timeouts for quick idle events.
58            gap_ms = run_time_sec * 1000 / 4
59            dim_ms = min(10000, gap_ms)
60            off_ms = min(20000, gap_ms * 2)
61            suspend_ms = min(30000, gap_ms * 3)
62            prefs = { 'disable_idle_suspend'   : 0,
63                      'ignore_external_policy' : 1,
64                      'plugged_dim_ms'         : dim_ms,
65                      'plugged_off_ms'         : off_ms,
66                      'plugged_suspend_ms'     : suspend_ms,
67                      'unplugged_dim_ms'       : dim_ms,
68                      'unplugged_off_ms'       : off_ms,
69                      'unplugged_suspend_ms'   : suspend_ms }
70            self._pref_change = power_utils.PowerPrefChanger(prefs)
71
72            # Set an alarm to wake up the system in case the audio detector
73            # fails and the system suspends.
74            alarm_time = rtc.get_seconds() + run_time_sec
75            rtc.set_wake_alarm(alarm_time)
76
77            time.sleep(run_time_sec)
78
79            # Stop powerd to avoid suspending when the audio stops.
80            utils.system_output('stop powerd')
81
82            # Stop audio and wait for the audio thread to terminate.
83            self._enable_audio_playback = False
84            thread.join(timeout=(audio_loop_time_sec * 2))
85            if thread.is_alive():
86                logging.error('Audio thread did not terminate at end of test.')
87
88            # Check the D-Bus log to make sure that no suspend took place.
89            # dbus-monitor logs messages about its initial connection to the bus
90            # in addition to the signals that we asked it for, so look for the
91            # signal name in its output.
92            dbus_proc.kill()
93            dbus_log.seek(0)
94            if 'SuspendImminent' in dbus_log.read():
95                err_str = 'System suspended while audio was playing.'
96                raise error.TestFail(err_str)
97
98
99    def cleanup(self):
100        # Restore powerd prefs.
101        del self._pref_change
102
103
104    def _play_audio(self, loop_time):
105        """
106        Repeatedly plays audio until self._audio_playback_enabled == False.
107        """
108        # TODO(crosbug.com/33988): Allow for pauses in audio playback to
109        # simulate delays in loading the next song.
110        while self._enable_audio_playback:
111            audio_helper.play_sound(duration_seconds=loop_time)
112        logging.info('Done playing audio.')
113