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