1 // Copyright 2014 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
5 #include <brillo/dbus/dbus_object.h>
6
7 #include <vector>
8
9 #include <base/bind.h>
10 #include <base/logging.h>
11 #include <brillo/dbus/async_event_sequencer.h>
12 #include <brillo/dbus/exported_object_manager.h>
13 #include <brillo/dbus/exported_property_set.h>
14 #include <dbus/property.h>
15
16 namespace brillo {
17 namespace dbus_utils {
18
19 //////////////////////////////////////////////////////////////////////////////
20
DBusInterface(DBusObject * dbus_object,const std::string & interface_name)21 DBusInterface::DBusInterface(DBusObject* dbus_object,
22 const std::string& interface_name)
23 : dbus_object_(dbus_object), interface_name_(interface_name) {
24 }
25
AddProperty(const std::string & property_name,ExportedPropertyBase * prop_base)26 void DBusInterface::AddProperty(const std::string& property_name,
27 ExportedPropertyBase* prop_base) {
28 dbus_object_->property_set_.RegisterProperty(
29 interface_name_, property_name, prop_base);
30 }
31
ExportAsync(ExportedObjectManager * object_manager,dbus::Bus *,dbus::ExportedObject * exported_object,const dbus::ObjectPath & object_path,const AsyncEventSequencer::CompletionAction & completion_callback)32 void DBusInterface::ExportAsync(
33 ExportedObjectManager* object_manager,
34 dbus::Bus* /* bus */,
35 dbus::ExportedObject* exported_object,
36 const dbus::ObjectPath& object_path,
37 const AsyncEventSequencer::CompletionAction& completion_callback) {
38 VLOG(1) << "Registering D-Bus interface '" << interface_name_ << "' for '"
39 << object_path.value() << "'";
40 scoped_refptr<AsyncEventSequencer> sequencer(new AsyncEventSequencer());
41 for (const auto& pair : handlers_) {
42 std::string method_name = pair.first;
43 VLOG(1) << "Exporting method: " << interface_name_ << "." << method_name;
44 std::string export_error = "Failed exporting " + method_name + " method";
45 auto export_handler = sequencer->GetExportHandler(
46 interface_name_, method_name, export_error, true);
47 auto method_handler =
48 base::Bind(&DBusInterface::HandleMethodCall, base::Unretained(this));
49 exported_object->ExportMethod(
50 interface_name_, method_name, method_handler, export_handler);
51 }
52
53 std::vector<AsyncEventSequencer::CompletionAction> actions;
54 if (object_manager) {
55 auto property_writer_callback =
56 dbus_object_->property_set_.GetPropertyWriter(interface_name_);
57 actions.push_back(
58 base::Bind(&DBusInterface::ClaimInterface,
59 weak_factory_.GetWeakPtr(),
60 object_manager->AsWeakPtr(),
61 object_path,
62 property_writer_callback));
63 }
64 actions.push_back(completion_callback);
65 sequencer->OnAllTasksCompletedCall(actions);
66 }
67
ExportAndBlock(ExportedObjectManager * object_manager,dbus::Bus *,dbus::ExportedObject * exported_object,const dbus::ObjectPath & object_path)68 void DBusInterface::ExportAndBlock(
69 ExportedObjectManager* object_manager,
70 dbus::Bus* /* bus */,
71 dbus::ExportedObject* exported_object,
72 const dbus::ObjectPath& object_path) {
73 VLOG(1) << "Registering D-Bus interface '" << interface_name_ << "' for '"
74 << object_path.value() << "'";
75 for (const auto& pair : handlers_) {
76 std::string method_name = pair.first;
77 VLOG(1) << "Exporting method: " << interface_name_ << "." << method_name;
78 auto method_handler =
79 base::Bind(&DBusInterface::HandleMethodCall, base::Unretained(this));
80 if (!exported_object->ExportMethodAndBlock(
81 interface_name_, method_name, method_handler)) {
82 LOG(FATAL) << "Failed exporting " << method_name << " method";
83 }
84 }
85
86 if (object_manager) {
87 auto property_writer_callback =
88 dbus_object_->property_set_.GetPropertyWriter(interface_name_);
89 ClaimInterface(object_manager->AsWeakPtr(),
90 object_path,
91 property_writer_callback,
92 true);
93 }
94 }
95
ClaimInterface(base::WeakPtr<ExportedObjectManager> object_manager,const dbus::ObjectPath & object_path,const ExportedPropertySet::PropertyWriter & writer,bool all_succeeded)96 void DBusInterface::ClaimInterface(
97 base::WeakPtr<ExportedObjectManager> object_manager,
98 const dbus::ObjectPath& object_path,
99 const ExportedPropertySet::PropertyWriter& writer,
100 bool all_succeeded) {
101 if (!all_succeeded || !object_manager) {
102 LOG(ERROR) << "Skipping claiming interface: " << interface_name_;
103 return;
104 }
105 object_manager->ClaimInterface(object_path, interface_name_, writer);
106 release_interface_cb_.ReplaceClosure(
107 base::Bind(&ExportedObjectManager::ReleaseInterface,
108 object_manager, object_path, interface_name_));
109 }
110
HandleMethodCall(dbus::MethodCall * method_call,ResponseSender sender)111 void DBusInterface::HandleMethodCall(dbus::MethodCall* method_call,
112 ResponseSender sender) {
113 std::string method_name = method_call->GetMember();
114 // Make a local copy of |interface_name_| because calling HandleMethod()
115 // can potentially kill this interface object...
116 std::string interface_name = interface_name_;
117 VLOG(1) << "Received method call request: " << interface_name << "."
118 << method_name << "(" << method_call->GetSignature() << ")";
119 auto pair = handlers_.find(method_name);
120 if (pair == handlers_.end()) {
121 auto response =
122 dbus::ErrorResponse::FromMethodCall(method_call,
123 DBUS_ERROR_UNKNOWN_METHOD,
124 "Unknown method: " + method_name);
125 sender.Run(std::move(response));
126 return;
127 }
128 VLOG(1) << "Dispatching DBus method call: " << method_name;
129 pair->second->HandleMethod(method_call, sender);
130 }
131
AddHandlerImpl(const std::string & method_name,std::unique_ptr<DBusInterfaceMethodHandlerInterface> handler)132 void DBusInterface::AddHandlerImpl(
133 const std::string& method_name,
134 std::unique_ptr<DBusInterfaceMethodHandlerInterface> handler) {
135 VLOG(1) << "Declaring method handler: " << interface_name_ << "."
136 << method_name;
137 auto res = handlers_.insert(std::make_pair(method_name, std::move(handler)));
138 CHECK(res.second) << "Method '" << method_name << "' already exists";
139 }
140
AddSignalImpl(const std::string & signal_name,const std::shared_ptr<DBusSignalBase> & signal)141 void DBusInterface::AddSignalImpl(
142 const std::string& signal_name,
143 const std::shared_ptr<DBusSignalBase>& signal) {
144 VLOG(1) << "Declaring a signal sink: " << interface_name_ << "."
145 << signal_name;
146 CHECK(signals_.insert(std::make_pair(signal_name, signal)).second)
147 << "The signal '" << signal_name << "' is already registered";
148 }
149
150 ///////////////////////////////////////////////////////////////////////////////
151
DBusObject(ExportedObjectManager * object_manager,const scoped_refptr<dbus::Bus> & bus,const dbus::ObjectPath & object_path)152 DBusObject::DBusObject(ExportedObjectManager* object_manager,
153 const scoped_refptr<dbus::Bus>& bus,
154 const dbus::ObjectPath& object_path)
155 : property_set_(bus.get()), bus_(bus), object_path_(object_path) {
156 if (object_manager)
157 object_manager_ = object_manager->AsWeakPtr();
158 }
159
~DBusObject()160 DBusObject::~DBusObject() {
161 if (exported_object_)
162 exported_object_->Unregister();
163 }
164
AddOrGetInterface(const std::string & interface_name)165 DBusInterface* DBusObject::AddOrGetInterface(
166 const std::string& interface_name) {
167 auto iter = interfaces_.find(interface_name);
168 if (iter == interfaces_.end()) {
169 VLOG(1) << "Adding an interface '" << interface_name << "' to object '"
170 << object_path_.value() << "'.";
171 // Interface doesn't exist yet. Create one...
172 std::unique_ptr<DBusInterface> new_itf(
173 new DBusInterface(this, interface_name));
174 iter = interfaces_.insert(std::make_pair(interface_name,
175 std::move(new_itf))).first;
176 }
177 return iter->second.get();
178 }
179
FindInterface(const std::string & interface_name) const180 DBusInterface* DBusObject::FindInterface(
181 const std::string& interface_name) const {
182 auto itf_iter = interfaces_.find(interface_name);
183 return (itf_iter == interfaces_.end()) ? nullptr : itf_iter->second.get();
184 }
185
RegisterAsync(const AsyncEventSequencer::CompletionAction & completion_callback)186 void DBusObject::RegisterAsync(
187 const AsyncEventSequencer::CompletionAction& completion_callback) {
188 VLOG(1) << "Registering D-Bus object '" << object_path_.value() << "'.";
189 CHECK(exported_object_ == nullptr) << "Object already registered.";
190 scoped_refptr<AsyncEventSequencer> sequencer(new AsyncEventSequencer());
191 exported_object_ = bus_->GetExportedObject(object_path_);
192
193 // Add the org.freedesktop.DBus.Properties interface to the object.
194 DBusInterface* prop_interface = AddOrGetInterface(dbus::kPropertiesInterface);
195 prop_interface->AddSimpleMethodHandler(
196 dbus::kPropertiesGetAll,
197 base::Unretained(&property_set_),
198 &ExportedPropertySet::HandleGetAll);
199 prop_interface->AddSimpleMethodHandlerWithError(
200 dbus::kPropertiesGet,
201 base::Unretained(&property_set_),
202 &ExportedPropertySet::HandleGet);
203 prop_interface->AddSimpleMethodHandlerWithError(
204 dbus::kPropertiesSet,
205 base::Unretained(&property_set_),
206 &ExportedPropertySet::HandleSet);
207 property_set_.OnPropertiesInterfaceExported(prop_interface);
208
209 // Export interface methods
210 for (const auto& pair : interfaces_) {
211 pair.second->ExportAsync(
212 object_manager_.get(),
213 bus_.get(),
214 exported_object_,
215 object_path_,
216 sequencer->GetHandler("Failed to export interface " + pair.first,
217 false));
218 }
219
220 sequencer->OnAllTasksCompletedCall({completion_callback});
221 }
222
RegisterAndBlock()223 void DBusObject::RegisterAndBlock() {
224 VLOG(1) << "Registering D-Bus object '" << object_path_.value() << "'.";
225 CHECK(exported_object_ == nullptr) << "Object already registered.";
226 exported_object_ = bus_->GetExportedObject(object_path_);
227
228 // Add the org.freedesktop.DBus.Properties interface to the object.
229 DBusInterface* prop_interface = AddOrGetInterface(dbus::kPropertiesInterface);
230 prop_interface->AddSimpleMethodHandler(
231 dbus::kPropertiesGetAll,
232 base::Unretained(&property_set_),
233 &ExportedPropertySet::HandleGetAll);
234 prop_interface->AddSimpleMethodHandlerWithError(
235 dbus::kPropertiesGet,
236 base::Unretained(&property_set_),
237 &ExportedPropertySet::HandleGet);
238 prop_interface->AddSimpleMethodHandlerWithError(
239 dbus::kPropertiesSet,
240 base::Unretained(&property_set_),
241 &ExportedPropertySet::HandleSet);
242 property_set_.OnPropertiesInterfaceExported(prop_interface);
243
244 // Export interface methods
245 for (const auto& pair : interfaces_) {
246 pair.second->ExportAndBlock(
247 object_manager_.get(),
248 bus_.get(),
249 exported_object_,
250 object_path_);
251 }
252 }
253
UnregisterAsync()254 void DBusObject::UnregisterAsync() {
255 VLOG(1) << "Unregistering D-Bus object '" << object_path_.value() << "'.";
256 CHECK(exported_object_ != nullptr) << "Object not registered.";
257
258 // This will unregister the object path from the bus.
259 exported_object_->Unregister();
260 // This will remove |exported_object_| from bus's object table. This function
261 // will also post a task to unregister |exported_object_| (same as the call
262 // above), which will be a no-op since it is already done by then.
263 // By doing both in here, the object path is guarantee to be reusable upon
264 // return from this function.
265 bus_->UnregisterExportedObject(object_path_);
266 exported_object_ = nullptr;
267 }
268
SendSignal(dbus::Signal * signal)269 bool DBusObject::SendSignal(dbus::Signal* signal) {
270 if (exported_object_) {
271 exported_object_->SendSignal(signal);
272 return true;
273 }
274 LOG(ERROR) << "Trying to send a signal from an object that is not exported";
275 return false;
276 }
277
278 } // namespace dbus_utils
279 } // namespace brillo
280