You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
301 lines
9.8 KiB
301 lines
9.8 KiB
# 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
|