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_service_client.h"
6
7#include "base/bind.h"
8#include "base/memory/weak_ptr.h"
9#include "base/message_loop/message_loop.h"
10#include "base/stl_util.h"
11#include "base/values.h"
12#include "chromeos/dbus/shill_property_changed_observer.h"
13#include "chromeos/network/network_event_log.h"
14#include "dbus/bus.h"
15#include "dbus/message.h"
16#include "dbus/object_proxy.h"
17#include "third_party/cros_system_api/dbus/service_constants.h"
18
19namespace chromeos {
20
21namespace {
22
23#ifndef DBUS_ERROR_UNKNOWN_OBJECT
24// The linux_chromeos ASAN builder has an older version of dbus-protocol.h
25// so make sure this is defined.
26#define DBUS_ERROR_UNKNOWN_OBJECT "org.freedesktop.DBus.Error.UnknownObject"
27#endif
28
29// Error callback for GetProperties.
30void OnGetDictionaryError(
31    const std::string& method_name,
32    const dbus::ObjectPath& service_path,
33    const ShillServiceClient::DictionaryValueCallback& callback,
34    const std::string& error_name,
35    const std::string& error_message) {
36  const std::string log_string =
37      "Failed to call org.chromium.shill.Service." + method_name +
38      " for: " + service_path.value() + ": " +
39      error_name + ": " + error_message;
40
41  // Suppress ERROR messages for UnknownMethod/Object" since this can
42  // happen under normal conditions. See crbug.com/130660 and crbug.com/222210.
43  if (error_name == DBUS_ERROR_UNKNOWN_METHOD ||
44      error_name == DBUS_ERROR_UNKNOWN_OBJECT)
45    VLOG(1) << log_string;
46  else
47    LOG(ERROR) << log_string;
48
49  base::DictionaryValue empty_dictionary;
50  callback.Run(DBUS_METHOD_CALL_FAILURE, empty_dictionary);
51}
52
53// The ShillServiceClient implementation.
54class ShillServiceClientImpl : public ShillServiceClient {
55 public:
56  explicit ShillServiceClientImpl()
57      : bus_(NULL),
58        weak_ptr_factory_(this) {
59  }
60
61  virtual ~ShillServiceClientImpl() {
62    for (HelperMap::iterator iter = helpers_.begin();
63         iter != helpers_.end(); ++iter) {
64      ShillClientHelper* helper = iter->second;
65      bus_->RemoveObjectProxy(shill::kFlimflamServiceName,
66                              helper->object_proxy()->object_path(),
67                              base::Bind(&base::DoNothing));
68      delete helper;
69    }
70  }
71
72  virtual void AddPropertyChangedObserver(
73      const dbus::ObjectPath& service_path,
74      ShillPropertyChangedObserver* observer) OVERRIDE {
75    GetHelper(service_path)->AddPropertyChangedObserver(observer);
76  }
77
78  virtual void RemovePropertyChangedObserver(
79      const dbus::ObjectPath& service_path,
80      ShillPropertyChangedObserver* observer) OVERRIDE {
81    GetHelper(service_path)->RemovePropertyChangedObserver(observer);
82  }
83
84  virtual void GetProperties(const dbus::ObjectPath& service_path,
85                             const DictionaryValueCallback& callback) OVERRIDE {
86    dbus::MethodCall method_call(shill::kFlimflamServiceInterface,
87                                 shill::kGetPropertiesFunction);
88    GetHelper(service_path)->CallDictionaryValueMethodWithErrorCallback(
89        &method_call,
90        base::Bind(callback, DBUS_METHOD_CALL_SUCCESS),
91        base::Bind(&OnGetDictionaryError, "GetProperties",
92                   service_path, callback));
93  }
94
95  virtual void SetProperty(const dbus::ObjectPath& service_path,
96                           const std::string& name,
97                           const base::Value& value,
98                           const base::Closure& callback,
99                           const ErrorCallback& error_callback) OVERRIDE {
100    dbus::MethodCall method_call(shill::kFlimflamServiceInterface,
101                                 shill::kSetPropertyFunction);
102    dbus::MessageWriter writer(&method_call);
103    writer.AppendString(name);
104    ShillClientHelper::AppendValueDataAsVariant(&writer, value);
105    GetHelper(service_path)->CallVoidMethodWithErrorCallback(&method_call,
106                                                             callback,
107                                                             error_callback);
108  }
109
110  virtual void SetProperties(const dbus::ObjectPath& service_path,
111                             const base::DictionaryValue& properties,
112                             const base::Closure& callback,
113                             const ErrorCallback& error_callback) OVERRIDE {
114    dbus::MethodCall method_call(shill::kFlimflamServiceInterface,
115                                 shill::kSetPropertiesFunction);
116    dbus::MessageWriter writer(&method_call);
117    ShillClientHelper::AppendServicePropertiesDictionary(&writer, properties);
118    GetHelper(service_path)->CallVoidMethodWithErrorCallback(&method_call,
119                                                             callback,
120                                                             error_callback);
121  }
122
123  virtual void ClearProperty(const dbus::ObjectPath& service_path,
124                             const std::string& name,
125                             const base::Closure& callback,
126                             const ErrorCallback& error_callback) OVERRIDE {
127    dbus::MethodCall method_call(shill::kFlimflamServiceInterface,
128                                 shill::kClearPropertyFunction);
129    dbus::MessageWriter writer(&method_call);
130    writer.AppendString(name);
131    GetHelper(service_path)->CallVoidMethodWithErrorCallback(&method_call,
132                                                             callback,
133                                                             error_callback);
134  }
135
136
137  virtual void ClearProperties(const dbus::ObjectPath& service_path,
138                               const std::vector<std::string>& names,
139                               const ListValueCallback& callback,
140                               const ErrorCallback& error_callback) OVERRIDE {
141    dbus::MethodCall method_call(shill::kFlimflamServiceInterface,
142                                 shill::kClearPropertiesFunction);
143    dbus::MessageWriter writer(&method_call);
144    writer.AppendArrayOfStrings(names);
145    GetHelper(service_path)->CallListValueMethodWithErrorCallback(
146        &method_call,
147        callback,
148        error_callback);
149  }
150
151  virtual void Connect(const dbus::ObjectPath& service_path,
152                       const base::Closure& callback,
153                       const ErrorCallback& error_callback) OVERRIDE {
154    dbus::MethodCall method_call(shill::kFlimflamServiceInterface,
155                                 shill::kConnectFunction);
156    GetHelper(service_path)->CallVoidMethodWithErrorCallback(
157        &method_call, callback, error_callback);
158  }
159
160  virtual void Disconnect(const dbus::ObjectPath& service_path,
161                          const base::Closure& callback,
162                          const ErrorCallback& error_callback) OVERRIDE {
163    dbus::MethodCall method_call(shill::kFlimflamServiceInterface,
164                                 shill::kDisconnectFunction);
165    GetHelper(service_path)->CallVoidMethodWithErrorCallback(&method_call,
166                                                             callback,
167                                                             error_callback);
168  }
169
170  virtual void Remove(const dbus::ObjectPath& service_path,
171                      const base::Closure& callback,
172                      const ErrorCallback& error_callback) OVERRIDE {
173    dbus::MethodCall method_call(shill::kFlimflamServiceInterface,
174                                 shill::kRemoveServiceFunction);
175    GetHelper(service_path)->CallVoidMethodWithErrorCallback(&method_call,
176                                                             callback,
177                                                             error_callback);
178  }
179
180  virtual void ActivateCellularModem(
181      const dbus::ObjectPath& service_path,
182      const std::string& carrier,
183      const base::Closure& callback,
184      const ErrorCallback& error_callback) OVERRIDE {
185    dbus::MethodCall method_call(shill::kFlimflamServiceInterface,
186                                 shill::kActivateCellularModemFunction);
187    dbus::MessageWriter writer(&method_call);
188    writer.AppendString(carrier);
189    GetHelper(service_path)->CallVoidMethodWithErrorCallback(&method_call,
190                                                             callback,
191                                                             error_callback);
192  }
193
194  virtual void CompleteCellularActivation(
195      const dbus::ObjectPath& service_path,
196      const base::Closure& callback,
197      const ErrorCallback& error_callback) OVERRIDE {
198    dbus::MethodCall method_call(shill::kFlimflamServiceInterface,
199                                 shill::kCompleteCellularActivationFunction);
200    dbus::MessageWriter writer(&method_call);
201    GetHelper(service_path)->CallVoidMethodWithErrorCallback(&method_call,
202                                                             callback,
203                                                             error_callback);
204  }
205
206  virtual void GetLoadableProfileEntries(
207      const dbus::ObjectPath& service_path,
208      const DictionaryValueCallback& callback) OVERRIDE {
209    dbus::MethodCall method_call(shill::kFlimflamServiceInterface,
210                                 shill::kGetLoadableProfileEntriesFunction);
211    GetHelper(service_path)->CallDictionaryValueMethodWithErrorCallback(
212        &method_call,
213        base::Bind(callback, DBUS_METHOD_CALL_SUCCESS),
214        base::Bind(&OnGetDictionaryError, "GetLoadableProfileEntries",
215                   service_path, callback));
216  }
217
218  virtual ShillServiceClient::TestInterface* GetTestInterface() OVERRIDE {
219    return NULL;
220  }
221
222 protected:
223  virtual void Init(dbus::Bus* bus) OVERRIDE {
224    bus_ = bus;
225  }
226
227 private:
228  typedef std::map<std::string, ShillClientHelper*> HelperMap;
229
230  // Returns the corresponding ShillClientHelper for the profile.
231  ShillClientHelper* GetHelper(const dbus::ObjectPath& service_path) {
232    HelperMap::iterator it = helpers_.find(service_path.value());
233    if (it != helpers_.end())
234      return it->second;
235
236    // There is no helper for the profile, create it.
237    NET_LOG_DEBUG("AddShillClientHelper", service_path.value());
238    dbus::ObjectProxy* object_proxy =
239        bus_->GetObjectProxy(shill::kFlimflamServiceName, service_path);
240    ShillClientHelper* helper = new ShillClientHelper(object_proxy);
241    helper->SetReleasedCallback(
242        base::Bind(&ShillServiceClientImpl::NotifyReleased,
243                   weak_ptr_factory_.GetWeakPtr()));
244    helper->MonitorPropertyChanged(shill::kFlimflamServiceInterface);
245    helpers_.insert(HelperMap::value_type(service_path.value(), helper));
246    return helper;
247  }
248
249  void NotifyReleased(ShillClientHelper* helper) {
250    // New Shill Service DBus objects are created relatively frequently, so
251    // remove them when they become inactive (no observers and no active method
252    // calls).
253    dbus::ObjectPath object_path = helper->object_proxy()->object_path();
254    // Make sure we don't release the proxy used by ShillManagerClient ("/").
255    // This shouldn't ever happen, but might if a bug in the code requests
256    // a service with path "/", or a bug in Shill passes "/" as a service path.
257    // Either way this would cause an invalid memory access in
258    // ShillManagerClient, see crbug.com/324849.
259    if (object_path == dbus::ObjectPath(shill::kFlimflamServicePath)) {
260      NET_LOG_ERROR("ShillServiceClient service has invalid path",
261                    shill::kFlimflamServicePath);
262      return;
263    }
264    NET_LOG_DEBUG("RemoveShillClientHelper", object_path.value());
265    bus_->RemoveObjectProxy(shill::kFlimflamServiceName,
266                            object_path, base::Bind(&base::DoNothing));
267    helpers_.erase(object_path.value());
268    delete helper;
269  }
270
271  dbus::Bus* bus_;
272  HelperMap helpers_;
273  base::WeakPtrFactory<ShillServiceClientImpl> weak_ptr_factory_;
274
275  DISALLOW_COPY_AND_ASSIGN(ShillServiceClientImpl);
276};
277
278}  // namespace
279
280ShillServiceClient::ShillServiceClient() {}
281
282ShillServiceClient::~ShillServiceClient() {}
283
284// static
285ShillServiceClient* ShillServiceClient::Create() {
286  return new ShillServiceClientImpl();
287}
288
289}  // namespace chromeos
290