xref: /aosp_15_r20/external/autotest/client/cros/dbus_util.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li# Lint as: python2, python3
2*9c5db199SXin Li# Copyright 2014 The Chromium OS Authors. All rights reserved.
3*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be
4*9c5db199SXin Li# found in the LICENSE file.
5*9c5db199SXin Li
6*9c5db199SXin Lifrom __future__ import absolute_import
7*9c5db199SXin Lifrom __future__ import division
8*9c5db199SXin Lifrom __future__ import print_function
9*9c5db199SXin Li
10*9c5db199SXin Liimport dbus
11*9c5db199SXin Liimport logging
12*9c5db199SXin Liimport six
13*9c5db199SXin Li
14*9c5db199SXin Liimport common
15*9c5db199SXin Li
16*9c5db199SXin Lifrom autotest_lib.client.bin import utils
17*9c5db199SXin Lifrom autotest_lib.client.common_lib import seven
18*9c5db199SXin Li
19*9c5db199SXin Li
20*9c5db199SXin LiDBUS_INTERFACE_OBJECT_MANAGER = 'org.freedesktop.DBus.ObjectManager'
21*9c5db199SXin LiDBUS_ERROR_SERVICEUNKNOWN = 'org.freedesktop.DBus.Error.ServiceUnknown'
22*9c5db199SXin Li
23*9c5db199SXin Li
24*9c5db199SXin Lidef dbus2primitive(value):
25*9c5db199SXin Li    """Convert values from dbus types to python types.
26*9c5db199SXin Li
27*9c5db199SXin Li    @param value: dbus object to convert to a primitive.
28*9c5db199SXin Li
29*9c5db199SXin Li    """
30*9c5db199SXin Li    if isinstance(value, dbus.Boolean):
31*9c5db199SXin Li        return bool(value)
32*9c5db199SXin Li    elif isinstance(value, int):
33*9c5db199SXin Li        return int(value)
34*9c5db199SXin Li    elif isinstance(value, dbus.UInt16):
35*9c5db199SXin Li        return seven.ensure_long(value)
36*9c5db199SXin Li    elif isinstance(value, dbus.UInt32):
37*9c5db199SXin Li        return seven.ensure_long(value)
38*9c5db199SXin Li    elif isinstance(value, dbus.UInt64):
39*9c5db199SXin Li        return seven.ensure_long(value)
40*9c5db199SXin Li    elif isinstance(value, float):
41*9c5db199SXin Li        return float(value)
42*9c5db199SXin Li    elif isinstance(value, str):
43*9c5db199SXin Li        return str(value)
44*9c5db199SXin Li    elif isinstance(value, six.text_type):
45*9c5db199SXin Li        return str(value.encode('utf-8'))
46*9c5db199SXin Li    elif isinstance(value, list):
47*9c5db199SXin Li        return [dbus2primitive(x) for x in value]
48*9c5db199SXin Li    elif isinstance(value, tuple):
49*9c5db199SXin Li        return tuple([dbus2primitive(x) for x in value])
50*9c5db199SXin Li    elif isinstance(value, dict):
51*9c5db199SXin Li        return dict([(dbus2primitive(k), dbus2primitive(v))
52*9c5db199SXin Li                     for k, v in value.items()])
53*9c5db199SXin Li    else:
54*9c5db199SXin Li        logging.error('Failed to convert dbus object of class: %r',
55*9c5db199SXin Li                      value.__class__.__name__)
56*9c5db199SXin Li        return value
57*9c5db199SXin Li
58*9c5db199SXin Li
59*9c5db199SXin Lidef get_objects_with_interface(service_name, object_manager_path,
60*9c5db199SXin Li                               dbus_interface, path_prefix=None,
61*9c5db199SXin Li                               bus=None):
62*9c5db199SXin Li    """Get objects that have a particular interface via a property manager.
63*9c5db199SXin Li
64*9c5db199SXin Li    @param service_name: string remote service exposing the object manager
65*9c5db199SXin Li            to query (e.g. 'org.chromium.peerd').
66*9c5db199SXin Li    @param object_manager_path: string DBus path of object manager on remote
67*9c5db199SXin Li            service (e.g. '/org/chromium/peerd')
68*9c5db199SXin Li    @param dbus_interface: string interface of object we're interested in.
69*9c5db199SXin Li    @param path_prefix: string prefix of DBus path to filter for.  If not
70*9c5db199SXin Li            None, we'll return only objects in the remote service whose
71*9c5db199SXin Li            paths start with this prefix.
72*9c5db199SXin Li    @param bus: dbus.Bus object, defaults to dbus.SystemBus().  Note that
73*9c5db199SXin Li            normally, dbus.SystemBus() multiplexes a single DBus connection
74*9c5db199SXin Li            among its instances.
75*9c5db199SXin Li    @return dict that maps object paths to dicts of interface name to properties
76*9c5db199SXin Li            exposed by that interface.  This is similar to the structure
77*9c5db199SXin Li            returned by org.freedesktop.DBus.ObjectManaber.GetManagedObjects().
78*9c5db199SXin Li
79*9c5db199SXin Li    """
80*9c5db199SXin Li    if bus is None:
81*9c5db199SXin Li        bus = dbus.SystemBus()
82*9c5db199SXin Li    object_manager = dbus.Interface(
83*9c5db199SXin Li            bus.get_object(service_name, object_manager_path),
84*9c5db199SXin Li            dbus_interface=DBUS_INTERFACE_OBJECT_MANAGER)
85*9c5db199SXin Li    objects = dbus2primitive(object_manager.GetManagedObjects())
86*9c5db199SXin Li    logging.debug('Saw objects %r', objects)
87*9c5db199SXin Li    # Filter by interface.
88*9c5db199SXin Li    objects = [(path, interfaces)
89*9c5db199SXin Li               for path, interfaces in six.iteritems(objects)
90*9c5db199SXin Li               if dbus_interface in interfaces]
91*9c5db199SXin Li    if path_prefix is not None:
92*9c5db199SXin Li        objects = [(path, interfaces)
93*9c5db199SXin Li                   for path, interfaces in objects
94*9c5db199SXin Li                   if path.startswith(path_prefix)]
95*9c5db199SXin Li    objects = dict(objects)
96*9c5db199SXin Li    logging.debug('Filtered objects: %r', objects)
97*9c5db199SXin Li    return objects
98*9c5db199SXin Li
99*9c5db199SXin Lidef get_dbus_object(bus, service_name, object_manager_path, timeout=None):
100*9c5db199SXin Li    """Keeps trying to get the a DBus object until a timeout expires.
101*9c5db199SXin Li    Useful if a test should wait for a system daemon to start up.
102*9c5db199SXin Li
103*9c5db199SXin Li    @param bus: dbus.Bus object.
104*9c5db199SXin Li    @param service_name: string service to look up (e.g. 'org.chromium.peerd').
105*9c5db199SXin Li    @param object_manager_path: string DBus path of object manager on remote
106*9c5db199SXin Li            service (e.g. '/org/chromium/peerd')
107*9c5db199SXin Li    @param timeout: maximum time in seconds to wait for the bus object.
108*9c5db199SXin Li    @return The DBus object or None if the timeout expired.
109*9c5db199SXin Li
110*9c5db199SXin Li    """
111*9c5db199SXin Li
112*9c5db199SXin Li    def try_get_object():
113*9c5db199SXin Li        try:
114*9c5db199SXin Li            return bus.get_object(service_name, object_manager_path)
115*9c5db199SXin Li        except dbus.exceptions.DBusException as e:
116*9c5db199SXin Li            # Only handle DBUS_ERROR_SERVICEUNKNOWN, which is thrown when the
117*9c5db199SXin Li            # service is not running yet. Otherwise, rethrow.
118*9c5db199SXin Li            if e.get_dbus_name() == DBUS_ERROR_SERVICEUNKNOWN:
119*9c5db199SXin Li                return None
120*9c5db199SXin Li            raise
121*9c5db199SXin Li
122*9c5db199SXin Li    return utils.poll_for_condition(
123*9c5db199SXin Li            condition=try_get_object,
124*9c5db199SXin Li            desc='Get bus object "%s" / "%s"' % (service_name,
125*9c5db199SXin Li                                                 object_manager_path),
126*9c5db199SXin Li            timeout=timeout or 0)
127