• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2015 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 threading
7
8import dbus
9import dbus.mainloop.glib
10# AU tests use ToT client code, but ToT -3 client version.
11try:
12    from gi.repository import GObject
13except ImportError:
14    import gobject as GObject
15import os
16
17from autotest_lib.client.cros.input_playback import keyboard
18
19DARK_SUSPEND_DELAY_MILLISECONDS = 50000
20
21class DarkResumeListener(object):
22    """Server which listens for dark resume-related DBus signals to count how
23    many dark resumes we have seen since instantiation."""
24
25    SIGNAL_NAME = 'DarkSuspendImminent'
26
27
28    def __init__(self):
29        dbus.mainloop.glib.threads_init()
30        GObject.threads_init()
31
32        dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
33        self._bus = dbus.SystemBus()
34        self._count = 0
35        self._stop_resuspend = False
36
37        self._bus.add_signal_receiver(handler_function=self._saw_dark_resume,
38                                      signal_name=self.SIGNAL_NAME)
39
40        def loop_runner():
41            """Handles DBus events on the system bus using the mainloop."""
42            # If we just call run on this loop, the listener will hang and the test
43            # will never finish. Instead, we process events as they come in. This
44            # thread is set to daemon below, which means that the program will exit
45            # when the main thread exits.
46            loop = GObject.MainLoop()
47            context = loop.get_context()
48            while True:
49                context.iteration(True)
50        thread = threading.Thread(None, loop_runner)
51        thread.daemon = True
52        thread.start()
53        logging.debug('Dark resume listener started')
54
55        def register_dark_delay():
56            """Register a new client with powerd to delay dark suspend."""
57            # Powerd resuspends on dark resume once all clients are ready. Once
58            # resuspended the device might not be reachable. Thus this test on
59            # seeing a dark resume injects an input event by creating a virtual
60            # input device so that powerd bails out of resuspend process. But we
61            # need to delay the resuspend long enough for powerd to detect new
62            # input device and read the input event. This needs to run on a
63            # seperate thread as powerd will automatically unregister this
64            # client's suspend delay when it disconnects from D-Bus. Thus this
65            # thread is set to daemon below, which means that the program will
66            # exit when the main thread exits.
67            command = ('/usr/bin/suspend_delay_sample --delay_ms=%d '
68                       '--timeout_ms=%d --dark_suspend_delay' %
69                       (DARK_SUSPEND_DELAY_MILLISECONDS,
70                        DARK_SUSPEND_DELAY_MILLISECONDS))
71            logging.info("Running '%s'", command)
72            os.system(command)
73
74        suspend_delay_thread = threading.Thread(None, register_dark_delay)
75        suspend_delay_thread.daemon = True
76        suspend_delay_thread.start()
77        logging.debug('Dark suspend delay registered')
78
79
80    @property
81    def count(self):
82        """Number of DarkSuspendImminent events this listener has seen since its
83        creation."""
84        return self._count
85
86
87    def _saw_dark_resume(self, unused):
88        self._count += 1
89        if self._stop_resuspend:
90            # Inject input event to stop re-suspend.
91            with keyboard.Keyboard() as keys:
92                    keys.press_key('f4')
93
94
95
96    def stop_resuspend(self, should_stop):
97        """
98        Whether to stop suspend after seeing a dark resume.
99
100        @param should_stop: Whether to stop system from re-suspending.
101        """
102        self._stop_resuspend = should_stop
103
104