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
16namespace brillo {
17namespace dbus_utils {
18
19//////////////////////////////////////////////////////////////////////////////
20
21DBusInterface::DBusInterface(DBusObject* dbus_object,
22                             const std::string& interface_name)
23    : dbus_object_(dbus_object), interface_name_(interface_name) {
24}
25
26void 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
32void 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
68void 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
96void 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
111void 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
132void 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
141void 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
152DBusObject::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
160DBusObject::~DBusObject() {
161  if (exported_object_)
162    exported_object_->Unregister();
163}
164
165DBusInterface* 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
180DBusInterface* 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
186void 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
223void 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
254void 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
269bool 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