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