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 21 DBusInterface::DBusInterface(DBusObject* dbus_object, 22 const std::string& interface_name) 23 : dbus_object_(dbus_object), interface_name_(interface_name) { 24 } 25 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 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 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 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_.Reset( 107 base::Bind(&ExportedObjectManager::ReleaseInterface, 108 object_manager, object_path, interface_name_)); 109 } 110 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 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 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 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 160 DBusObject::~DBusObject() { 161 if (exported_object_) 162 exported_object_->Unregister(); 163 } 164 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 180 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 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 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 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 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