1# Copyright 2015 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file.
3
4import dbus
5import dbus.service
6import logging
7
8
9class ObjectManager(dbus.service.Object):
10    """Exports the org.freedesktop.DBus.ObjectManager interface:
11
12    GetManagedObjects(
13            out DICT<OBJPATH,DICT<STRING,DICT<STRING,VARIANT>>>
14                    objpath_interfaces_and_properties);
15
16    The return value of this method is a dict whose keys are object
17    paths. All returned object paths are children of the object path
18    implementing this interface, i.e. their object paths start with
19    the ObjectManager's object path plus '/'.
20
21    Each value is a dict whose keys are interfaces names. Each value
22    in this inner dict is the same dict that would be returned by the
23    org.freedesktop.DBus.Properties.GetAll() method for that
24    combination of object path and interface. If an interface has no
25    properties, the empty dict is returned.
26
27    Changes are emitted using the following two signals:
28
29    InterfacesAdded(
30            OBJPATH object_path,
31            DICT<STRING,DICT<STRING,VARIANT>> interfaces_and_properties);
32
33    InterfacesRemoved (OBJPATH object_path, ARRAY<STRING> interfaces);
34
35    """
36
37    OBJECT_MANAGER_INTERFACE = 'org.freedesktop.DBus.ObjectManager'
38
39
40    def __init__(self, bus, path):
41        super(ObjectManager, self).__init__(bus, path)
42        self._paths = dict()
43
44
45    def claim_interface(self, path, interface_name, property_getter):
46        """Claim an interface for an object exposed under this ObjectManager.
47
48        @param path: string object path of object exposed.
49        @param interface_name: string DBus interface name of exposed object.
50        @param property_getter: function that takes no objects and returns
51                a dictionary mapping from property name to property value.
52                Both property names and values must be DBus types.
53
54        """
55        logging.debug('claim_interface(%s, %s, ...)', path, interface_name)
56        if path in self._paths:
57            self._paths[path][interface_name] = property_getter
58        else:
59            self._paths[path] = {interface_name: property_getter}
60        interface2properties = dbus.Dictionary(
61                {dbus.String(interface_name): property_getter()}, 'sa{sv}')
62        self.InterfacesAdded(dbus.ObjectPath(path), interface2properties)
63
64
65    def release_interface(self, path, interface_name):
66        """Release an interface previously claimed for a particular |path|.
67
68        You may call this method even if the interface has not been claimed.
69        This is intended to simplify destruction patterns.
70
71        @param path: string path exposed previously.
72        @param interface_name: string DBus interface name previously claimed.
73
74        """
75        logging.debug('release_interface(%s, %s)', path, interface_name)
76        if path not in self._paths or interface_name not in self._paths[path]:
77            return
78        self._paths[path].pop(interface_name)
79        if not self._paths[path]:
80            self._paths.pop(path)
81        self.InterfacesRemoved(dbus.ObjectPath(path),
82                               dbus.Array([dbus.String(interface_name)]))
83
84
85    @dbus.service.method(dbus_interface=OBJECT_MANAGER_INTERFACE,
86                         in_signature='', out_signature='a{oa{sa{sv}}}')
87    def GetManagedObjects(self):
88        """Implementation of DBus interface method of the same name.
89
90        @return see class documentation.
91
92        """
93        logging.debug('Received call to GetManagedObjects()')
94        result = dbus.Dictionary(dict(), 'oa{sa{sv}}')
95        for path, interfaces in self._paths.iteritems():
96            dbus_path = dbus.ObjectPath(path)
97            result[dbus_path] = dbus.Dictionary(dict(), 'sa{sv}')
98            for interface_name, property_getter in interfaces.iteritems():
99                dbus_if_name = dbus.String(interface_name)
100                result[dbus_path][dbus_if_name] = property_getter()
101        return result
102
103
104    @dbus.service.signal(dbus_interface=OBJECT_MANAGER_INTERFACE,
105                         signature='oa{sa{sv}}')
106    def InterfacesAdded(self, object_path, interfaces2properties):
107        """Implementation of DBus interface signal.
108
109        @param object_path: dbus.ObjectPath object.
110        @param interfaces2properties: see class documentation.
111
112        """
113
114
115    @dbus.service.signal(dbus_interface=OBJECT_MANAGER_INTERFACE,
116                         signature='oas')
117    def InterfacesRemoved(self, object_path, interfaces):
118        """Implementation of DBus interface signal.
119
120        @param object_path: dbus.ObjectPath object.
121        @param interfaces: see class documentation.
122
123        """
124