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