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