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