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