# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import dbus from autotest_lib.client.bin import utils from autotest_lib.client.cros import constants def connect(bus_loop): """Create and return a DBus connection to session_manager. Connects to the session manager over the DBus system bus. Returns appropriately configured DBus interface object. @param bus_loop: An externally-owned DBusGMainLoop. @return a dbus.Interface object connection to the session_manager. """ bus = dbus.SystemBus(mainloop=bus_loop) proxy = bus.get_object('org.chromium.SessionManager', '/org/chromium/SessionManager') return dbus.Interface(proxy, 'org.chromium.SessionManagerInterface') def make_device_policy_descriptor(): """Create a PolicyDescriptor object for Chrome device policy. Creates a PolicyDescriptor suitable for storing and retrieving device policy using Session Manager's policy storage interface. @return PolicyDescriptor object for device policy, serialized as byte array. """ import policy_descriptor_pb2 descriptor = policy_descriptor_pb2.PolicyDescriptor() descriptor.account_type = policy_descriptor_pb2.ACCOUNT_TYPE_DEVICE descriptor.domain = policy_descriptor_pb2.POLICY_DOMAIN_CHROME return dbus.ByteArray(descriptor.SerializeToString()) def make_user_policy_descriptor(account_id): """Create a PolicyDescriptor object for Chrome user policy. Creates a PolicyDescriptor suitable for storing and retrieving user policy using Session Manager's policy storage interface. @param account_id: Account ID of the user to store/retrieve policy for. @return PolicyDescriptor object for user policy, serialized as byte array. """ import policy_descriptor_pb2 descriptor = policy_descriptor_pb2.PolicyDescriptor() descriptor.account_type = policy_descriptor_pb2.ACCOUNT_TYPE_USER descriptor.account_id = account_id descriptor.domain = policy_descriptor_pb2.POLICY_DOMAIN_CHROME return dbus.ByteArray(descriptor.SerializeToString()) class SignalListener(object): """A class to listen for DBus signals from the session manager. The session_manager emits several DBus signals when different events of interest occur. This class provides a framework for derived classes to use to listen for certain signals. """ def __init__(self, g_main_loop): """Constructor @param g_mail_loop: glib main loop object. """ self._main_loop = g_main_loop def wait_for_signals(self, desc, timeout=constants.DEFAULT_OWNERSHIP_TIMEOUT): """Block for |timeout| seconds waiting for the signals to come in. @param desc: string describing the high-level reason you're waiting for the signals. @param timeout: maximum seconds to wait for the signals. @raises TimeoutError if the timeout is hit. """ utils.poll_for_condition( condition=lambda: self.__received_signals(), desc=desc, timeout=timeout) self.reset_signal_state() def __received_signals(self): """Run main loop until all pending events are done, checks for signals. Runs self._main_loop until it says it has no more events pending, then returns the state of the internal variables tracking whether desired signals have been received. @return True if both signals have been handled, False otherwise. """ self.__flush() return self.all_signals_received() def __flush(self): """Runs the main loop until pending events are done.""" context = self._main_loop.get_context() while context.iteration(False): pass def reset(self): """Prepares the listener to receive a new signal. This resets the signal state and flushes any pending signals in order to avoid picking up stale signals still lingering in the process' input queues. """ self.__flush() self.reset_signal_state() def reset_signal_state(self): """Resets internal signal tracking state.""" raise NotImplementedError() def all_signals_received(self): """Resets internal signal tracking state.""" raise NotImplementedError() def listen_to_signal(self, callback, signal): """Connect a callback to a given session_manager dbus signal. Sets up a signal receiver for signal, and calls the provided callback when it comes in. @param callback: a callable to call when signal is received. @param signal: the signal to listen for. """ bus = dbus.SystemBus() bus.add_signal_receiver( handler_function=callback, signal_name=signal, dbus_interface='org.chromium.SessionManagerInterface', bus_name=None, path='/org/chromium/SessionManager') class OwnershipSignalListener(SignalListener): """A class to listen for ownership-related DBus signals. The session_manager emits a couple of DBus signals when certain events related to device ownership occur. This class provides a way to listen for them and check on their status. """ def __init__(self, g_main_loop): """Constructor @param g_mail_loop: glib main loop object. """ super(OwnershipSignalListener, self).__init__(g_main_loop) self._listen_for_new_key = False self._got_new_key = False self._listen_for_new_policy = False self._got_new_policy = False def listen_for_new_key_and_policy(self): """Set to listen for signals indicating new owner key and device policy. """ self._listen_for_new_key = self._listen_for_new_policy = True self.listen_to_signal(self.__handle_new_key, 'SetOwnerKeyComplete') self.listen_to_signal(self.__handle_new_policy, 'PropertyChangeComplete') self.reset() def listen_for_new_policy(self): """Set to listen for signal indicating new device policy. """ self._listen_for_new_key = False self._listen_for_new_policy = True self.listen_to_signal(self.__handle_new_policy, 'PropertyChangeComplete') self.reset() def reset_signal_state(self): """Resets internal signal tracking state.""" self._got_new_key = not self._listen_for_new_key self._got_new_policy = not self._listen_for_new_policy def all_signals_received(self): """Returns true when expected signals are all receieved.""" return self._got_new_key and self._got_new_policy def __handle_new_key(self, success): """Callback to be used when a new key signal is received. @param success: the string 'success' if the key was generated. """ self._got_new_key = (success == 'success') def __handle_new_policy(self, success): """Callback to be used when a new policy signal is received. @param success: the string 'success' if the policy was stored. """ self._got_new_policy = (success == 'success') class SessionSignalListener(SignalListener): """A class to listen for SessionStateChanged DBus signals. The session_manager emits a DBus signal whenever a user signs in, when the user session begins termination, and when the session is terminated. This class allows this signal to be polled for """ def __init__(self, g_main_loop): """Constructor @param g_mail_loop: glib main loop object. """ super(SessionSignalListener, self).__init__(g_main_loop) self._new_state = None self._expected_state = None def listen_for_session_state_change(self, expected): """Set to listen for state changed signal with payload == |expected|. @param expected: string representing the state transition we expect. One of 'started', 'stopping', or 'stopped'. """ if expected not in {'started', 'stopping', 'stopped'}: raise ValueError("expected must be one of 'started', 'stopping'," + " or 'stopped'.") self.listen_to_signal(self.__handle_signal, 'SessionStateChanged') self._expected_state = expected def reset_signal_state(self): """Resets internal signal tracking state.""" self._new_state = None def all_signals_received(self): """Returns true when expected signals are all receieved.""" return self._new_state == self._expected_state def __handle_signal(self, state): """Callback to be used when a new state-change signal is received. @param state: the state transition being signaled. """ self._new_state = state class ScreenIsLockedSignalListener(SignalListener): """A class to listen for ScreenIsLocked DBus signal. The session_manager emits a DBus signal when screen lock operation is completed. """ def __init__(self, g_main_loop): """Constructor @param g_main_loop: glib main loop object. """ super(ScreenIsLockedSignalListener, self).__init__(g_main_loop) self._screen_is_locked_received = False self.listen_to_signal(self.__handle_signal, 'ScreenIsLocked') def reset_signal_state(self): """Resets internal signal tracking state.""" self._screen_is_locked_received = False def all_signals_received(self): """Returns true when expected signals are all receieved.""" return self._screen_is_locked_received def __handle_signal(self): """Callback to be used when ScreenIsLocked signal is received. """ self._screen_is_locked_received = True