• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
RefHolder(base::WeakPtr<ShillClientHelper> helper)24   explicit RefHolder(base::WeakPtr<ShillClientHelper> helper)
25       : helper_(helper) {
26     helper_->AddRef();
27   }
~RefHolder()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.
OnBooleanMethodWithErrorCallback(ShillClientHelper::RefHolder * ref_holder,const ShillClientHelper::BooleanCallback & callback,const ShillClientHelper::ErrorCallback & error_callback,dbus::Response * response)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 
OnStringMethodWithErrorCallback(ShillClientHelper::RefHolder * ref_holder,const ShillClientHelper::StringCallback & callback,const ShillClientHelper::ErrorCallback & error_callback,dbus::Response * response)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.
OnVoidMethod(ShillClientHelper::RefHolder * ref_holder,const VoidDBusMethodCallback & callback,dbus::Response * response)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.
OnObjectPathMethod(ShillClientHelper::RefHolder * ref_holder,const ObjectPathDBusMethodCallback & callback,dbus::Response * response)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.
OnObjectPathMethodWithoutStatus(ShillClientHelper::RefHolder * ref_holder,const ObjectPathCallback & callback,const ShillClientHelper::ErrorCallback & error_callback,dbus::Response * response)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.
OnDictionaryValueMethod(ShillClientHelper::RefHolder * ref_holder,const ShillClientHelper::DictionaryValueCallback & callback,dbus::Response * response)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.
OnVoidMethodWithErrorCallback(ShillClientHelper::RefHolder * ref_holder,const base::Closure & callback,dbus::Response * response)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().
OnDictionaryValueMethodWithErrorCallback(ShillClientHelper::RefHolder * ref_holder,const ShillClientHelper::DictionaryValueCallbackWithoutStatus & callback,const ShillClientHelper::ErrorCallback & error_callback,dbus::Response * response)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.
OnListValueMethodWithErrorCallback(ShillClientHelper::RefHolder * ref_holder,const ShillClientHelper::ListValueCallback & callback,const ShillClientHelper::ErrorCallback & error_callback,dbus::Response * response)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.
OnError(const ShillClientHelper::ErrorCallback & error_callback,dbus::ErrorResponse * response)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 
ShillClientHelper(dbus::ObjectProxy * proxy)207 ShillClientHelper::ShillClientHelper(dbus::ObjectProxy* proxy)
208     : proxy_(proxy),
209       active_refs_(0),
210       weak_ptr_factory_(this) {
211 }
212 
~ShillClientHelper()213 ShillClientHelper::~ShillClientHelper() {
214   LOG_IF(ERROR, observer_list_.might_have_observers())
215       << "ShillClientHelper destroyed with active observers";
216 }
217 
SetReleasedCallback(ReleasedCallback callback)218 void ShillClientHelper::SetReleasedCallback(ReleasedCallback callback) {
219   CHECK(released_callback_.is_null());
220   released_callback_ = callback;
221 }
222 
AddPropertyChangedObserver(ShillPropertyChangedObserver * observer)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 
RemovePropertyChangedObserver(ShillPropertyChangedObserver * observer)237 void ShillClientHelper::RemovePropertyChangedObserver(
238     ShillPropertyChangedObserver* observer) {
239   if (!observer_list_.HasObserver(observer))
240     return;
241   observer_list_.RemoveObserver(observer);
242   Release();
243 }
244 
MonitorPropertyChanged(const std::string & interface_name)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 
MonitorPropertyChangedInternal(const std::string & interface_name)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 
CallVoidMethod(dbus::MethodCall * method_call,const VoidDBusMethodCallback & callback)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 
CallObjectPathMethod(dbus::MethodCall * method_call,const ObjectPathDBusMethodCallback & callback)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 
CallObjectPathMethodWithErrorCallback(dbus::MethodCall * method_call,const ObjectPathCallback & callback,const ErrorCallback & error_callback)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 
CallDictionaryValueMethod(dbus::MethodCall * method_call,const DictionaryValueCallback & callback)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 
CallVoidMethodWithErrorCallback(dbus::MethodCall * method_call,const base::Closure & callback,const ErrorCallback & error_callback)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 
CallBooleanMethodWithErrorCallback(dbus::MethodCall * method_call,const BooleanCallback & callback,const ErrorCallback & error_callback)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 
CallStringMethodWithErrorCallback(dbus::MethodCall * method_call,const StringCallback & callback,const ErrorCallback & error_callback)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 
CallDictionaryValueMethodWithErrorCallback(dbus::MethodCall * method_call,const DictionaryValueCallbackWithoutStatus & callback,const ErrorCallback & error_callback)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 
CallListValueMethodWithErrorCallback(dbus::MethodCall * method_call,const ListValueCallback & callback,const ErrorCallback & error_callback)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
AppendValueDataAsVariant(dbus::MessageWriter * writer,const base::Value & value)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
AppendServicePropertiesDictionary(dbus::MessageWriter * writer,const base::DictionaryValue & dictionary)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 
AddRef()477 void ShillClientHelper::AddRef() {
478   ++active_refs_;
479 }
480 
Release()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 
OnSignalConnected(const std::string & interface,const std::string & signal,bool success)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 
OnPropertyChanged(dbus::Signal * signal)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