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 "chromeos/dbus/shill_client_helper.h" 6 7 #include "base/bind.h" 8 #include "base/callback_helpers.h" 9 #include "base/values.h" 10 #include "dbus/message.h" 11 #include "dbus/object_proxy.h" 12 #include "dbus/values_util.h" 13 #include "third_party/cros_system_api/dbus/service_constants.h" 14 15 namespace chromeos { 16 17 // Class to hold onto a reference to a ShillClientHelper. This calss 18 // is owned by callbacks and released once the callback completes. 19 // Note: Only success callbacks hold the reference. If an error callback is 20 // invoked instead, the success callback will still be destroyed and the 21 // RefHolder with it, once the callback chain completes. 22 class ShillClientHelper::RefHolder { 23 public: 24 explicit RefHolder(base::WeakPtr<ShillClientHelper> helper) 25 : helper_(helper) { 26 helper_->AddRef(); 27 } 28 ~RefHolder() { 29 if (helper_) 30 helper_->Release(); 31 } 32 33 private: 34 base::WeakPtr<ShillClientHelper> helper_; 35 }; 36 37 namespace { 38 39 const char kInvalidResponseErrorName[] = ""; // No error name. 40 const char kInvalidResponseErrorMessage[] = "Invalid response."; 41 42 // Note: here and below, |ref_holder| is unused in the function body. It only 43 // exists so that it will be destroyed (and the reference released) with the 44 // Callback object once completed. 45 void OnBooleanMethodWithErrorCallback( 46 ShillClientHelper::RefHolder* ref_holder, 47 const ShillClientHelper::BooleanCallback& callback, 48 const ShillClientHelper::ErrorCallback& error_callback, 49 dbus::Response* response) { 50 if (!response) { 51 error_callback.Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage); 52 return; 53 } 54 dbus::MessageReader reader(response); 55 bool result; 56 if (!reader.PopBool(&result)) { 57 error_callback.Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage); 58 return; 59 } 60 callback.Run(result); 61 } 62 63 void OnStringMethodWithErrorCallback( 64 ShillClientHelper::RefHolder* ref_holder, 65 const ShillClientHelper::StringCallback& callback, 66 const ShillClientHelper::ErrorCallback& error_callback, 67 dbus::Response* response) { 68 if (!response) { 69 error_callback.Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage); 70 return; 71 } 72 dbus::MessageReader reader(response); 73 std::string result; 74 if (!reader.PopString(&result)) { 75 error_callback.Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage); 76 return; 77 } 78 callback.Run(result); 79 } 80 81 // Handles responses for methods without results. 82 void OnVoidMethod(ShillClientHelper::RefHolder* ref_holder, 83 const VoidDBusMethodCallback& callback, 84 dbus::Response* response) { 85 if (!response) { 86 callback.Run(DBUS_METHOD_CALL_FAILURE); 87 return; 88 } 89 callback.Run(DBUS_METHOD_CALL_SUCCESS); 90 } 91 92 // Handles responses for methods with ObjectPath results. 93 void OnObjectPathMethod( 94 ShillClientHelper::RefHolder* ref_holder, 95 const ObjectPathDBusMethodCallback& callback, 96 dbus::Response* response) { 97 if (!response) { 98 callback.Run(DBUS_METHOD_CALL_FAILURE, dbus::ObjectPath()); 99 return; 100 } 101 dbus::MessageReader reader(response); 102 dbus::ObjectPath result; 103 if (!reader.PopObjectPath(&result)) { 104 callback.Run(DBUS_METHOD_CALL_FAILURE, dbus::ObjectPath()); 105 return; 106 } 107 callback.Run(DBUS_METHOD_CALL_SUCCESS, result); 108 } 109 110 // Handles responses for methods with ObjectPath results and no status. 111 void OnObjectPathMethodWithoutStatus( 112 ShillClientHelper::RefHolder* ref_holder, 113 const ObjectPathCallback& callback, 114 const ShillClientHelper::ErrorCallback& error_callback, 115 dbus::Response* response) { 116 if (!response) { 117 error_callback.Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage); 118 return; 119 } 120 dbus::MessageReader reader(response); 121 dbus::ObjectPath result; 122 if (!reader.PopObjectPath(&result)) { 123 error_callback.Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage); 124 return; 125 } 126 callback.Run(result); 127 } 128 129 // Handles responses for methods with DictionaryValue results. 130 void OnDictionaryValueMethod( 131 ShillClientHelper::RefHolder* ref_holder, 132 const ShillClientHelper::DictionaryValueCallback& callback, 133 dbus::Response* response) { 134 if (!response) { 135 base::DictionaryValue result; 136 callback.Run(DBUS_METHOD_CALL_FAILURE, result); 137 return; 138 } 139 dbus::MessageReader reader(response); 140 scoped_ptr<base::Value> value(dbus::PopDataAsValue(&reader)); 141 base::DictionaryValue* result = NULL; 142 if (!value.get() || !value->GetAsDictionary(&result)) { 143 base::DictionaryValue result; 144 callback.Run(DBUS_METHOD_CALL_FAILURE, result); 145 return; 146 } 147 callback.Run(DBUS_METHOD_CALL_SUCCESS, *result); 148 } 149 150 // Handles responses for methods without results. 151 void OnVoidMethodWithErrorCallback( 152 ShillClientHelper::RefHolder* ref_holder, 153 const base::Closure& callback, 154 dbus::Response* response) { 155 callback.Run(); 156 } 157 158 // Handles responses for methods with DictionaryValue results. 159 // Used by CallDictionaryValueMethodWithErrorCallback(). 160 void OnDictionaryValueMethodWithErrorCallback( 161 ShillClientHelper::RefHolder* ref_holder, 162 const ShillClientHelper::DictionaryValueCallbackWithoutStatus& callback, 163 const ShillClientHelper::ErrorCallback& error_callback, 164 dbus::Response* response) { 165 dbus::MessageReader reader(response); 166 scoped_ptr<base::Value> value(dbus::PopDataAsValue(&reader)); 167 base::DictionaryValue* result = NULL; 168 if (!value.get() || !value->GetAsDictionary(&result)) { 169 error_callback.Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage); 170 return; 171 } 172 callback.Run(*result); 173 } 174 175 // Handles responses for methods with ListValue results. 176 void OnListValueMethodWithErrorCallback( 177 ShillClientHelper::RefHolder* ref_holder, 178 const ShillClientHelper::ListValueCallback& callback, 179 const ShillClientHelper::ErrorCallback& error_callback, 180 dbus::Response* response) { 181 dbus::MessageReader reader(response); 182 scoped_ptr<base::Value> value(dbus::PopDataAsValue(&reader)); 183 base::ListValue* result = NULL; 184 if (!value.get() || !value->GetAsList(&result)) { 185 error_callback.Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage); 186 return; 187 } 188 callback.Run(*result); 189 } 190 191 // Handles running appropriate error callbacks. 192 void OnError(const ShillClientHelper::ErrorCallback& error_callback, 193 dbus::ErrorResponse* response) { 194 std::string error_name; 195 std::string error_message; 196 if (response) { 197 // Error message may contain the error message as string. 198 dbus::MessageReader reader(response); 199 error_name = response->GetErrorName(); 200 reader.PopString(&error_message); 201 } 202 error_callback.Run(error_name, error_message); 203 } 204 205 } // namespace 206 207 ShillClientHelper::ShillClientHelper(dbus::ObjectProxy* proxy) 208 : proxy_(proxy), 209 active_refs_(0), 210 weak_ptr_factory_(this) { 211 } 212 213 ShillClientHelper::~ShillClientHelper() { 214 LOG_IF(ERROR, observer_list_.might_have_observers()) 215 << "ShillClientHelper destroyed with active observers"; 216 } 217 218 void ShillClientHelper::SetReleasedCallback(ReleasedCallback callback) { 219 CHECK(released_callback_.is_null()); 220 released_callback_ = callback; 221 } 222 223 void ShillClientHelper::AddPropertyChangedObserver( 224 ShillPropertyChangedObserver* observer) { 225 if (observer_list_.HasObserver(observer)) 226 return; 227 AddRef(); 228 // Excecute all the pending MonitorPropertyChanged calls. 229 for (size_t i = 0; i < interfaces_to_be_monitored_.size(); ++i) { 230 MonitorPropertyChangedInternal(interfaces_to_be_monitored_[i]); 231 } 232 interfaces_to_be_monitored_.clear(); 233 234 observer_list_.AddObserver(observer); 235 } 236 237 void ShillClientHelper::RemovePropertyChangedObserver( 238 ShillPropertyChangedObserver* observer) { 239 if (!observer_list_.HasObserver(observer)) 240 return; 241 observer_list_.RemoveObserver(observer); 242 Release(); 243 } 244 245 void ShillClientHelper::MonitorPropertyChanged( 246 const std::string& interface_name) { 247 if (observer_list_.might_have_observers()) { 248 // Effectively monitor the PropertyChanged now. 249 MonitorPropertyChangedInternal(interface_name); 250 } else { 251 // Delay the ConnectToSignal until an observer is added. 252 interfaces_to_be_monitored_.push_back(interface_name); 253 } 254 } 255 256 void ShillClientHelper::MonitorPropertyChangedInternal( 257 const std::string& interface_name) { 258 // We are not using dbus::PropertySet to monitor PropertyChanged signal 259 // because the interface is not "org.freedesktop.DBus.Properties". 260 proxy_->ConnectToSignal(interface_name, 261 shill::kMonitorPropertyChanged, 262 base::Bind(&ShillClientHelper::OnPropertyChanged, 263 weak_ptr_factory_.GetWeakPtr()), 264 base::Bind(&ShillClientHelper::OnSignalConnected, 265 weak_ptr_factory_.GetWeakPtr())); 266 } 267 268 void ShillClientHelper::CallVoidMethod( 269 dbus::MethodCall* method_call, 270 const VoidDBusMethodCallback& callback) { 271 DCHECK(!callback.is_null()); 272 proxy_->CallMethod( 273 method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 274 base::Bind(&OnVoidMethod, 275 base::Owned(new RefHolder(weak_ptr_factory_.GetWeakPtr())), 276 callback)); 277 } 278 279 void ShillClientHelper::CallObjectPathMethod( 280 dbus::MethodCall* method_call, 281 const ObjectPathDBusMethodCallback& callback) { 282 DCHECK(!callback.is_null()); 283 proxy_->CallMethod( 284 method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 285 base::Bind(&OnObjectPathMethod, 286 base::Owned(new RefHolder(weak_ptr_factory_.GetWeakPtr())), 287 callback)); 288 } 289 290 void ShillClientHelper::CallObjectPathMethodWithErrorCallback( 291 dbus::MethodCall* method_call, 292 const ObjectPathCallback& callback, 293 const ErrorCallback& error_callback) { 294 DCHECK(!callback.is_null()); 295 DCHECK(!error_callback.is_null()); 296 proxy_->CallMethodWithErrorCallback( 297 method_call, 298 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 299 base::Bind(&OnObjectPathMethodWithoutStatus, 300 base::Owned(new RefHolder(weak_ptr_factory_.GetWeakPtr())), 301 callback, 302 error_callback), 303 base::Bind(&OnError, 304 error_callback)); 305 } 306 307 void ShillClientHelper::CallDictionaryValueMethod( 308 dbus::MethodCall* method_call, 309 const DictionaryValueCallback& callback) { 310 DCHECK(!callback.is_null()); 311 proxy_->CallMethod( 312 method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 313 base::Bind(&OnDictionaryValueMethod, 314 base::Owned(new RefHolder(weak_ptr_factory_.GetWeakPtr())), 315 callback)); 316 } 317 318 void ShillClientHelper::CallVoidMethodWithErrorCallback( 319 dbus::MethodCall* method_call, 320 const base::Closure& callback, 321 const ErrorCallback& error_callback) { 322 DCHECK(!callback.is_null()); 323 DCHECK(!error_callback.is_null()); 324 proxy_->CallMethodWithErrorCallback( 325 method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 326 base::Bind(&OnVoidMethodWithErrorCallback, 327 base::Owned(new RefHolder(weak_ptr_factory_.GetWeakPtr())), 328 callback), 329 base::Bind(&OnError, 330 error_callback)); 331 } 332 333 void ShillClientHelper::CallBooleanMethodWithErrorCallback( 334 dbus::MethodCall* method_call, 335 const BooleanCallback& callback, 336 const ErrorCallback& error_callback) { 337 DCHECK(!callback.is_null()); 338 DCHECK(!error_callback.is_null()); 339 proxy_->CallMethodWithErrorCallback( 340 method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 341 base::Bind(&OnBooleanMethodWithErrorCallback, 342 base::Owned(new RefHolder(weak_ptr_factory_.GetWeakPtr())), 343 callback, 344 error_callback), 345 base::Bind(&OnError, 346 error_callback)); 347 } 348 349 void ShillClientHelper::CallStringMethodWithErrorCallback( 350 dbus::MethodCall* method_call, 351 const StringCallback& callback, 352 const ErrorCallback& error_callback) { 353 DCHECK(!callback.is_null()); 354 DCHECK(!error_callback.is_null()); 355 proxy_->CallMethodWithErrorCallback( 356 method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 357 base::Bind(&OnStringMethodWithErrorCallback, 358 base::Owned(new RefHolder(weak_ptr_factory_.GetWeakPtr())), 359 callback, 360 error_callback), 361 base::Bind(&OnError, 362 error_callback)); 363 } 364 365 void ShillClientHelper::CallDictionaryValueMethodWithErrorCallback( 366 dbus::MethodCall* method_call, 367 const DictionaryValueCallbackWithoutStatus& callback, 368 const ErrorCallback& error_callback) { 369 DCHECK(!callback.is_null()); 370 DCHECK(!error_callback.is_null()); 371 proxy_->CallMethodWithErrorCallback( 372 method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 373 base::Bind(&OnDictionaryValueMethodWithErrorCallback, 374 base::Owned(new RefHolder(weak_ptr_factory_.GetWeakPtr())), 375 callback, 376 error_callback), 377 base::Bind(&OnError, 378 error_callback)); 379 } 380 381 void ShillClientHelper::CallListValueMethodWithErrorCallback( 382 dbus::MethodCall* method_call, 383 const ListValueCallback& callback, 384 const ErrorCallback& error_callback) { 385 DCHECK(!callback.is_null()); 386 DCHECK(!error_callback.is_null()); 387 proxy_->CallMethodWithErrorCallback( 388 method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 389 base::Bind(&OnListValueMethodWithErrorCallback, 390 base::Owned(new RefHolder(weak_ptr_factory_.GetWeakPtr())), 391 callback, 392 error_callback), 393 base::Bind(&OnError, 394 error_callback)); 395 } 396 397 // static 398 void ShillClientHelper::AppendValueDataAsVariant(dbus::MessageWriter* writer, 399 const base::Value& value) { 400 // Support basic types and string-to-string dictionary. 401 switch (value.GetType()) { 402 case base::Value::TYPE_DICTIONARY: { 403 const base::DictionaryValue* dictionary = NULL; 404 value.GetAsDictionary(&dictionary); 405 dbus::MessageWriter variant_writer(NULL); 406 writer->OpenVariant("a{ss}", &variant_writer); 407 dbus::MessageWriter array_writer(NULL); 408 variant_writer.OpenArray("{ss}", &array_writer); 409 for (base::DictionaryValue::Iterator it(*dictionary); 410 !it.IsAtEnd(); 411 it.Advance()) { 412 dbus::MessageWriter entry_writer(NULL); 413 array_writer.OpenDictEntry(&entry_writer); 414 entry_writer.AppendString(it.key()); 415 const base::Value& value = it.value(); 416 std::string value_string; 417 DLOG_IF(ERROR, value.GetType() != base::Value::TYPE_STRING) 418 << "Unexpected type " << value.GetType(); 419 value.GetAsString(&value_string); 420 entry_writer.AppendString(value_string); 421 array_writer.CloseContainer(&entry_writer); 422 } 423 variant_writer.CloseContainer(&array_writer); 424 writer->CloseContainer(&variant_writer); 425 break; 426 } 427 case base::Value::TYPE_LIST: { 428 const base::ListValue* list = NULL; 429 value.GetAsList(&list); 430 dbus::MessageWriter variant_writer(NULL); 431 writer->OpenVariant("as", &variant_writer); 432 dbus::MessageWriter array_writer(NULL); 433 variant_writer.OpenArray("s", &array_writer); 434 for (base::ListValue::const_iterator it = list->begin(); 435 it != list->end(); ++it) { 436 const base::Value& value = **it; 437 LOG_IF(ERROR, value.GetType() != base::Value::TYPE_STRING) 438 << "Unexpected type " << value.GetType(); 439 std::string value_string; 440 value.GetAsString(&value_string); 441 array_writer.AppendString(value_string); 442 } 443 variant_writer.CloseContainer(&array_writer); 444 writer->CloseContainer(&variant_writer); 445 break; 446 } 447 case base::Value::TYPE_BOOLEAN: 448 case base::Value::TYPE_INTEGER: 449 case base::Value::TYPE_DOUBLE: 450 case base::Value::TYPE_STRING: 451 dbus::AppendBasicTypeValueDataAsVariant(writer, value); 452 break; 453 default: 454 DLOG(ERROR) << "Unexpected type " << value.GetType(); 455 } 456 457 } 458 459 // static 460 void ShillClientHelper::AppendServicePropertiesDictionary( 461 dbus::MessageWriter* writer, 462 const base::DictionaryValue& dictionary) { 463 dbus::MessageWriter array_writer(NULL); 464 writer->OpenArray("{sv}", &array_writer); 465 for (base::DictionaryValue::Iterator it(dictionary); 466 !it.IsAtEnd(); 467 it.Advance()) { 468 dbus::MessageWriter entry_writer(NULL); 469 array_writer.OpenDictEntry(&entry_writer); 470 entry_writer.AppendString(it.key()); 471 ShillClientHelper::AppendValueDataAsVariant(&entry_writer, it.value()); 472 array_writer.CloseContainer(&entry_writer); 473 } 474 writer->CloseContainer(&array_writer); 475 } 476 477 void ShillClientHelper::AddRef() { 478 ++active_refs_; 479 } 480 481 void ShillClientHelper::Release() { 482 --active_refs_; 483 if (active_refs_ == 0 && !released_callback_.is_null()) 484 base::ResetAndReturn(&released_callback_).Run(this); // May delete this 485 } 486 487 void ShillClientHelper::OnSignalConnected(const std::string& interface, 488 const std::string& signal, 489 bool success) { 490 LOG_IF(ERROR, !success) << "Connect to " << interface << " " << signal 491 << " failed."; 492 } 493 494 void ShillClientHelper::OnPropertyChanged(dbus::Signal* signal) { 495 if (!observer_list_.might_have_observers()) 496 return; 497 498 dbus::MessageReader reader(signal); 499 std::string name; 500 if (!reader.PopString(&name)) 501 return; 502 scoped_ptr<base::Value> value(dbus::PopDataAsValue(&reader)); 503 if (!value.get()) 504 return; 505 506 FOR_EACH_OBSERVER(ShillPropertyChangedObserver, observer_list_, 507 OnPropertyChanged(name, *value)); 508 } 509 510 } // namespace chromeos 511