1 // Copyright (c) 2012 The Chromium 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 "dbus/exported_object.h" 6 7 #include <stdint.h> 8 #include <utility> 9 10 #include "base/bind.h" 11 #include "base/logging.h" 12 #include "base/memory/ref_counted.h" 13 #include "base/message_loop/message_loop.h" 14 #include "base/metrics/histogram.h" 15 #include "base/threading/thread_restrictions.h" 16 #include "base/time/time.h" 17 #include "dbus/bus.h" 18 #include "dbus/message.h" 19 #include "dbus/object_path.h" 20 #include "dbus/scoped_dbus_error.h" 21 #include "dbus/util.h" 22 23 namespace dbus { 24 25 namespace { 26 27 // Used for success ratio histograms. 1 for success, 0 for failure. 28 const int kSuccessRatioHistogramMaxValue = 2; 29 30 } // namespace 31 32 ExportedObject::ExportedObject(Bus* bus, 33 const ObjectPath& object_path) 34 : bus_(bus), 35 object_path_(object_path), 36 object_is_registered_(false) { 37 } 38 39 ExportedObject::~ExportedObject() { 40 DCHECK(!object_is_registered_); 41 } 42 43 bool ExportedObject::ExportMethodAndBlock( 44 const std::string& interface_name, 45 const std::string& method_name, 46 MethodCallCallback method_call_callback) { 47 bus_->AssertOnDBusThread(); 48 49 // Check if the method is already exported. 50 const std::string absolute_method_name = 51 GetAbsoluteMemberName(interface_name, method_name); 52 if (method_table_.find(absolute_method_name) != method_table_.end()) { 53 LOG(ERROR) << absolute_method_name << " is already exported"; 54 return false; 55 } 56 57 if (!bus_->Connect()) 58 return false; 59 if (!bus_->SetUpAsyncOperations()) 60 return false; 61 if (!Register()) 62 return false; 63 64 // Add the method callback to the method table. 65 method_table_[absolute_method_name] = method_call_callback; 66 67 return true; 68 } 69 70 void ExportedObject::ExportMethod(const std::string& interface_name, 71 const std::string& method_name, 72 MethodCallCallback method_call_callback, 73 OnExportedCallback on_exported_calback) { 74 bus_->AssertOnOriginThread(); 75 76 base::Closure task = base::Bind(&ExportedObject::ExportMethodInternal, 77 this, 78 interface_name, 79 method_name, 80 method_call_callback, 81 on_exported_calback); 82 bus_->GetDBusTaskRunner()->PostTask(FROM_HERE, task); 83 } 84 85 void ExportedObject::SendSignal(Signal* signal) { 86 // For signals, the object path should be set to the path to the sender 87 // object, which is this exported object here. 88 CHECK(signal->SetPath(object_path_)); 89 90 // Increment the reference count so we can safely reference the 91 // underlying signal message until the signal sending is complete. This 92 // will be unref'ed in SendSignalInternal(). 93 DBusMessage* signal_message = signal->raw_message(); 94 dbus_message_ref(signal_message); 95 96 const base::TimeTicks start_time = base::TimeTicks::Now(); 97 if (bus_->GetDBusTaskRunner()->RunsTasksOnCurrentThread()) { 98 // The Chrome OS power manager doesn't use a dedicated TaskRunner for 99 // sending DBus messages. Sending signals asynchronously can cause an 100 // inversion in the message order if the power manager calls 101 // ObjectProxy::CallMethodAndBlock() before going back to the top level of 102 // the MessageLoop: crbug.com/472361. 103 SendSignalInternal(start_time, signal_message); 104 } else { 105 bus_->GetDBusTaskRunner()->PostTask( 106 FROM_HERE, 107 base::Bind(&ExportedObject::SendSignalInternal, 108 this, 109 start_time, 110 signal_message)); 111 } 112 } 113 114 void ExportedObject::Unregister() { 115 bus_->AssertOnDBusThread(); 116 117 if (!object_is_registered_) 118 return; 119 120 bus_->UnregisterObjectPath(object_path_); 121 object_is_registered_ = false; 122 } 123 124 void ExportedObject::ExportMethodInternal( 125 const std::string& interface_name, 126 const std::string& method_name, 127 MethodCallCallback method_call_callback, 128 OnExportedCallback on_exported_calback) { 129 bus_->AssertOnDBusThread(); 130 131 const bool success = ExportMethodAndBlock(interface_name, 132 method_name, 133 method_call_callback); 134 bus_->GetOriginTaskRunner()->PostTask(FROM_HERE, 135 base::Bind(&ExportedObject::OnExported, 136 this, 137 on_exported_calback, 138 interface_name, 139 method_name, 140 success)); 141 } 142 143 void ExportedObject::OnExported(OnExportedCallback on_exported_callback, 144 const std::string& interface_name, 145 const std::string& method_name, 146 bool success) { 147 bus_->AssertOnOriginThread(); 148 149 on_exported_callback.Run(interface_name, method_name, success); 150 } 151 152 void ExportedObject::SendSignalInternal(base::TimeTicks start_time, 153 DBusMessage* signal_message) { 154 uint32_t serial = 0; 155 bus_->Send(signal_message, &serial); 156 dbus_message_unref(signal_message); 157 // Record time spent to send the the signal. This is not accurate as the 158 // signal will actually be sent from the next run of the message loop, 159 // but we can at least tell the number of signals sent. 160 UMA_HISTOGRAM_TIMES("DBus.SignalSendTime", 161 base::TimeTicks::Now() - start_time); 162 } 163 164 bool ExportedObject::Register() { 165 bus_->AssertOnDBusThread(); 166 167 if (object_is_registered_) 168 return true; 169 170 ScopedDBusError error; 171 172 DBusObjectPathVTable vtable; 173 memset(&vtable, 0, sizeof(vtable)); 174 vtable.message_function = &ExportedObject::HandleMessageThunk; 175 vtable.unregister_function = &ExportedObject::OnUnregisteredThunk; 176 const bool success = bus_->TryRegisterObjectPath(object_path_, 177 &vtable, 178 this, 179 error.get()); 180 if (!success) { 181 LOG(ERROR) << "Failed to register the object: " << object_path_.value() 182 << ": " << (error.is_set() ? error.message() : ""); 183 return false; 184 } 185 186 object_is_registered_ = true; 187 return true; 188 } 189 190 DBusHandlerResult ExportedObject::HandleMessage( 191 DBusConnection* /* connection */, 192 DBusMessage* raw_message) { 193 bus_->AssertOnDBusThread(); 194 DCHECK_EQ(DBUS_MESSAGE_TYPE_METHOD_CALL, dbus_message_get_type(raw_message)); 195 196 // raw_message will be unrefed on exit of the function. Increment the 197 // reference so we can use it in MethodCall. 198 dbus_message_ref(raw_message); 199 scoped_ptr<MethodCall> method_call( 200 MethodCall::FromRawMessage(raw_message)); 201 const std::string interface = method_call->GetInterface(); 202 const std::string member = method_call->GetMember(); 203 204 if (interface.empty()) { 205 // We don't support method calls without interface. 206 LOG(WARNING) << "Interface is missing: " << method_call->ToString(); 207 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 208 } 209 210 // Check if we know about the method. 211 const std::string absolute_method_name = GetAbsoluteMemberName( 212 interface, member); 213 MethodTable::const_iterator iter = method_table_.find(absolute_method_name); 214 if (iter == method_table_.end()) { 215 // Don't know about the method. 216 LOG(WARNING) << "Unknown method: " << method_call->ToString(); 217 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 218 } 219 220 const base::TimeTicks start_time = base::TimeTicks::Now(); 221 if (bus_->HasDBusThread()) { 222 // Post a task to run the method in the origin thread. 223 bus_->GetOriginTaskRunner()->PostTask(FROM_HERE, 224 base::Bind(&ExportedObject::RunMethod, 225 this, 226 iter->second, 227 base::Passed(&method_call), 228 start_time)); 229 } else { 230 // If the D-Bus thread is not used, just call the method directly. 231 MethodCall* method = method_call.get(); 232 iter->second.Run(method, 233 base::Bind(&ExportedObject::SendResponse, 234 this, 235 start_time, 236 base::Passed(&method_call))); 237 } 238 239 // It's valid to say HANDLED here, and send a method response at a later 240 // time from OnMethodCompleted() asynchronously. 241 return DBUS_HANDLER_RESULT_HANDLED; 242 } 243 244 void ExportedObject::RunMethod(MethodCallCallback method_call_callback, 245 scoped_ptr<MethodCall> method_call, 246 base::TimeTicks start_time) { 247 bus_->AssertOnOriginThread(); 248 MethodCall* method = method_call.get(); 249 method_call_callback.Run(method, 250 base::Bind(&ExportedObject::SendResponse, 251 this, 252 start_time, 253 base::Passed(&method_call))); 254 } 255 256 void ExportedObject::SendResponse(base::TimeTicks start_time, 257 scoped_ptr<MethodCall> method_call, 258 scoped_ptr<Response> response) { 259 DCHECK(method_call); 260 if (bus_->HasDBusThread()) { 261 bus_->GetDBusTaskRunner()->PostTask( 262 FROM_HERE, 263 base::Bind(&ExportedObject::OnMethodCompleted, 264 this, 265 base::Passed(&method_call), 266 base::Passed(&response), 267 start_time)); 268 } else { 269 OnMethodCompleted(std::move(method_call), std::move(response), start_time); 270 } 271 } 272 273 void ExportedObject::OnMethodCompleted(scoped_ptr<MethodCall> method_call, 274 scoped_ptr<Response> response, 275 base::TimeTicks start_time) { 276 bus_->AssertOnDBusThread(); 277 278 // Record if the method call is successful, or not. 1 if successful. 279 UMA_HISTOGRAM_ENUMERATION("DBus.ExportedMethodHandleSuccess", 280 response ? 1 : 0, 281 kSuccessRatioHistogramMaxValue); 282 283 // Check if the bus is still connected. If the method takes long to 284 // complete, the bus may be shut down meanwhile. 285 if (!bus_->is_connected()) 286 return; 287 288 if (!response) { 289 // Something bad happened in the method call. 290 scoped_ptr<ErrorResponse> error_response( 291 ErrorResponse::FromMethodCall( 292 method_call.get(), 293 DBUS_ERROR_FAILED, 294 "error occurred in " + method_call->GetMember())); 295 bus_->Send(error_response->raw_message(), NULL); 296 return; 297 } 298 299 // The method call was successful. 300 bus_->Send(response->raw_message(), NULL); 301 302 // Record time spent to handle the the method call. Don't include failures. 303 UMA_HISTOGRAM_TIMES("DBus.ExportedMethodHandleTime", 304 base::TimeTicks::Now() - start_time); 305 } 306 307 void ExportedObject::OnUnregistered(DBusConnection* /* connection */) { 308 } 309 310 DBusHandlerResult ExportedObject::HandleMessageThunk( 311 DBusConnection* connection, 312 DBusMessage* raw_message, 313 void* user_data) { 314 ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data); 315 return self->HandleMessage(connection, raw_message); 316 } 317 318 void ExportedObject::OnUnregisteredThunk(DBusConnection *connection, 319 void* user_data) { 320 ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data); 321 return self->OnUnregistered(connection); 322 } 323 324 } // namespace dbus 325