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.
5 #include "chromeos/dbus/fake_shill_service_client.h"
8 #include "base/bind_helpers.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string_util.h"
12 #include "base/values.h"
13 #include "chromeos/dbus/dbus_thread_manager.h"
14 #include "chromeos/dbus/shill_device_client.h"
15 #include "chromeos/dbus/shill_manager_client.h"
16 #include "chromeos/dbus/shill_property_changed_observer.h"
17 #include "chromeos/network/shill_property_util.h"
19 #include "dbus/message.h"
20 #include "dbus/object_path.h"
21 #include "third_party/cros_system_api/dbus/service_constants.h"
27 void PassStubListValue(const ShillServiceClient::ListValueCallback& callback,
28 base::ListValue* value) {
32 void PassStubServiceProperties(
33 const ShillServiceClient::DictionaryValueCallback& callback,
34 DBusMethodCallStatus call_status,
35 const base::DictionaryValue* properties) {
36 callback.Run(call_status, *properties);
39 void CallSortManagerServices() {
40 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
41 SortManagerServices();
44 int GetInteractiveDelay() {
45 return DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
46 GetInteractiveDelay();
51 FakeShillServiceClient::FakeShillServiceClient() : weak_ptr_factory_(this) {
54 FakeShillServiceClient::~FakeShillServiceClient() {
55 STLDeleteContainerPairSecondPointers(
56 observer_list_.begin(), observer_list_.end());
60 // ShillServiceClient overrides.
62 void FakeShillServiceClient::Init(dbus::Bus* bus) {
65 void FakeShillServiceClient::AddPropertyChangedObserver(
66 const dbus::ObjectPath& service_path,
67 ShillPropertyChangedObserver* observer) {
68 GetObserverList(service_path).AddObserver(observer);
71 void FakeShillServiceClient::RemovePropertyChangedObserver(
72 const dbus::ObjectPath& service_path,
73 ShillPropertyChangedObserver* observer) {
74 GetObserverList(service_path).RemoveObserver(observer);
77 void FakeShillServiceClient::GetProperties(
78 const dbus::ObjectPath& service_path,
79 const DictionaryValueCallback& callback) {
80 base::DictionaryValue* nested_dict = NULL;
81 scoped_ptr<base::DictionaryValue> result_properties;
82 DBusMethodCallStatus call_status;
83 stub_services_.GetDictionaryWithoutPathExpansion(service_path.value(),
86 result_properties.reset(nested_dict->DeepCopy());
87 // Remove credentials that Shill wouldn't send.
88 result_properties->RemoveWithoutPathExpansion(shill::kPassphraseProperty,
90 call_status = DBUS_METHOD_CALL_SUCCESS;
92 // This may happen if we remove services from the list.
93 VLOG(2) << "Properties not found for: " << service_path.value();
94 result_properties.reset(new base::DictionaryValue);
95 call_status = DBUS_METHOD_CALL_FAILURE;
98 base::MessageLoop::current()->PostTask(
100 base::Bind(&PassStubServiceProperties,
103 base::Owned(result_properties.release())));
106 void FakeShillServiceClient::SetProperty(const dbus::ObjectPath& service_path,
107 const std::string& name,
108 const base::Value& value,
109 const base::Closure& callback,
110 const ErrorCallback& error_callback) {
111 if (!SetServiceProperty(service_path.value(), name, value)) {
112 LOG(ERROR) << "Service not found: " << service_path.value();
113 error_callback.Run("Error.InvalidService", "Invalid Service");
116 base::MessageLoop::current()->PostTask(FROM_HERE, callback);
119 void FakeShillServiceClient::SetProperties(
120 const dbus::ObjectPath& service_path,
121 const base::DictionaryValue& properties,
122 const base::Closure& callback,
123 const ErrorCallback& error_callback) {
124 for (base::DictionaryValue::Iterator iter(properties);
125 !iter.IsAtEnd(); iter.Advance()) {
126 if (!SetServiceProperty(service_path.value(), iter.key(), iter.value())) {
127 LOG(ERROR) << "Service not found: " << service_path.value();
128 error_callback.Run("Error.InvalidService", "Invalid Service");
132 base::MessageLoop::current()->PostTask(FROM_HERE, callback);
135 void FakeShillServiceClient::ClearProperty(
136 const dbus::ObjectPath& service_path,
137 const std::string& name,
138 const base::Closure& callback,
139 const ErrorCallback& error_callback) {
140 base::DictionaryValue* dict = NULL;
141 if (!stub_services_.GetDictionaryWithoutPathExpansion(
142 service_path.value(), &dict)) {
143 error_callback.Run("Error.InvalidService", "Invalid Service");
146 dict->RemoveWithoutPathExpansion(name, NULL);
147 // Note: Shill does not send notifications when properties are cleared.
148 base::MessageLoop::current()->PostTask(FROM_HERE, callback);
151 void FakeShillServiceClient::ClearProperties(
152 const dbus::ObjectPath& service_path,
153 const std::vector<std::string>& names,
154 const ListValueCallback& callback,
155 const ErrorCallback& error_callback) {
156 base::DictionaryValue* dict = NULL;
157 if (!stub_services_.GetDictionaryWithoutPathExpansion(
158 service_path.value(), &dict)) {
159 error_callback.Run("Error.InvalidService", "Invalid Service");
162 scoped_ptr<base::ListValue> results(new base::ListValue);
163 for (std::vector<std::string>::const_iterator iter = names.begin();
164 iter != names.end(); ++iter) {
165 dict->RemoveWithoutPathExpansion(*iter, NULL);
166 // Note: Shill does not send notifications when properties are cleared.
167 results->AppendBoolean(true);
169 base::MessageLoop::current()->PostTask(
171 base::Bind(&PassStubListValue,
172 callback, base::Owned(results.release())));
175 void FakeShillServiceClient::Connect(const dbus::ObjectPath& service_path,
176 const base::Closure& callback,
177 const ErrorCallback& error_callback) {
178 VLOG(1) << "FakeShillServiceClient::Connect: " << service_path.value();
179 base::DictionaryValue* service_properties = NULL;
180 if (!stub_services_.GetDictionary(
181 service_path.value(), &service_properties)) {
182 LOG(ERROR) << "Service not found: " << service_path.value();
183 error_callback.Run("Error.InvalidService", "Invalid Service");
187 // Set any other services of the same Type to 'offline' first, before setting
188 // State to Association which will trigger sorting Manager.Services and
189 // sending an update.
190 SetOtherServicesOffline(service_path.value());
193 base::StringValue associating_value(shill::kStateAssociation);
194 SetServiceProperty(service_path.value(),
195 shill::kStateProperty,
198 // Stay Associating until the state is changed again after a delay.
199 base::MessageLoop::current()->PostDelayedTask(
201 base::Bind(&FakeShillServiceClient::ContinueConnect,
202 weak_ptr_factory_.GetWeakPtr(),
203 service_path.value()),
204 base::TimeDelta::FromSeconds(GetInteractiveDelay()));
209 void FakeShillServiceClient::Disconnect(const dbus::ObjectPath& service_path,
210 const base::Closure& callback,
211 const ErrorCallback& error_callback) {
212 base::Value* service;
213 if (!stub_services_.Get(service_path.value(), &service)) {
214 error_callback.Run("Error.InvalidService", "Invalid Service");
217 // Set Idle after a delay
218 base::StringValue idle_value(shill::kStateIdle);
219 base::MessageLoop::current()->PostDelayedTask(
221 base::Bind(&FakeShillServiceClient::SetProperty,
222 weak_ptr_factory_.GetWeakPtr(),
224 shill::kStateProperty,
226 base::Bind(&base::DoNothing),
228 base::TimeDelta::FromSeconds(GetInteractiveDelay()));
232 void FakeShillServiceClient::Remove(const dbus::ObjectPath& service_path,
233 const base::Closure& callback,
234 const ErrorCallback& error_callback) {
235 base::MessageLoop::current()->PostTask(FROM_HERE, callback);
238 void FakeShillServiceClient::ActivateCellularModem(
239 const dbus::ObjectPath& service_path,
240 const std::string& carrier,
241 const base::Closure& callback,
242 const ErrorCallback& error_callback) {
243 base::DictionaryValue* service_properties =
244 GetModifiableServiceProperties(service_path.value(), false);
245 if (!service_properties) {
246 LOG(ERROR) << "Service not found: " << service_path.value();
247 error_callback.Run("Error.InvalidService", "Invalid Service");
249 SetServiceProperty(service_path.value(),
250 shill::kActivationStateProperty,
251 base::StringValue(shill::kActivationStateActivating));
252 // Set Activated after a delay
253 base::MessageLoop::current()->PostDelayedTask(
255 base::Bind(&FakeShillServiceClient::SetCellularActivated,
256 weak_ptr_factory_.GetWeakPtr(),
259 base::TimeDelta::FromSeconds(GetInteractiveDelay()));
261 base::MessageLoop::current()->PostTask(FROM_HERE, callback);
264 void FakeShillServiceClient::CompleteCellularActivation(
265 const dbus::ObjectPath& service_path,
266 const base::Closure& callback,
267 const ErrorCallback& error_callback) {
268 base::MessageLoop::current()->PostTask(FROM_HERE, callback);
271 void FakeShillServiceClient::GetLoadableProfileEntries(
272 const dbus::ObjectPath& service_path,
273 const DictionaryValueCallback& callback) {
274 // Provide a dictionary with a single { profile_path, service_path } entry
275 // if the Profile property is set, or an empty dictionary.
276 scoped_ptr<base::DictionaryValue> result_properties(
277 new base::DictionaryValue);
278 base::DictionaryValue* service_properties =
279 GetModifiableServiceProperties(service_path.value(), false);
280 if (service_properties) {
281 std::string profile_path;
282 if (service_properties->GetStringWithoutPathExpansion(
283 shill::kProfileProperty, &profile_path)) {
284 result_properties->SetStringWithoutPathExpansion(
285 profile_path, service_path.value());
288 LOG(WARNING) << "Service not in profile: " << service_path.value();
291 DBusMethodCallStatus call_status = DBUS_METHOD_CALL_SUCCESS;
292 base::MessageLoop::current()->PostTask(
294 base::Bind(&PassStubServiceProperties,
297 base::Owned(result_properties.release())));
300 ShillServiceClient::TestInterface* FakeShillServiceClient::GetTestInterface() {
304 // ShillServiceClient::TestInterface overrides.
306 void FakeShillServiceClient::AddService(const std::string& service_path,
307 const std::string& name,
308 const std::string& type,
309 const std::string& state,
310 bool add_to_visible_list,
311 bool add_to_watch_list) {
312 AddServiceWithIPConfig(service_path, name, type, state, "",
313 add_to_visible_list, add_to_watch_list);
316 void FakeShillServiceClient::AddServiceWithIPConfig(
317 const std::string& service_path,
318 const std::string& name,
319 const std::string& type,
320 const std::string& state,
321 const std::string& ipconfig_path,
322 bool add_to_visible_list,
323 bool add_to_watch_list) {
324 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
325 AddManagerService(service_path, add_to_visible_list, add_to_watch_list);
326 std::string device_path =
327 DBusThreadManager::Get()->GetShillDeviceClient()->GetTestInterface()->
328 GetDevicePathForType(type);
330 base::DictionaryValue* properties =
331 GetModifiableServiceProperties(service_path, true);
332 connect_behavior_.erase(service_path);
333 shill_property_util::SetSSID(name, properties);
334 properties->SetWithoutPathExpansion(
335 shill::kNameProperty,
336 base::Value::CreateStringValue(name));
337 properties->SetWithoutPathExpansion(
338 shill::kDeviceProperty,
339 base::Value::CreateStringValue(device_path));
340 properties->SetWithoutPathExpansion(
341 shill::kTypeProperty,
342 base::Value::CreateStringValue(type));
343 properties->SetWithoutPathExpansion(
344 shill::kStateProperty,
345 base::Value::CreateStringValue(state));
346 if (!ipconfig_path.empty()) {
347 properties->SetWithoutPathExpansion(
348 shill::kIPConfigProperty,
349 base::Value::CreateStringValue(ipconfig_path));
352 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
353 SortManagerServices();
356 void FakeShillServiceClient::RemoveService(const std::string& service_path) {
357 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
358 RemoveManagerService(service_path);
360 stub_services_.RemoveWithoutPathExpansion(service_path, NULL);
361 connect_behavior_.erase(service_path);
364 bool FakeShillServiceClient::SetServiceProperty(const std::string& service_path,
365 const std::string& property,
366 const base::Value& value) {
367 base::DictionaryValue* dict = NULL;
368 if (!stub_services_.GetDictionaryWithoutPathExpansion(service_path, &dict))
371 VLOG(1) << "Service.SetProperty: " << property << " = " << value
372 << " For: " << service_path;
374 base::DictionaryValue new_properties;
375 std::string changed_property;
376 bool case_sensitive = true;
377 if (StartsWithASCII(property, "Provider.", case_sensitive) ||
378 StartsWithASCII(property, "OpenVPN.", case_sensitive) ||
379 StartsWithASCII(property, "L2TPIPsec.", case_sensitive)) {
380 // These properties are only nested within the Provider dictionary if read
382 base::DictionaryValue* provider = new base::DictionaryValue;
383 provider->SetWithoutPathExpansion(property, value.DeepCopy());
384 new_properties.SetWithoutPathExpansion(shill::kProviderProperty, provider);
385 changed_property = shill::kProviderProperty;
387 new_properties.SetWithoutPathExpansion(property, value.DeepCopy());
388 changed_property = property;
391 dict->MergeDictionary(&new_properties);
393 // Notify the Manager if the state changed (affects DefaultService).
394 if (property == shill::kStateProperty) {
396 value.GetAsString(&state);
397 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
398 ServiceStateChanged(service_path, state);
401 // If the State changes, the sort order of Services may change and the
402 // DefaultService property may change.
403 if (property == shill::kStateProperty) {
404 base::MessageLoop::current()->PostTask(
405 FROM_HERE, base::Bind(&CallSortManagerServices));
408 // Notifiy Chrome of the property change.
409 base::MessageLoop::current()->PostTask(
411 base::Bind(&FakeShillServiceClient::NotifyObserversPropertyChanged,
412 weak_ptr_factory_.GetWeakPtr(),
413 dbus::ObjectPath(service_path), changed_property));
417 const base::DictionaryValue* FakeShillServiceClient::GetServiceProperties(
418 const std::string& service_path) const {
419 const base::DictionaryValue* properties = NULL;
420 stub_services_.GetDictionaryWithoutPathExpansion(service_path, &properties);
424 void FakeShillServiceClient::ClearServices() {
425 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
426 ClearManagerServices();
428 stub_services_.Clear();
429 connect_behavior_.clear();
432 void FakeShillServiceClient::SetConnectBehavior(const std::string& service_path,
433 const base::Closure& behavior) {
434 connect_behavior_[service_path] = behavior;
437 void FakeShillServiceClient::NotifyObserversPropertyChanged(
438 const dbus::ObjectPath& service_path,
439 const std::string& property) {
440 base::DictionaryValue* dict = NULL;
441 std::string path = service_path.value();
442 if (!stub_services_.GetDictionaryWithoutPathExpansion(path, &dict)) {
443 LOG(ERROR) << "Notify for unknown service: " << path;
446 base::Value* value = NULL;
447 if (!dict->GetWithoutPathExpansion(property, &value)) {
448 LOG(ERROR) << "Notify for unknown property: "
449 << path << " : " << property;
452 FOR_EACH_OBSERVER(ShillPropertyChangedObserver,
453 GetObserverList(service_path),
454 OnPropertyChanged(property, *value));
457 base::DictionaryValue* FakeShillServiceClient::GetModifiableServiceProperties(
458 const std::string& service_path, bool create_if_missing) {
459 base::DictionaryValue* properties = NULL;
460 if (!stub_services_.GetDictionaryWithoutPathExpansion(service_path,
463 properties = new base::DictionaryValue;
464 stub_services_.Set(service_path, properties);
469 FakeShillServiceClient::PropertyObserverList&
470 FakeShillServiceClient::GetObserverList(const dbus::ObjectPath& device_path) {
471 std::map<dbus::ObjectPath, PropertyObserverList*>::iterator iter =
472 observer_list_.find(device_path);
473 if (iter != observer_list_.end())
474 return *(iter->second);
475 PropertyObserverList* observer_list = new PropertyObserverList();
476 observer_list_[device_path] = observer_list;
477 return *observer_list;
480 void FakeShillServiceClient::SetOtherServicesOffline(
481 const std::string& service_path) {
482 const base::DictionaryValue* service_properties = GetServiceProperties(
484 if (!service_properties) {
485 LOG(ERROR) << "Missing service: " << service_path;
488 std::string service_type;
489 service_properties->GetString(shill::kTypeProperty, &service_type);
490 // Set all other services of the same type to offline (Idle).
491 for (base::DictionaryValue::Iterator iter(stub_services_);
492 !iter.IsAtEnd(); iter.Advance()) {
493 std::string path = iter.key();
494 if (path == service_path)
496 base::DictionaryValue* properties;
497 if (!stub_services_.GetDictionaryWithoutPathExpansion(path, &properties))
501 properties->GetString(shill::kTypeProperty, &type);
502 if (type != service_type)
504 properties->SetWithoutPathExpansion(
505 shill::kStateProperty,
506 base::Value::CreateStringValue(shill::kStateIdle));
510 void FakeShillServiceClient::SetCellularActivated(
511 const dbus::ObjectPath& service_path,
512 const ErrorCallback& error_callback) {
513 SetProperty(service_path,
514 shill::kActivationStateProperty,
515 base::StringValue(shill::kActivationStateActivated),
516 base::Bind(&base::DoNothing),
518 SetProperty(service_path,
519 shill::kConnectableProperty,
520 base::FundamentalValue(true),
521 base::Bind(&base::DoNothing),
525 void FakeShillServiceClient::ContinueConnect(
526 const std::string& service_path) {
527 VLOG(1) << "FakeShillServiceClient::ContinueConnect: " << service_path;
528 base::DictionaryValue* service_properties = NULL;
529 if (!stub_services_.GetDictionary(service_path, &service_properties)) {
530 LOG(ERROR) << "Service not found: " << service_path;
534 if (ContainsKey(connect_behavior_, service_path)) {
535 const base::Closure& custom_connect_behavior =
536 connect_behavior_[service_path];
537 custom_connect_behavior.Run();
541 // No custom connect behavior set, continue with the default connect behavior.
542 std::string passphrase;
543 service_properties->GetStringWithoutPathExpansion(
544 shill::kPassphraseProperty, &passphrase);
545 if (passphrase == "failure") {
546 // Simulate a password failure.
547 SetServiceProperty(service_path,
548 shill::kStateProperty,
549 base::StringValue(shill::kStateFailure));
550 base::MessageLoop::current()->PostTask(
553 base::IgnoreResult(&FakeShillServiceClient::SetServiceProperty),
554 weak_ptr_factory_.GetWeakPtr(),
556 shill::kErrorProperty,
557 base::StringValue(shill::kErrorBadPassphrase)));
560 SetServiceProperty(service_path,
561 shill::kStateProperty,
562 base::StringValue(shill::kStateOnline));
566 } // namespace chromeos