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 <memory>
8 #include <utility>
9 #include <vector>
10
11 #include <base/bind.h>
12 #include <base/bind_helpers.h>
13 #include <base/logging.h>
14 #include <brillo/dbus/async_event_sequencer.h>
15 #include <brillo/dbus/exported_object_manager.h>
16 #include <brillo/dbus/exported_property_set.h>
17 #include <dbus/property.h>
18
19 namespace brillo {
20 namespace dbus_utils {
21
22 namespace {
23
SetupDefaultPropertyHandlers(DBusInterface * prop_interface,ExportedPropertySet * property_set)24 void SetupDefaultPropertyHandlers(DBusInterface* prop_interface,
25 ExportedPropertySet* property_set) {
26 prop_interface->AddSimpleMethodHandler(dbus::kPropertiesGetAll,
27 base::Unretained(property_set),
28 &ExportedPropertySet::HandleGetAll);
29 prop_interface->AddSimpleMethodHandlerWithError(
30 dbus::kPropertiesGet, base::Unretained(property_set),
31 &ExportedPropertySet::HandleGet);
32 prop_interface->AddSimpleMethodHandlerWithError(
33 dbus::kPropertiesSet, base::Unretained(property_set),
34 &ExportedPropertySet::HandleSet);
35 }
36
37 } // namespace
38
39 //////////////////////////////////////////////////////////////////////////////
40
DBusInterface(DBusObject * dbus_object,const std::string & interface_name)41 DBusInterface::DBusInterface(DBusObject* dbus_object,
42 const std::string& interface_name)
43 : dbus_object_(dbus_object),
44 interface_name_(interface_name),
45 release_interface_cb_(base::DoNothing()) {}
46
AddProperty(const std::string & property_name,ExportedPropertyBase * prop_base)47 void DBusInterface::AddProperty(const std::string& property_name,
48 ExportedPropertyBase* prop_base) {
49 dbus_object_->property_set_.RegisterProperty(
50 interface_name_, property_name, prop_base);
51 }
52
RemoveProperty(const std::string & property_name)53 void DBusInterface::RemoveProperty(const std::string& property_name) {
54 dbus_object_->property_set_.UnregisterProperty(interface_name_,
55 property_name);
56 }
57
ExportAsync(ExportedObjectManager * object_manager,dbus::Bus *,dbus::ExportedObject * exported_object,const dbus::ObjectPath & object_path,const AsyncEventSequencer::CompletionAction & completion_callback)58 void DBusInterface::ExportAsync(
59 ExportedObjectManager* object_manager,
60 dbus::Bus* /* bus */,
61 dbus::ExportedObject* exported_object,
62 const dbus::ObjectPath& object_path,
63 const AsyncEventSequencer::CompletionAction& completion_callback) {
64 VLOG(1) << "Registering D-Bus interface '" << interface_name_ << "' for '"
65 << object_path.value() << "'";
66 scoped_refptr<AsyncEventSequencer> sequencer(new AsyncEventSequencer());
67 for (const auto& pair : handlers_) {
68 std::string method_name = pair.first;
69 VLOG(1) << "Exporting method: " << interface_name_ << "." << method_name;
70 std::string export_error = "Failed exporting " + method_name + " method";
71 auto export_handler = sequencer->GetExportHandler(
72 interface_name_, method_name, export_error, true);
73 auto method_handler =
74 base::Bind(&DBusInterface::HandleMethodCall, base::Unretained(this));
75 exported_object->ExportMethod(
76 interface_name_, method_name, method_handler, export_handler);
77 }
78
79 std::vector<AsyncEventSequencer::CompletionAction> actions;
80 if (object_manager) {
81 auto property_writer_callback =
82 dbus_object_->property_set_.GetPropertyWriter(interface_name_);
83 actions.push_back(
84 base::Bind(&DBusInterface::ClaimInterface,
85 weak_factory_.GetWeakPtr(),
86 object_manager->AsWeakPtr(),
87 object_path,
88 property_writer_callback));
89 }
90 actions.push_back(completion_callback);
91 sequencer->OnAllTasksCompletedCall(actions);
92 }
93
ExportAndBlock(ExportedObjectManager * object_manager,dbus::Bus *,dbus::ExportedObject * exported_object,const dbus::ObjectPath & object_path)94 void DBusInterface::ExportAndBlock(
95 ExportedObjectManager* object_manager,
96 dbus::Bus* /* bus */,
97 dbus::ExportedObject* exported_object,
98 const dbus::ObjectPath& object_path) {
99 VLOG(1) << "Registering D-Bus interface '" << interface_name_ << "' for '"
100 << object_path.value() << "'";
101 for (const auto& pair : handlers_) {
102 std::string method_name = pair.first;
103 VLOG(1) << "Exporting method: " << interface_name_ << "." << method_name;
104 auto method_handler =
105 base::Bind(&DBusInterface::HandleMethodCall, base::Unretained(this));
106 if (!exported_object->ExportMethodAndBlock(
107 interface_name_, method_name, method_handler)) {
108 LOG(FATAL) << "Failed exporting " << method_name << " method";
109 }
110 }
111
112 if (object_manager) {
113 auto property_writer_callback =
114 dbus_object_->property_set_.GetPropertyWriter(interface_name_);
115 ClaimInterface(object_manager->AsWeakPtr(),
116 object_path,
117 property_writer_callback,
118 true);
119 }
120 }
121
UnexportAsync(ExportedObjectManager * object_manager,dbus::ExportedObject * exported_object,const dbus::ObjectPath & object_path,const AsyncEventSequencer::CompletionAction & completion_callback)122 void DBusInterface::UnexportAsync(
123 ExportedObjectManager* object_manager,
124 dbus::ExportedObject* exported_object,
125 const dbus::ObjectPath& object_path,
126 const AsyncEventSequencer::CompletionAction& completion_callback) {
127 VLOG(1) << "Unexporting D-Bus interface " << interface_name_ << " for "
128 << object_path.value();
129
130 // Release the interface.
131 release_interface_cb_.RunAndReset();
132
133 // Unexport all method handlers.
134 scoped_refptr<AsyncEventSequencer> sequencer(new AsyncEventSequencer());
135 for (const auto& pair : handlers_) {
136 std::string method_name = pair.first;
137 VLOG(1) << "Unexporting method: " << interface_name_ << "." << method_name;
138 std::string export_error = "Failed unexporting " + method_name + " method";
139 auto export_handler = sequencer->GetExportHandler(
140 interface_name_, method_name, export_error, true);
141 exported_object->UnexportMethod(interface_name_, method_name,
142 export_handler);
143 }
144
145 sequencer->OnAllTasksCompletedCall({completion_callback});
146 }
147
UnexportAndBlock(ExportedObjectManager * object_manager,dbus::ExportedObject * exported_object,const dbus::ObjectPath & object_path)148 void DBusInterface::UnexportAndBlock(ExportedObjectManager* object_manager,
149 dbus::ExportedObject* exported_object,
150 const dbus::ObjectPath& object_path) {
151 VLOG(1) << "Unexporting D-Bus interface " << interface_name_ << " for "
152 << object_path.value();
153
154 // Release the interface.
155 release_interface_cb_.RunAndReset();
156
157 // Unexport all method handlers.
158 for (const auto& pair : handlers_) {
159 std::string method_name = pair.first;
160 VLOG(1) << "Unexporting method: " << interface_name_ << "." << method_name;
161 if (!exported_object->UnexportMethodAndBlock(interface_name_, method_name))
162 LOG(FATAL) << "Failed unexporting " << method_name << " method";
163 }
164 }
165
ClaimInterface(base::WeakPtr<ExportedObjectManager> object_manager,const dbus::ObjectPath & object_path,const ExportedPropertySet::PropertyWriter & writer,bool all_succeeded)166 void DBusInterface::ClaimInterface(
167 base::WeakPtr<ExportedObjectManager> object_manager,
168 const dbus::ObjectPath& object_path,
169 const ExportedPropertySet::PropertyWriter& writer,
170 bool all_succeeded) {
171 if (!all_succeeded || !object_manager) {
172 LOG(ERROR) << "Skipping claiming interface: " << interface_name_;
173 return;
174 }
175 object_manager->ClaimInterface(object_path, interface_name_, writer);
176 release_interface_cb_.RunAndReset();
177 release_interface_cb_.ReplaceClosure(
178 base::Bind(&ExportedObjectManager::ReleaseInterface,
179 object_manager, object_path, interface_name_));
180 }
181
HandleMethodCall(dbus::MethodCall * method_call,ResponseSender sender)182 void DBusInterface::HandleMethodCall(dbus::MethodCall* method_call,
183 ResponseSender sender) {
184 std::string method_name = method_call->GetMember();
185 // Make a local copy of |interface_name_| because calling HandleMethod()
186 // can potentially kill this interface object...
187 std::string interface_name = interface_name_;
188 VLOG(1) << "Received method call request: " << interface_name << "."
189 << method_name << "(" << method_call->GetSignature() << ")";
190 auto pair = handlers_.find(method_name);
191 if (pair == handlers_.end()) {
192 auto response =
193 dbus::ErrorResponse::FromMethodCall(method_call,
194 DBUS_ERROR_UNKNOWN_METHOD,
195 "Unknown method: " + method_name);
196 sender.Run(std::move(response));
197 return;
198 }
199 VLOG(1) << "Dispatching DBus method call: " << method_name;
200 pair->second->HandleMethod(method_call, sender);
201 }
202
AddHandlerImpl(const std::string & method_name,std::unique_ptr<DBusInterfaceMethodHandlerInterface> handler)203 void DBusInterface::AddHandlerImpl(
204 const std::string& method_name,
205 std::unique_ptr<DBusInterfaceMethodHandlerInterface> handler) {
206 VLOG(1) << "Declaring method handler: " << interface_name_ << "."
207 << method_name;
208 auto res = handlers_.insert(std::make_pair(method_name, std::move(handler)));
209 CHECK(res.second) << "Method '" << method_name << "' already exists";
210 }
211
AddSignalImpl(const std::string & signal_name,const std::shared_ptr<DBusSignalBase> & signal)212 void DBusInterface::AddSignalImpl(
213 const std::string& signal_name,
214 const std::shared_ptr<DBusSignalBase>& signal) {
215 VLOG(1) << "Declaring a signal sink: " << interface_name_ << "."
216 << signal_name;
217 CHECK(signals_.insert(std::make_pair(signal_name, signal)).second)
218 << "The signal '" << signal_name << "' is already registered";
219 }
220
221 ///////////////////////////////////////////////////////////////////////////////
222
DBusObject(ExportedObjectManager * object_manager,const scoped_refptr<dbus::Bus> & bus,const dbus::ObjectPath & object_path)223 DBusObject::DBusObject(ExportedObjectManager* object_manager,
224 const scoped_refptr<dbus::Bus>& bus,
225 const dbus::ObjectPath& object_path)
226 : DBusObject::DBusObject(object_manager,
227 bus,
228 object_path,
229 base::Bind(&SetupDefaultPropertyHandlers)) {}
230
DBusObject(ExportedObjectManager * object_manager,const scoped_refptr<dbus::Bus> & bus,const dbus::ObjectPath & object_path,PropertyHandlerSetupCallback property_handler_setup_callback)231 DBusObject::DBusObject(
232 ExportedObjectManager* object_manager,
233 const scoped_refptr<dbus::Bus>& bus,
234 const dbus::ObjectPath& object_path,
235 PropertyHandlerSetupCallback property_handler_setup_callback)
236 : property_set_(bus.get()),
237 bus_(bus),
238 object_path_(object_path),
239 property_handler_setup_callback_(
240 std::move(property_handler_setup_callback)) {
241 if (object_manager)
242 object_manager_ = object_manager->AsWeakPtr();
243 }
244
~DBusObject()245 DBusObject::~DBusObject() {
246 if (exported_object_)
247 exported_object_->Unregister();
248 }
249
AddOrGetInterface(const std::string & interface_name)250 DBusInterface* DBusObject::AddOrGetInterface(
251 const std::string& interface_name) {
252 auto iter = interfaces_.find(interface_name);
253 if (iter == interfaces_.end()) {
254 VLOG(1) << "Adding an interface '" << interface_name << "' to object '"
255 << object_path_.value() << "'.";
256 // Interface doesn't exist yet. Create one...
257 std::unique_ptr<DBusInterface> new_itf(
258 new DBusInterface(this, interface_name));
259 iter = interfaces_.insert(std::make_pair(interface_name,
260 std::move(new_itf))).first;
261 }
262 return iter->second.get();
263 }
264
FindInterface(const std::string & interface_name) const265 DBusInterface* DBusObject::FindInterface(
266 const std::string& interface_name) const {
267 auto itf_iter = interfaces_.find(interface_name);
268 return (itf_iter == interfaces_.end()) ? nullptr : itf_iter->second.get();
269 }
270
RemoveInterface(const std::string & interface_name)271 void DBusObject::RemoveInterface(const std::string& interface_name) {
272 auto itf_iter = interfaces_.find(interface_name);
273 CHECK(itf_iter != interfaces_.end())
274 << "Interface " << interface_name << " has not been added.";
275 interfaces_.erase(itf_iter);
276 }
277
ExportInterfaceAsync(const std::string & interface_name,const AsyncEventSequencer::CompletionAction & completion_callback)278 void DBusObject::ExportInterfaceAsync(
279 const std::string& interface_name,
280 const AsyncEventSequencer::CompletionAction& completion_callback) {
281 AddOrGetInterface(interface_name)
282 ->ExportAsync(object_manager_.get(), bus_.get(), exported_object_,
283 object_path_, completion_callback);
284 }
285
ExportInterfaceAndBlock(const std::string & interface_name)286 void DBusObject::ExportInterfaceAndBlock(const std::string& interface_name) {
287 AddOrGetInterface(interface_name)
288 ->ExportAndBlock(object_manager_.get(), bus_.get(), exported_object_,
289 object_path_);
290 }
291
UnexportInterfaceAsync(const std::string & interface_name,const AsyncEventSequencer::CompletionAction & completion_callback)292 void DBusObject::UnexportInterfaceAsync(
293 const std::string& interface_name,
294 const AsyncEventSequencer::CompletionAction& completion_callback) {
295 AddOrGetInterface(interface_name)
296 ->UnexportAsync(object_manager_.get(), exported_object_, object_path_,
297 completion_callback);
298 }
299
UnexportInterfaceAndBlock(const std::string & interface_name)300 void DBusObject::UnexportInterfaceAndBlock(const std::string& interface_name) {
301 AddOrGetInterface(interface_name)
302 ->UnexportAndBlock(object_manager_.get(), exported_object_, object_path_);
303 }
304
RegisterAsync(const AsyncEventSequencer::CompletionAction & completion_callback)305 void DBusObject::RegisterAsync(
306 const AsyncEventSequencer::CompletionAction& completion_callback) {
307 VLOG(1) << "Registering D-Bus object '" << object_path_.value() << "'.";
308 CHECK(exported_object_ == nullptr) << "Object already registered.";
309 scoped_refptr<AsyncEventSequencer> sequencer(new AsyncEventSequencer());
310 exported_object_ = bus_->GetExportedObject(object_path_);
311
312 RegisterPropertiesInterface();
313
314 // Export interface methods
315 for (const auto& pair : interfaces_) {
316 pair.second->ExportAsync(
317 object_manager_.get(),
318 bus_.get(),
319 exported_object_,
320 object_path_,
321 sequencer->GetHandler("Failed to export interface " + pair.first,
322 false));
323 }
324
325 sequencer->OnAllTasksCompletedCall({completion_callback});
326 }
327
RegisterAndBlock()328 void DBusObject::RegisterAndBlock() {
329 VLOG(1) << "Registering D-Bus object '" << object_path_.value() << "'.";
330 CHECK(exported_object_ == nullptr) << "Object already registered.";
331 exported_object_ = bus_->GetExportedObject(object_path_);
332
333 RegisterPropertiesInterface();
334
335 // Export interface methods
336 for (const auto& pair : interfaces_) {
337 pair.second->ExportAndBlock(
338 object_manager_.get(),
339 bus_.get(),
340 exported_object_,
341 object_path_);
342 }
343 }
344
UnregisterAsync()345 void DBusObject::UnregisterAsync() {
346 VLOG(1) << "Unregistering D-Bus object '" << object_path_.value() << "'.";
347 CHECK(exported_object_ != nullptr) << "Object not registered.";
348
349 // This will unregister the object path from the bus.
350 exported_object_->Unregister();
351 // This will remove |exported_object_| from bus's object table. This function
352 // will also post a task to unregister |exported_object_| (same as the call
353 // above), which will be a no-op since it is already done by then.
354 // By doing both in here, the object path is guarantee to be reusable upon
355 // return from this function.
356 bus_->UnregisterExportedObject(object_path_);
357 exported_object_ = nullptr;
358 }
359
SendSignal(dbus::Signal * signal)360 bool DBusObject::SendSignal(dbus::Signal* signal) {
361 if (exported_object_) {
362 exported_object_->SendSignal(signal);
363 return true;
364 }
365 LOG(ERROR) << "Trying to send a signal from an object that is not exported";
366 return false;
367 }
368
RegisterPropertiesInterface()369 void DBusObject::RegisterPropertiesInterface() {
370 DBusInterface* prop_interface = AddOrGetInterface(dbus::kPropertiesInterface);
371 property_handler_setup_callback_.Run(prop_interface, &property_set_);
372 property_set_.OnPropertiesInterfaceExported(prop_interface);
373 }
374
375 } // namespace dbus_utils
376 } // namespace brillo
377