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 5import dbus 6 7from autotest_lib.client.bin import utils 8from autotest_lib.client.cros import constants 9 10def 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 26def 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 41def 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 59class 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 152class 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 220class 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 270class 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