• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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