1// Copyright 2013 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/fake_shill_device_client.h"
6
7#include "base/bind.h"
8#include "base/message_loop/message_loop.h"
9#include "base/stl_util.h"
10#include "base/values.h"
11#include "chromeos/dbus/dbus_thread_manager.h"
12#include "chromeos/dbus/shill_manager_client.h"
13#include "chromeos/dbus/shill_property_changed_observer.h"
14#include "dbus/bus.h"
15#include "dbus/message.h"
16#include "dbus/object_path.h"
17#include "dbus/object_proxy.h"
18#include "dbus/values_util.h"
19#include "third_party/cros_system_api/dbus/service_constants.h"
20
21namespace chromeos {
22
23namespace {
24
25std::string kSimPin = "1111";
26
27void ErrorFunction(const std::string& device_path,
28                   const std::string& error_name,
29                   const std::string& error_message) {
30  LOG(ERROR) << "Shill Error for: " << device_path
31             << ": " << error_name << " : " << error_message;
32}
33
34void PostDeviceNotFoundError(
35    const ShillDeviceClient::ErrorCallback& error_callback) {
36  std::string error_message("Failed");
37  base::MessageLoop::current()->PostTask(
38      FROM_HERE,
39      base::Bind(error_callback, shill::kErrorResultNotFound, error_message));
40}
41
42}  // namespace
43
44FakeShillDeviceClient::FakeShillDeviceClient()
45    : tdls_busy_count_(0),
46      weak_ptr_factory_(this) {
47}
48
49FakeShillDeviceClient::~FakeShillDeviceClient() {
50  STLDeleteContainerPairSecondPointers(
51      observer_list_.begin(), observer_list_.end());
52}
53
54// ShillDeviceClient overrides.
55
56void FakeShillDeviceClient::Init(dbus::Bus* bus) {}
57
58void FakeShillDeviceClient::AddPropertyChangedObserver(
59    const dbus::ObjectPath& device_path,
60    ShillPropertyChangedObserver* observer) {
61  GetObserverList(device_path).AddObserver(observer);
62}
63
64void FakeShillDeviceClient::RemovePropertyChangedObserver(
65    const dbus::ObjectPath& device_path,
66    ShillPropertyChangedObserver* observer) {
67  GetObserverList(device_path).RemoveObserver(observer);
68}
69
70void FakeShillDeviceClient::GetProperties(
71    const dbus::ObjectPath& device_path,
72    const DictionaryValueCallback& callback) {
73  base::MessageLoop::current()->PostTask(
74      FROM_HERE,
75      base::Bind(&FakeShillDeviceClient::PassStubDeviceProperties,
76                 weak_ptr_factory_.GetWeakPtr(),
77                 device_path, callback));
78}
79
80void FakeShillDeviceClient::ProposeScan(
81    const dbus::ObjectPath& device_path,
82    const VoidDBusMethodCallback& callback) {
83  PostVoidCallback(callback, DBUS_METHOD_CALL_SUCCESS);
84}
85
86void FakeShillDeviceClient::SetProperty(const dbus::ObjectPath& device_path,
87                                        const std::string& name,
88                                        const base::Value& value,
89                                        const base::Closure& callback,
90                                        const ErrorCallback& error_callback) {
91  base::DictionaryValue* device_properties = NULL;
92  if (!stub_devices_.GetDictionaryWithoutPathExpansion(device_path.value(),
93                                                       &device_properties)) {
94    PostDeviceNotFoundError(error_callback);
95    return;
96  }
97  device_properties->SetWithoutPathExpansion(name, value.DeepCopy());
98  base::MessageLoop::current()->PostTask(
99      FROM_HERE,
100      base::Bind(&FakeShillDeviceClient::NotifyObserversPropertyChanged,
101                 weak_ptr_factory_.GetWeakPtr(), device_path, name));
102  base::MessageLoop::current()->PostTask(FROM_HERE, callback);
103}
104
105void FakeShillDeviceClient::ClearProperty(
106    const dbus::ObjectPath& device_path,
107    const std::string& name,
108    const VoidDBusMethodCallback& callback) {
109  base::DictionaryValue* device_properties = NULL;
110  if (!stub_devices_.GetDictionaryWithoutPathExpansion(device_path.value(),
111                                                       &device_properties)) {
112    PostVoidCallback(callback, DBUS_METHOD_CALL_FAILURE);
113    return;
114  }
115  device_properties->RemoveWithoutPathExpansion(name, NULL);
116  PostVoidCallback(callback, DBUS_METHOD_CALL_SUCCESS);
117}
118
119void FakeShillDeviceClient::AddIPConfig(
120    const dbus::ObjectPath& device_path,
121    const std::string& method,
122    const ObjectPathDBusMethodCallback& callback) {
123  base::MessageLoop::current()->PostTask(FROM_HERE,
124                                         base::Bind(callback,
125                                                    DBUS_METHOD_CALL_SUCCESS,
126                                                    dbus::ObjectPath()));
127}
128
129void FakeShillDeviceClient::RequirePin(const dbus::ObjectPath& device_path,
130                                       const std::string& pin,
131                                       bool require,
132                                       const base::Closure& callback,
133                                       const ErrorCallback& error_callback) {
134  VLOG(1) << "RequirePin: " << device_path.value();
135  if (pin != kSimPin) {
136    base::MessageLoop::current()->PostTask(
137        FROM_HERE,
138        base::Bind(error_callback, shill::kErrorResultIncorrectPin, ""));
139    return;
140  }
141  base::DictionaryValue* device_properties = NULL;
142  if (!stub_devices_.GetDictionaryWithoutPathExpansion(device_path.value(),
143                                                       &device_properties)) {
144    PostDeviceNotFoundError(error_callback);
145    return;
146  }
147  base::DictionaryValue* simlock_dict = NULL;
148  if (!device_properties->GetDictionaryWithoutPathExpansion(
149          shill::kSIMLockStatusProperty, &simlock_dict)) {
150    simlock_dict = new base::DictionaryValue;
151    device_properties->SetWithoutPathExpansion(
152        shill::kSIMLockStatusProperty, simlock_dict);
153  }
154  simlock_dict->Clear();
155  simlock_dict->SetBoolean(shill::kSIMLockEnabledProperty, require);
156  // TODO(stevenjb): Investigate why non-empty value breaks UI.
157  std::string lock_type = "";  // shill::kSIMLockPin
158  simlock_dict->SetString(shill::kSIMLockTypeProperty, lock_type);
159  simlock_dict->SetInteger(shill::kSIMLockRetriesLeftProperty, 5);
160
161  NotifyObserversPropertyChanged(device_path, shill::kSIMLockStatusProperty);
162  base::MessageLoop::current()->PostTask(FROM_HERE, callback);
163}
164
165void FakeShillDeviceClient::EnterPin(const dbus::ObjectPath& device_path,
166                                     const std::string& pin,
167                                     const base::Closure& callback,
168                                     const ErrorCallback& error_callback) {
169  VLOG(1) << "EnterPin: " << device_path.value();
170  if (pin != kSimPin) {
171    base::MessageLoop::current()->PostTask(
172        FROM_HERE,
173        base::Bind(error_callback, shill::kErrorResultIncorrectPin, ""));
174    return;
175  }
176  if (!stub_devices_.HasKey(device_path.value())) {
177    PostDeviceNotFoundError(error_callback);
178    return;
179  }
180  base::MessageLoop::current()->PostTask(FROM_HERE, callback);
181}
182
183void FakeShillDeviceClient::UnblockPin(const dbus::ObjectPath& device_path,
184                                       const std::string& puk,
185                                       const std::string& pin,
186                                       const base::Closure& callback,
187                                       const ErrorCallback& error_callback) {
188  VLOG(1) << "UnblockPin: " << device_path.value();
189  if (!stub_devices_.HasKey(device_path.value())) {
190    PostDeviceNotFoundError(error_callback);
191    return;
192  }
193  base::MessageLoop::current()->PostTask(FROM_HERE, callback);
194}
195
196void FakeShillDeviceClient::ChangePin(const dbus::ObjectPath& device_path,
197                                      const std::string& old_pin,
198                                      const std::string& new_pin,
199                                      const base::Closure& callback,
200                                      const ErrorCallback& error_callback) {
201  VLOG(1) << "ChangePin: " << device_path.value();
202  if (!stub_devices_.HasKey(device_path.value())) {
203    PostDeviceNotFoundError(error_callback);
204    return;
205  }
206  base::MessageLoop::current()->PostTask(FROM_HERE, callback);
207}
208
209void FakeShillDeviceClient::Register(const dbus::ObjectPath& device_path,
210                                     const std::string& network_id,
211                                     const base::Closure& callback,
212                                     const ErrorCallback& error_callback) {
213  if (!stub_devices_.HasKey(device_path.value())) {
214    PostDeviceNotFoundError(error_callback);
215    return;
216  }
217  base::MessageLoop::current()->PostTask(FROM_HERE, callback);
218}
219
220void FakeShillDeviceClient::SetCarrier(const dbus::ObjectPath& device_path,
221                                       const std::string& carrier,
222                                       const base::Closure& callback,
223                                       const ErrorCallback& error_callback) {
224  if (!stub_devices_.HasKey(device_path.value())) {
225    PostDeviceNotFoundError(error_callback);
226    return;
227  }
228  base::MessageLoop::current()->PostTask(FROM_HERE, callback);
229}
230
231void FakeShillDeviceClient::Reset(const dbus::ObjectPath& device_path,
232                                  const base::Closure& callback,
233                                  const ErrorCallback& error_callback) {
234  if (!stub_devices_.HasKey(device_path.value())) {
235    PostDeviceNotFoundError(error_callback);
236    return;
237  }
238  base::MessageLoop::current()->PostTask(FROM_HERE, callback);
239}
240
241void FakeShillDeviceClient::PerformTDLSOperation(
242    const dbus::ObjectPath& device_path,
243    const std::string& operation,
244    const std::string& peer,
245    const StringCallback& callback,
246    const ErrorCallback& error_callback) {
247  if (!stub_devices_.HasKey(device_path.value())) {
248    PostDeviceNotFoundError(error_callback);
249    return;
250  }
251  if (tdls_busy_count_) {
252    --tdls_busy_count_;
253    std::string error_message("In-Progress");
254    base::MessageLoop::current()->PostTask(
255        FROM_HERE,
256        base::Bind(error_callback,
257                   shill::kErrorResultInProgress, error_message));
258    return;
259  }
260  std::string result;
261  if (operation == shill::kTDLSStatusOperation)
262    result = shill::kTDLSConnectedState;
263  base::MessageLoop::current()->PostTask(FROM_HERE,
264                                         base::Bind(callback, result));
265}
266
267ShillDeviceClient::TestInterface* FakeShillDeviceClient::GetTestInterface() {
268  return this;
269}
270
271// ShillDeviceClient::TestInterface overrides.
272
273void FakeShillDeviceClient::AddDevice(const std::string& device_path,
274                                      const std::string& type,
275                                      const std::string& name) {
276  DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
277      AddDevice(device_path);
278
279  base::DictionaryValue* properties = GetDeviceProperties(device_path);
280  properties->SetStringWithoutPathExpansion(shill::kTypeProperty, type);
281  properties->SetStringWithoutPathExpansion(shill::kNameProperty, name);
282  properties->SetStringWithoutPathExpansion(shill::kDBusObjectProperty,
283                                            device_path);
284  properties->SetStringWithoutPathExpansion(
285      shill::kDBusServiceProperty, modemmanager::kModemManager1ServiceName);
286  if (type == shill::kTypeCellular) {
287    properties->SetBooleanWithoutPathExpansion(
288        shill::kCellularAllowRoamingProperty, false);
289  }
290}
291
292void FakeShillDeviceClient::RemoveDevice(const std::string& device_path) {
293  DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
294      RemoveDevice(device_path);
295
296  stub_devices_.RemoveWithoutPathExpansion(device_path, NULL);
297}
298
299void FakeShillDeviceClient::ClearDevices() {
300  DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
301      ClearDevices();
302
303  stub_devices_.Clear();
304}
305
306void FakeShillDeviceClient::SetDeviceProperty(const std::string& device_path,
307                                              const std::string& name,
308                                              const base::Value& value) {
309  VLOG(1) << "SetDeviceProperty: " << device_path
310          << ": " << name << " = " << value;
311  SetProperty(dbus::ObjectPath(device_path), name, value,
312              base::Bind(&base::DoNothing),
313              base::Bind(&ErrorFunction, device_path));
314}
315
316std::string FakeShillDeviceClient::GetDevicePathForType(
317    const std::string& type) {
318  for (base::DictionaryValue::Iterator iter(stub_devices_);
319       !iter.IsAtEnd(); iter.Advance()) {
320    const base::DictionaryValue* properties = NULL;
321    if (!iter.value().GetAsDictionary(&properties))
322      continue;
323    std::string prop_type;
324    if (!properties->GetStringWithoutPathExpansion(
325            shill::kTypeProperty, &prop_type) ||
326        prop_type != type)
327      continue;
328    return iter.key();
329  }
330  return std::string();
331}
332
333void FakeShillDeviceClient::PassStubDeviceProperties(
334    const dbus::ObjectPath& device_path,
335    const DictionaryValueCallback& callback) const {
336  const base::DictionaryValue* device_properties = NULL;
337  if (!stub_devices_.GetDictionaryWithoutPathExpansion(
338      device_path.value(), &device_properties)) {
339    base::DictionaryValue empty_dictionary;
340    callback.Run(DBUS_METHOD_CALL_FAILURE, empty_dictionary);
341    return;
342  }
343  callback.Run(DBUS_METHOD_CALL_SUCCESS, *device_properties);
344}
345
346// Posts a task to run a void callback with status code |status|.
347void FakeShillDeviceClient::PostVoidCallback(
348    const VoidDBusMethodCallback& callback,
349    DBusMethodCallStatus status) {
350  base::MessageLoop::current()->PostTask(FROM_HERE,
351                                         base::Bind(callback, status));
352}
353
354void FakeShillDeviceClient::NotifyObserversPropertyChanged(
355    const dbus::ObjectPath& device_path,
356    const std::string& property) {
357  base::DictionaryValue* dict = NULL;
358  std::string path = device_path.value();
359  if (!stub_devices_.GetDictionaryWithoutPathExpansion(path, &dict)) {
360    LOG(ERROR) << "Notify for unknown service: " << path;
361    return;
362  }
363  base::Value* value = NULL;
364  if (!dict->GetWithoutPathExpansion(property, &value)) {
365    LOG(ERROR) << "Notify for unknown property: "
366        << path << " : " << property;
367    return;
368  }
369  FOR_EACH_OBSERVER(ShillPropertyChangedObserver,
370                    GetObserverList(device_path),
371                    OnPropertyChanged(property, *value));
372}
373
374base::DictionaryValue* FakeShillDeviceClient::GetDeviceProperties(
375    const std::string& device_path) {
376  base::DictionaryValue* properties = NULL;
377  if (!stub_devices_.GetDictionaryWithoutPathExpansion(
378      device_path, &properties)) {
379    properties = new base::DictionaryValue;
380    stub_devices_.SetWithoutPathExpansion(device_path, properties);
381  }
382  return properties;
383}
384
385FakeShillDeviceClient::PropertyObserverList&
386FakeShillDeviceClient::GetObserverList(const dbus::ObjectPath& device_path) {
387  std::map<dbus::ObjectPath, PropertyObserverList*>::iterator iter =
388      observer_list_.find(device_path);
389  if (iter != observer_list_.end())
390    return *(iter->second);
391  PropertyObserverList* observer_list = new PropertyObserverList();
392  observer_list_[device_path] = observer_list;
393  return *observer_list;
394}
395
396}  // namespace chromeos
397