Home | History | Annotate | Download | only in cros
      1 # Copyright (c) 2013 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 dbus
      6 
      7 from autotest_lib.client.bin import utils
      8 from autotest_lib.client.cros import constants
      9 
     10 def connect(bus_loop):
     11     """Create and return a DBus connection to session_manager.
     12 
     13     Connects to the session manager over the DBus system bus.  Returns
     14     appropriately configured DBus interface object.
     15 
     16     @param bus_loop: An externally-owned DBusGMainLoop.
     17 
     18     @return a dbus.Interface object connection to the session_manager.
     19     """
     20     bus = dbus.SystemBus(mainloop=bus_loop)
     21     proxy = bus.get_object('org.chromium.SessionManager',
     22                            '/org/chromium/SessionManager')
     23     return dbus.Interface(proxy, 'org.chromium.SessionManagerInterface')
     24 
     25 
     26 class SignalListener(object):
     27     """A class to listen for DBus signals from the session manager.
     28 
     29     The session_manager emits several DBus signals when different events
     30     of interest occur. This class provides a framework for derived classes
     31     to use to listen for certain signals.
     32     """
     33 
     34     def __init__(self, g_main_loop):
     35         """Constructor
     36 
     37         @param g_mail_loop: glib main loop object.
     38         """
     39         self._main_loop = g_main_loop
     40 
     41 
     42     def wait_for_signals(self, desc,
     43                          timeout=constants.DEFAULT_OWNERSHIP_TIMEOUT):
     44         """Block for |timeout| seconds waiting for the signals to come in.
     45 
     46         @param desc: string describing the high-level reason you're waiting
     47                      for the signals.
     48         @param timeout: maximum seconds to wait for the signals.
     49 
     50         @raises TimeoutError if the timeout is hit.
     51         """
     52         utils.poll_for_condition(
     53             condition=lambda: self.__received_signals(),
     54             desc=desc,
     55             timeout=timeout)
     56         self.reset_signal_state()
     57 
     58 
     59     def __received_signals(self):
     60         """Run main loop until all pending events are done, checks for signals.
     61 
     62         Runs self._main_loop until it says it has no more events pending,
     63         then returns the state of the internal variables tracking whether
     64         desired signals have been received.
     65 
     66         @return True if both signals have been handled, False otherwise.
     67         """
     68         self.__flush()
     69         return self.all_signals_received()
     70 
     71 
     72     def __flush(self):
     73         """Runs the main loop until pending events are done."""
     74         context = self._main_loop.get_context()
     75         while context.iteration(False):
     76             pass
     77 
     78 
     79     def reset(self):
     80         """Prepares the listener to receive a new signal.
     81 
     82         This resets the signal state and flushes any pending signals in order to
     83         avoid picking up stale signals still lingering in the process' input
     84         queues.
     85         """
     86         self.__flush()
     87         self.reset_signal_state()
     88 
     89 
     90     def reset_signal_state(self):
     91         """Resets internal signal tracking state."""
     92         raise NotImplementedError()
     93 
     94 
     95     def all_signals_received(self):
     96         """Resets internal signal tracking state."""
     97         raise NotImplementedError()
     98 
     99 
    100     def listen_to_signal(self, callback, signal):
    101         """Connect a callback to a given session_manager dbus signal.
    102 
    103         Sets up a signal receiver for signal, and calls the provided callback
    104         when it comes in.
    105 
    106         @param callback: a callable to call when signal is received.
    107         @param signal: the signal to listen for.
    108         """
    109         bus = dbus.SystemBus()
    110         bus.add_signal_receiver(
    111             handler_function=callback,
    112             signal_name=signal,
    113             dbus_interface='org.chromium.SessionManagerInterface',
    114             bus_name=None,
    115             path='/org/chromium/SessionManager')
    116 
    117 
    118 
    119 class OwnershipSignalListener(SignalListener):
    120     """A class to listen for ownership-related DBus signals.
    121 
    122     The session_manager emits a couple of DBus signals when certain events
    123     related to device ownership occur.  This class provides a way to
    124     listen for them and check on their status.
    125     """
    126 
    127     def __init__(self, g_main_loop):
    128         """Constructor
    129 
    130         @param g_mail_loop: glib main loop object.
    131         """
    132         super(OwnershipSignalListener, self).__init__(g_main_loop)
    133         self._listen_for_new_key = False
    134         self._got_new_key = False
    135         self._listen_for_new_policy = False
    136         self._got_new_policy = False
    137 
    138 
    139     def listen_for_new_key_and_policy(self):
    140         """Set to listen for signals indicating new owner key and device policy.
    141         """
    142         self._listen_for_new_key = self._listen_for_new_policy = True
    143         self.listen_to_signal(self.__handle_new_key, 'SetOwnerKeyComplete')
    144         self.listen_to_signal(self.__handle_new_policy,
    145                               'PropertyChangeComplete')
    146         self.reset()
    147 
    148 
    149     def listen_for_new_policy(self):
    150         """Set to listen for signal indicating new device policy.
    151         """
    152         self._listen_for_new_key = False
    153         self._listen_for_new_policy = True
    154         self.listen_to_signal(self.__handle_new_policy,
    155                               'PropertyChangeComplete')
    156         self.reset()
    157 
    158 
    159     def reset_signal_state(self):
    160         """Resets internal signal tracking state."""
    161         self._got_new_key = not self._listen_for_new_key
    162         self._got_new_policy = not self._listen_for_new_policy
    163 
    164 
    165     def all_signals_received(self):
    166         """Returns true when expected signals are all receieved."""
    167         return self._got_new_key and self._got_new_policy
    168 
    169 
    170     def __handle_new_key(self, success):
    171         """Callback to be used when a new key signal is received.
    172 
    173         @param success: the string 'success' if the key was generated.
    174         """
    175         self._got_new_key = (success == 'success')
    176 
    177 
    178     def __handle_new_policy(self, success):
    179         """Callback to be used when a new policy signal is received.
    180 
    181         @param success: the string 'success' if the policy was stored.
    182         """
    183         self._got_new_policy = (success == 'success')
    184 
    185 
    186 
    187 class SessionSignalListener(SignalListener):
    188     """A class to listen for SessionStateChanged DBus signals.
    189 
    190     The session_manager emits a DBus signal whenever a user signs in, when
    191     the user session begins termination, and when the session is terminated.
    192     This class allows this signal to be polled for
    193     """
    194 
    195     def __init__(self, g_main_loop):
    196         """Constructor
    197 
    198         @param g_mail_loop: glib main loop object.
    199         """
    200         super(SessionSignalListener, self).__init__(g_main_loop)
    201         self._new_state = None
    202         self._expected_state = None
    203 
    204 
    205     def listen_for_session_state_change(self, expected):
    206         """Set to listen for state changed signal with payload == |expected|.
    207 
    208         @param expected: string representing the state transition we expect.
    209                          One of 'started', 'stopping', or 'stopped'.
    210         """
    211         if expected not in {'started', 'stopping', 'stopped'}:
    212             raise ValueError("expected must be one of 'started', 'stopping'," +
    213                              " or 'stopped'.")
    214         self.listen_to_signal(self.__handle_signal, 'SessionStateChanged')
    215         self._expected_state = expected
    216 
    217 
    218     def reset_signal_state(self):
    219         """Resets internal signal tracking state."""
    220         self._new_state = None
    221 
    222 
    223     def all_signals_received(self):
    224         """Returns true when expected signals are all receieved."""
    225         return self._new_state == self._expected_state
    226 
    227 
    228     def __handle_signal(self, state):
    229         """Callback to be used when a new state-change signal is received.
    230 
    231         @param state: the state transition being signaled.
    232         """
    233         self._new_state = state
    234 
    235 
    236 
    237 class ScreenIsLockedSignalListener(SignalListener):
    238     """A class to listen for ScreenIsLocked DBus signal.
    239 
    240     The session_manager emits a DBus signal when screen lock operation is
    241     completed.
    242     """
    243 
    244     def __init__(self, g_main_loop):
    245         """Constructor
    246 
    247         @param g_main_loop: glib main loop object.
    248         """
    249         super(ScreenIsLockedSignalListener, self).__init__(g_main_loop)
    250         self._screen_is_locked_received = False
    251         self.listen_to_signal(self.__handle_signal, 'ScreenIsLocked')
    252 
    253 
    254     def reset_signal_state(self):
    255         """Resets internal signal tracking state."""
    256         self._screen_is_locked_received = False
    257 
    258 
    259     def all_signals_received(self):
    260         """Returns true when expected signals are all receieved."""
    261         return self._screen_is_locked_received
    262 
    263 
    264     def __handle_signal(self):
    265         """Callback to be used when ScreenIsLocked signal is received.
    266         """
    267         self._screen_is_locked_received = True
    268