- add sources.
[platform/framework/web/crosswalk.git] / src / chromeos / dbus / fake_shill_service_client.cc
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_service_client.h"
6
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/command_line.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/stl_util.h"
12 #include "base/strings/string_util.h"
13 #include "base/values.h"
14 #include "chromeos/chromeos_switches.h"
15 #include "chromeos/dbus/dbus_thread_manager.h"
16 #include "chromeos/dbus/shill_manager_client.h"
17 #include "chromeos/dbus/shill_property_changed_observer.h"
18 #include "chromeos/dbus/shill_stub_helper.h"
19 #include "chromeos/network/shill_property_util.h"
20 #include "dbus/bus.h"
21 #include "dbus/message.h"
22 #include "dbus/object_path.h"
23 #include "third_party/cros_system_api/dbus/service_constants.h"
24
25 namespace chromeos {
26
27 namespace {
28
29 void ErrorFunction(const std::string& error_name,
30                    const std::string& error_message) {
31   LOG(ERROR) << "Shill Error: " << error_name << " : " << error_message;
32 }
33
34 void PassStubListValue(const ShillServiceClient::ListValueCallback& callback,
35                        base::ListValue* value) {
36   callback.Run(*value);
37 }
38
39 void PassStubServiceProperties(
40     const ShillServiceClient::DictionaryValueCallback& callback,
41     DBusMethodCallStatus call_status,
42     const base::DictionaryValue* properties) {
43   callback.Run(call_status, *properties);
44 }
45
46 }  // namespace
47
48 FakeShillServiceClient::FakeShillServiceClient() : weak_ptr_factory_(this) {
49 }
50
51 FakeShillServiceClient::~FakeShillServiceClient() {
52   STLDeleteContainerPairSecondPointers(
53       observer_list_.begin(), observer_list_.end());
54 }
55
56
57 // ShillServiceClient overrides.
58
59 void FakeShillServiceClient::Init(dbus::Bus* bus) {
60 }
61
62 void FakeShillServiceClient::AddPropertyChangedObserver(
63     const dbus::ObjectPath& service_path,
64     ShillPropertyChangedObserver* observer) {
65   GetObserverList(service_path).AddObserver(observer);
66 }
67
68 void FakeShillServiceClient::RemovePropertyChangedObserver(
69     const dbus::ObjectPath& service_path,
70     ShillPropertyChangedObserver* observer) {
71   GetObserverList(service_path).RemoveObserver(observer);
72 }
73
74 void FakeShillServiceClient::GetProperties(
75     const dbus::ObjectPath& service_path,
76     const DictionaryValueCallback& callback) {
77   base::DictionaryValue* nested_dict = NULL;
78   scoped_ptr<base::DictionaryValue> result_properties;
79   DBusMethodCallStatus call_status;
80   stub_services_.GetDictionaryWithoutPathExpansion(service_path.value(),
81                                                    &nested_dict);
82   if (nested_dict) {
83     result_properties.reset(nested_dict->DeepCopy());
84     // Remove credentials that Shill wouldn't send.
85     result_properties->RemoveWithoutPathExpansion(shill::kPassphraseProperty,
86                                                   NULL);
87     call_status = DBUS_METHOD_CALL_SUCCESS;
88   } else {
89     LOG(ERROR) << "Properties not found for: " << service_path.value();
90     result_properties.reset(new base::DictionaryValue);
91     call_status = DBUS_METHOD_CALL_FAILURE;
92   }
93
94   base::MessageLoop::current()->PostTask(
95       FROM_HERE,
96       base::Bind(&PassStubServiceProperties,
97                  callback,
98                  call_status,
99                  base::Owned(result_properties.release())));
100 }
101
102 void FakeShillServiceClient::SetProperty(const dbus::ObjectPath& service_path,
103                                          const std::string& name,
104                                          const base::Value& value,
105                                          const base::Closure& callback,
106                                          const ErrorCallback& error_callback) {
107   if (!SetServiceProperty(service_path.value(), name, value)) {
108     LOG(ERROR) << "Service not found: " << service_path.value();
109     error_callback.Run("Error.InvalidService", "Invalid Service");
110     return;
111   }
112   base::MessageLoop::current()->PostTask(FROM_HERE, callback);
113 }
114
115 void FakeShillServiceClient::SetProperties(
116     const dbus::ObjectPath& service_path,
117     const base::DictionaryValue& properties,
118     const base::Closure& callback,
119     const ErrorCallback& error_callback) {
120   for (base::DictionaryValue::Iterator iter(properties);
121        !iter.IsAtEnd(); iter.Advance()) {
122     if (!SetServiceProperty(service_path.value(), iter.key(), iter.value())) {
123       LOG(ERROR) << "Service not found: " << service_path.value();
124       error_callback.Run("Error.InvalidService", "Invalid Service");
125       return;
126     }
127   }
128   base::MessageLoop::current()->PostTask(FROM_HERE, callback);
129 }
130
131 void FakeShillServiceClient::ClearProperty(
132     const dbus::ObjectPath& service_path,
133     const std::string& name,
134     const base::Closure& callback,
135     const ErrorCallback& error_callback) {
136   base::DictionaryValue* dict = NULL;
137   if (!stub_services_.GetDictionaryWithoutPathExpansion(
138       service_path.value(), &dict)) {
139     error_callback.Run("Error.InvalidService", "Invalid Service");
140     return;
141   }
142   dict->RemoveWithoutPathExpansion(name, NULL);
143   // Note: Shill does not send notifications when properties are cleared.
144   base::MessageLoop::current()->PostTask(FROM_HERE, callback);
145 }
146
147 void FakeShillServiceClient::ClearProperties(
148     const dbus::ObjectPath& service_path,
149     const std::vector<std::string>& names,
150     const ListValueCallback& callback,
151     const ErrorCallback& error_callback) {
152   base::DictionaryValue* dict = NULL;
153   if (!stub_services_.GetDictionaryWithoutPathExpansion(
154       service_path.value(), &dict)) {
155     error_callback.Run("Error.InvalidService", "Invalid Service");
156     return;
157   }
158   scoped_ptr<base::ListValue> results(new base::ListValue);
159   for (std::vector<std::string>::const_iterator iter = names.begin();
160       iter != names.end(); ++iter) {
161     dict->RemoveWithoutPathExpansion(*iter, NULL);
162     // Note: Shill does not send notifications when properties are cleared.
163     results->AppendBoolean(true);
164   }
165   base::MessageLoop::current()->PostTask(
166       FROM_HERE,
167       base::Bind(&PassStubListValue,
168                  callback, base::Owned(results.release())));
169 }
170
171 void FakeShillServiceClient::Connect(const dbus::ObjectPath& service_path,
172                                      const base::Closure& callback,
173                                      const ErrorCallback& error_callback) {
174   VLOG(1) << "FakeShillServiceClient::Connect: " << service_path.value();
175   base::DictionaryValue* service_properties = NULL;
176   if (!stub_services_.GetDictionary(
177           service_path.value(), &service_properties)) {
178     LOG(ERROR) << "Service not found: " << service_path.value();
179     error_callback.Run("Error.InvalidService", "Invalid Service");
180     return;
181   }
182
183   // Set any other services of the same Type to 'offline' first, before setting
184   // State to Association which will trigger sorting Manager.Services and
185   // sending an update.
186   SetOtherServicesOffline(service_path.value());
187
188   // Set Associating.
189   base::StringValue associating_value(shill::kStateAssociation);
190   SetServiceProperty(service_path.value(),
191                      shill::kStateProperty,
192                      associating_value);
193
194   // Stay Associating until the state is changed again after a delay.
195   base::TimeDelta delay;
196   if (CommandLine::ForCurrentProcess()->HasSwitch(
197           chromeos::switches::kEnableStubInteractive)) {
198     const int kConnectDelaySeconds = 5;
199     delay = base::TimeDelta::FromSeconds(kConnectDelaySeconds);
200   }
201   base::MessageLoop::current()->PostDelayedTask(
202       FROM_HERE,
203       base::Bind(&FakeShillServiceClient::ContinueConnect,
204                  weak_ptr_factory_.GetWeakPtr(),
205                  service_path.value()),
206       delay);
207
208   callback.Run();
209 }
210
211 void FakeShillServiceClient::Disconnect(const dbus::ObjectPath& service_path,
212                                         const base::Closure& callback,
213                                         const ErrorCallback& error_callback) {
214   base::Value* service;
215   if (!stub_services_.Get(service_path.value(), &service)) {
216     error_callback.Run("Error.InvalidService", "Invalid Service");
217     return;
218   }
219   base::TimeDelta delay;
220   if (CommandLine::ForCurrentProcess()->HasSwitch(
221           chromeos::switches::kEnableStubInteractive)) {
222     const int kConnectDelaySeconds = 2;
223     delay = base::TimeDelta::FromSeconds(kConnectDelaySeconds);
224   }
225   // Set Idle after a delay
226   base::StringValue idle_value(shill::kStateIdle);
227   base::MessageLoop::current()->PostDelayedTask(
228       FROM_HERE,
229       base::Bind(&FakeShillServiceClient::SetProperty,
230                  weak_ptr_factory_.GetWeakPtr(),
231                  service_path,
232                  shill::kStateProperty,
233                  idle_value,
234                  base::Bind(&base::DoNothing),
235                  error_callback),
236       delay);
237   callback.Run();
238 }
239
240 void FakeShillServiceClient::Remove(const dbus::ObjectPath& service_path,
241                                     const base::Closure& callback,
242                                     const ErrorCallback& error_callback) {
243   base::MessageLoop::current()->PostTask(FROM_HERE, callback);
244 }
245
246 void FakeShillServiceClient::ActivateCellularModem(
247     const dbus::ObjectPath& service_path,
248     const std::string& carrier,
249     const base::Closure& callback,
250     const ErrorCallback& error_callback) {
251   base::DictionaryValue* service_properties =
252       GetModifiableServiceProperties(service_path.value(), false);
253   if (!service_properties) {
254     LOG(ERROR) << "Service not found: " << service_path.value();
255     error_callback.Run("Error.InvalidService", "Invalid Service");
256   }
257   SetServiceProperty(service_path.value(),
258                      shill::kActivationStateProperty,
259                      base::StringValue(shill::kActivationStateActivating));
260   base::TimeDelta delay;
261   if (CommandLine::ForCurrentProcess()->HasSwitch(
262           chromeos::switches::kEnableStubInteractive)) {
263     const int kConnectDelaySeconds = 2;
264     delay = base::TimeDelta::FromSeconds(kConnectDelaySeconds);
265   }
266   // Set Activated after a delay
267   base::MessageLoop::current()->PostDelayedTask(
268       FROM_HERE,
269       base::Bind(&FakeShillServiceClient::SetCellularActivated,
270                  weak_ptr_factory_.GetWeakPtr(),
271                  service_path,
272                  error_callback),
273       delay);
274
275   base::MessageLoop::current()->PostTask(FROM_HERE, callback);
276 }
277
278 void FakeShillServiceClient::CompleteCellularActivation(
279     const dbus::ObjectPath& service_path,
280     const base::Closure& callback,
281     const ErrorCallback& error_callback) {
282   base::MessageLoop::current()->PostTask(FROM_HERE, callback);
283 }
284
285 void FakeShillServiceClient::GetLoadableProfileEntries(
286     const dbus::ObjectPath& service_path,
287     const DictionaryValueCallback& callback) {
288   // Provide a dictionary with a single { profile_path, service_path } entry
289   // if the Profile property is set, or an empty dictionary.
290   scoped_ptr<base::DictionaryValue> result_properties(
291       new base::DictionaryValue);
292   base::DictionaryValue* service_properties =
293       GetModifiableServiceProperties(service_path.value(), false);
294   if (service_properties) {
295     std::string profile_path;
296     if (service_properties->GetStringWithoutPathExpansion(
297             shill::kProfileProperty, &profile_path)) {
298       result_properties->SetStringWithoutPathExpansion(
299           profile_path, service_path.value());
300     }
301   } else {
302     LOG(WARNING) << "Service not in profile: " << service_path.value();
303   }
304
305   DBusMethodCallStatus call_status = DBUS_METHOD_CALL_SUCCESS;
306   base::MessageLoop::current()->PostTask(
307       FROM_HERE,
308       base::Bind(&PassStubServiceProperties,
309                  callback,
310                  call_status,
311                  base::Owned(result_properties.release())));
312 }
313
314 ShillServiceClient::TestInterface* FakeShillServiceClient::GetTestInterface() {
315   return this;
316 }
317
318 // ShillServiceClient::TestInterface overrides.
319
320 void FakeShillServiceClient::AddService(const std::string& service_path,
321                                         const std::string& name,
322                                         const std::string& type,
323                                         const std::string& state,
324                                         bool add_to_visible_list,
325                                         bool add_to_watch_list) {
326   std::string nstate = state;
327   if (CommandLine::ForCurrentProcess()->HasSwitch(
328           chromeos::switches::kDefaultStubNetworkStateIdle)) {
329     nstate = shill::kStateIdle;
330   }
331   AddServiceWithIPConfig(service_path, name, type, nstate, "",
332                          add_to_visible_list, add_to_watch_list);
333 }
334
335 void FakeShillServiceClient::AddServiceWithIPConfig(
336     const std::string& service_path,
337     const std::string& name,
338     const std::string& type,
339     const std::string& state,
340     const std::string& ipconfig_path,
341     bool add_to_visible_list,
342     bool add_to_watch_list) {
343   DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
344       AddManagerService(service_path, add_to_visible_list, add_to_watch_list);
345
346   base::DictionaryValue* properties =
347       GetModifiableServiceProperties(service_path, true);
348   connect_behavior_.erase(service_path);
349   shill_property_util::SetSSID(name, properties);
350   properties->SetWithoutPathExpansion(
351       shill::kNameProperty,
352       base::Value::CreateStringValue(name));
353   properties->SetWithoutPathExpansion(
354       shill::kDeviceProperty,
355       base::Value::CreateStringValue(
356           shill_stub_helper::DevicePathForType(type)));
357   properties->SetWithoutPathExpansion(
358       shill::kTypeProperty,
359       base::Value::CreateStringValue(type));
360   properties->SetWithoutPathExpansion(
361       shill::kStateProperty,
362       base::Value::CreateStringValue(state));
363   if (!ipconfig_path.empty())
364     properties->SetWithoutPathExpansion(
365         shill::kIPConfigProperty,
366         base::Value::CreateStringValue(ipconfig_path));
367 }
368
369 void FakeShillServiceClient::RemoveService(const std::string& service_path) {
370   DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
371       RemoveManagerService(service_path);
372
373   stub_services_.RemoveWithoutPathExpansion(service_path, NULL);
374   connect_behavior_.erase(service_path);
375 }
376
377 bool FakeShillServiceClient::SetServiceProperty(const std::string& service_path,
378                                                 const std::string& property,
379                                                 const base::Value& value) {
380   base::DictionaryValue* dict = NULL;
381   if (!stub_services_.GetDictionaryWithoutPathExpansion(service_path, &dict))
382     return false;
383
384   VLOG(1) << "Service.SetProperty: " << property << " = " << value
385           << " For: " << service_path;
386
387   base::DictionaryValue new_properties;
388   std::string changed_property;
389   bool case_sensitive = true;
390   if (StartsWithASCII(property, "Provider.", case_sensitive) ||
391       StartsWithASCII(property, "OpenVPN.", case_sensitive) ||
392       StartsWithASCII(property, "L2TPIPsec.", case_sensitive)) {
393     // These properties are only nested within the Provider dictionary if read
394     // from Shill.
395     base::DictionaryValue* provider = new base::DictionaryValue;
396     provider->SetWithoutPathExpansion(property, value.DeepCopy());
397     new_properties.SetWithoutPathExpansion(shill::kProviderProperty, provider);
398     changed_property = shill::kProviderProperty;
399   } else {
400     new_properties.SetWithoutPathExpansion(property, value.DeepCopy());
401     changed_property = property;
402   }
403
404   dict->MergeDictionary(&new_properties);
405
406   if (property == shill::kStateProperty) {
407     // When State changes the sort order of Services may change.
408     DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
409         SortManagerServices();
410   }
411
412   base::MessageLoop::current()->PostTask(
413       FROM_HERE,
414       base::Bind(&FakeShillServiceClient::NotifyObserversPropertyChanged,
415                  weak_ptr_factory_.GetWeakPtr(),
416                  dbus::ObjectPath(service_path), changed_property));
417   return true;
418 }
419
420 const base::DictionaryValue* FakeShillServiceClient::GetServiceProperties(
421     const std::string& service_path) const {
422   const base::DictionaryValue* properties = NULL;
423   stub_services_.GetDictionaryWithoutPathExpansion(service_path, &properties);
424   return properties;
425 }
426
427 void FakeShillServiceClient::ClearServices() {
428   DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
429       ClearManagerServices();
430
431   stub_services_.Clear();
432   connect_behavior_.clear();
433 }
434
435 void FakeShillServiceClient::SetConnectBehavior(const std::string& service_path,
436                                 const base::Closure& behavior) {
437   connect_behavior_[service_path] = behavior;
438 }
439
440 void FakeShillServiceClient::NotifyObserversPropertyChanged(
441     const dbus::ObjectPath& service_path,
442     const std::string& property) {
443   base::DictionaryValue* dict = NULL;
444   std::string path = service_path.value();
445   if (!stub_services_.GetDictionaryWithoutPathExpansion(path, &dict)) {
446     LOG(ERROR) << "Notify for unknown service: " << path;
447     return;
448   }
449   base::Value* value = NULL;
450   if (!dict->GetWithoutPathExpansion(property, &value)) {
451     LOG(ERROR) << "Notify for unknown property: "
452                << path << " : " << property;
453     return;
454   }
455   FOR_EACH_OBSERVER(ShillPropertyChangedObserver,
456                     GetObserverList(service_path),
457                     OnPropertyChanged(property, *value));
458 }
459
460 base::DictionaryValue* FakeShillServiceClient::GetModifiableServiceProperties(
461     const std::string& service_path, bool create_if_missing) {
462   base::DictionaryValue* properties = NULL;
463   if (!stub_services_.GetDictionaryWithoutPathExpansion(service_path,
464                                                         &properties) &&
465       create_if_missing) {
466     properties = new base::DictionaryValue;
467     stub_services_.Set(service_path, properties);
468   }
469   return properties;
470 }
471
472 FakeShillServiceClient::PropertyObserverList&
473 FakeShillServiceClient::GetObserverList(const dbus::ObjectPath& device_path) {
474   std::map<dbus::ObjectPath, PropertyObserverList*>::iterator iter =
475       observer_list_.find(device_path);
476   if (iter != observer_list_.end())
477     return *(iter->second);
478   PropertyObserverList* observer_list = new PropertyObserverList();
479   observer_list_[device_path] = observer_list;
480   return *observer_list;
481 }
482
483 void FakeShillServiceClient::SetOtherServicesOffline(
484     const std::string& service_path) {
485   const base::DictionaryValue* service_properties = GetServiceProperties(
486       service_path);
487   if (!service_properties) {
488     LOG(ERROR) << "Missing service: " << service_path;
489     return;
490   }
491   std::string service_type;
492   service_properties->GetString(shill::kTypeProperty, &service_type);
493   // Set all other services of the same type to offline (Idle).
494   for (base::DictionaryValue::Iterator iter(stub_services_);
495        !iter.IsAtEnd(); iter.Advance()) {
496     std::string path = iter.key();
497     if (path == service_path)
498       continue;
499     base::DictionaryValue* properties;
500     if (!stub_services_.GetDictionaryWithoutPathExpansion(path, &properties))
501       NOTREACHED();
502
503     std::string type;
504     properties->GetString(shill::kTypeProperty, &type);
505     if (type != service_type)
506       continue;
507     properties->SetWithoutPathExpansion(
508         shill::kStateProperty,
509         base::Value::CreateStringValue(shill::kStateIdle));
510   }
511 }
512
513 void FakeShillServiceClient::SetCellularActivated(
514     const dbus::ObjectPath& service_path,
515     const ErrorCallback& error_callback) {
516   SetProperty(service_path,
517               shill::kActivationStateProperty,
518               base::StringValue(shill::kActivationStateActivated),
519               base::Bind(&base::DoNothing),
520               error_callback);
521   SetProperty(service_path,
522               shill::kConnectableProperty,
523               base::FundamentalValue(true),
524               base::Bind(&base::DoNothing),
525               error_callback);
526 }
527
528 void FakeShillServiceClient::ContinueConnect(
529     const std::string& service_path) {
530   VLOG(1) << "FakeShillServiceClient::ContinueConnect: " << service_path;
531   base::DictionaryValue* service_properties = NULL;
532   if (!stub_services_.GetDictionary(service_path, &service_properties)) {
533     LOG(ERROR) << "Service not found: " << service_path;
534     return;
535   }
536
537   if (ContainsKey(connect_behavior_, service_path)) {
538     const base::Closure& custom_connect_behavior =
539         connect_behavior_[service_path];
540     custom_connect_behavior.Run();
541     return;
542   }
543
544   // No custom connect behavior set, continue with the default connect behavior.
545   std::string passphrase;
546   service_properties->GetStringWithoutPathExpansion(
547       shill::kPassphraseProperty, &passphrase);
548   if (passphrase == "failure") {
549     // Simulate a password failure.
550     SetServiceProperty(service_path,
551                        shill::kStateProperty,
552                        base::StringValue(shill::kStateFailure));
553     base::MessageLoop::current()->PostTask(
554         FROM_HERE,
555         base::Bind(
556             base::IgnoreResult(&FakeShillServiceClient::SetServiceProperty),
557             weak_ptr_factory_.GetWeakPtr(),
558             service_path,
559             shill::kErrorProperty,
560             base::StringValue(shill::kErrorBadPassphrase)));
561   } else {
562     // Set Online.
563     SetServiceProperty(service_path,
564                        shill::kStateProperty,
565                        base::StringValue(shill::kStateOnline));
566   }
567 }
568
569 }  // namespace chromeos