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 LOG(ERROR
) << "Properties not found for: " << service_path
.value();
93 result_properties
.reset(new base::DictionaryValue
);
94 call_status
= DBUS_METHOD_CALL_FAILURE
;
97 base::MessageLoop::current()->PostTask(
99 base::Bind(&PassStubServiceProperties
,
102 base::Owned(result_properties
.release())));
105 void FakeShillServiceClient::SetProperty(const dbus::ObjectPath
& service_path
,
106 const std::string
& name
,
107 const base::Value
& value
,
108 const base::Closure
& callback
,
109 const ErrorCallback
& error_callback
) {
110 if (!SetServiceProperty(service_path
.value(), name
, value
)) {
111 LOG(ERROR
) << "Service not found: " << service_path
.value();
112 error_callback
.Run("Error.InvalidService", "Invalid Service");
115 base::MessageLoop::current()->PostTask(FROM_HERE
, callback
);
118 void FakeShillServiceClient::SetProperties(
119 const dbus::ObjectPath
& service_path
,
120 const base::DictionaryValue
& properties
,
121 const base::Closure
& callback
,
122 const ErrorCallback
& error_callback
) {
123 for (base::DictionaryValue::Iterator
iter(properties
);
124 !iter
.IsAtEnd(); iter
.Advance()) {
125 if (!SetServiceProperty(service_path
.value(), iter
.key(), iter
.value())) {
126 LOG(ERROR
) << "Service not found: " << service_path
.value();
127 error_callback
.Run("Error.InvalidService", "Invalid Service");
131 base::MessageLoop::current()->PostTask(FROM_HERE
, callback
);
134 void FakeShillServiceClient::ClearProperty(
135 const dbus::ObjectPath
& service_path
,
136 const std::string
& name
,
137 const base::Closure
& callback
,
138 const ErrorCallback
& error_callback
) {
139 base::DictionaryValue
* dict
= NULL
;
140 if (!stub_services_
.GetDictionaryWithoutPathExpansion(
141 service_path
.value(), &dict
)) {
142 error_callback
.Run("Error.InvalidService", "Invalid Service");
145 dict
->RemoveWithoutPathExpansion(name
, NULL
);
146 // Note: Shill does not send notifications when properties are cleared.
147 base::MessageLoop::current()->PostTask(FROM_HERE
, callback
);
150 void FakeShillServiceClient::ClearProperties(
151 const dbus::ObjectPath
& service_path
,
152 const std::vector
<std::string
>& names
,
153 const ListValueCallback
& callback
,
154 const ErrorCallback
& error_callback
) {
155 base::DictionaryValue
* dict
= NULL
;
156 if (!stub_services_
.GetDictionaryWithoutPathExpansion(
157 service_path
.value(), &dict
)) {
158 error_callback
.Run("Error.InvalidService", "Invalid Service");
161 scoped_ptr
<base::ListValue
> results(new base::ListValue
);
162 for (std::vector
<std::string
>::const_iterator iter
= names
.begin();
163 iter
!= names
.end(); ++iter
) {
164 dict
->RemoveWithoutPathExpansion(*iter
, NULL
);
165 // Note: Shill does not send notifications when properties are cleared.
166 results
->AppendBoolean(true);
168 base::MessageLoop::current()->PostTask(
170 base::Bind(&PassStubListValue
,
171 callback
, base::Owned(results
.release())));
174 void FakeShillServiceClient::Connect(const dbus::ObjectPath
& service_path
,
175 const base::Closure
& callback
,
176 const ErrorCallback
& error_callback
) {
177 VLOG(1) << "FakeShillServiceClient::Connect: " << service_path
.value();
178 base::DictionaryValue
* service_properties
= NULL
;
179 if (!stub_services_
.GetDictionary(
180 service_path
.value(), &service_properties
)) {
181 LOG(ERROR
) << "Service not found: " << service_path
.value();
182 error_callback
.Run("Error.InvalidService", "Invalid Service");
186 // Set any other services of the same Type to 'offline' first, before setting
187 // State to Association which will trigger sorting Manager.Services and
188 // sending an update.
189 SetOtherServicesOffline(service_path
.value());
192 base::StringValue
associating_value(shill::kStateAssociation
);
193 SetServiceProperty(service_path
.value(),
194 shill::kStateProperty
,
197 // Stay Associating until the state is changed again after a delay.
198 base::MessageLoop::current()->PostDelayedTask(
200 base::Bind(&FakeShillServiceClient::ContinueConnect
,
201 weak_ptr_factory_
.GetWeakPtr(),
202 service_path
.value()),
203 base::TimeDelta::FromSeconds(GetInteractiveDelay()));
208 void FakeShillServiceClient::Disconnect(const dbus::ObjectPath
& service_path
,
209 const base::Closure
& callback
,
210 const ErrorCallback
& error_callback
) {
211 base::Value
* service
;
212 if (!stub_services_
.Get(service_path
.value(), &service
)) {
213 error_callback
.Run("Error.InvalidService", "Invalid Service");
216 // Set Idle after a delay
217 base::StringValue
idle_value(shill::kStateIdle
);
218 base::MessageLoop::current()->PostDelayedTask(
220 base::Bind(&FakeShillServiceClient::SetProperty
,
221 weak_ptr_factory_
.GetWeakPtr(),
223 shill::kStateProperty
,
225 base::Bind(&base::DoNothing
),
227 base::TimeDelta::FromSeconds(GetInteractiveDelay()));
231 void FakeShillServiceClient::Remove(const dbus::ObjectPath
& service_path
,
232 const base::Closure
& callback
,
233 const ErrorCallback
& error_callback
) {
234 base::MessageLoop::current()->PostTask(FROM_HERE
, callback
);
237 void FakeShillServiceClient::ActivateCellularModem(
238 const dbus::ObjectPath
& service_path
,
239 const std::string
& carrier
,
240 const base::Closure
& callback
,
241 const ErrorCallback
& error_callback
) {
242 base::DictionaryValue
* service_properties
=
243 GetModifiableServiceProperties(service_path
.value(), false);
244 if (!service_properties
) {
245 LOG(ERROR
) << "Service not found: " << service_path
.value();
246 error_callback
.Run("Error.InvalidService", "Invalid Service");
248 SetServiceProperty(service_path
.value(),
249 shill::kActivationStateProperty
,
250 base::StringValue(shill::kActivationStateActivating
));
251 // Set Activated after a delay
252 base::MessageLoop::current()->PostDelayedTask(
254 base::Bind(&FakeShillServiceClient::SetCellularActivated
,
255 weak_ptr_factory_
.GetWeakPtr(),
258 base::TimeDelta::FromSeconds(GetInteractiveDelay()));
260 base::MessageLoop::current()->PostTask(FROM_HERE
, callback
);
263 void FakeShillServiceClient::CompleteCellularActivation(
264 const dbus::ObjectPath
& service_path
,
265 const base::Closure
& callback
,
266 const ErrorCallback
& error_callback
) {
267 base::MessageLoop::current()->PostTask(FROM_HERE
, callback
);
270 void FakeShillServiceClient::GetLoadableProfileEntries(
271 const dbus::ObjectPath
& service_path
,
272 const DictionaryValueCallback
& callback
) {
273 // Provide a dictionary with a single { profile_path, service_path } entry
274 // if the Profile property is set, or an empty dictionary.
275 scoped_ptr
<base::DictionaryValue
> result_properties(
276 new base::DictionaryValue
);
277 base::DictionaryValue
* service_properties
=
278 GetModifiableServiceProperties(service_path
.value(), false);
279 if (service_properties
) {
280 std::string profile_path
;
281 if (service_properties
->GetStringWithoutPathExpansion(
282 shill::kProfileProperty
, &profile_path
)) {
283 result_properties
->SetStringWithoutPathExpansion(
284 profile_path
, service_path
.value());
287 LOG(WARNING
) << "Service not in profile: " << service_path
.value();
290 DBusMethodCallStatus call_status
= DBUS_METHOD_CALL_SUCCESS
;
291 base::MessageLoop::current()->PostTask(
293 base::Bind(&PassStubServiceProperties
,
296 base::Owned(result_properties
.release())));
299 ShillServiceClient::TestInterface
* FakeShillServiceClient::GetTestInterface() {
303 // ShillServiceClient::TestInterface overrides.
305 void FakeShillServiceClient::AddService(const std::string
& service_path
,
306 const std::string
& name
,
307 const std::string
& type
,
308 const std::string
& state
,
309 bool add_to_visible_list
,
310 bool add_to_watch_list
) {
311 AddServiceWithIPConfig(service_path
, name
, type
, state
, "",
312 add_to_visible_list
, add_to_watch_list
);
315 void FakeShillServiceClient::AddServiceWithIPConfig(
316 const std::string
& service_path
,
317 const std::string
& name
,
318 const std::string
& type
,
319 const std::string
& state
,
320 const std::string
& ipconfig_path
,
321 bool add_to_visible_list
,
322 bool add_to_watch_list
) {
323 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
324 AddManagerService(service_path
, add_to_visible_list
, add_to_watch_list
);
325 std::string device_path
=
326 DBusThreadManager::Get()->GetShillDeviceClient()->GetTestInterface()->
327 GetDevicePathForType(type
);
329 base::DictionaryValue
* properties
=
330 GetModifiableServiceProperties(service_path
, true);
331 connect_behavior_
.erase(service_path
);
332 shill_property_util::SetSSID(name
, properties
);
333 properties
->SetWithoutPathExpansion(
334 shill::kNameProperty
,
335 base::Value::CreateStringValue(name
));
336 properties
->SetWithoutPathExpansion(
337 shill::kDeviceProperty
,
338 base::Value::CreateStringValue(device_path
));
339 properties
->SetWithoutPathExpansion(
340 shill::kTypeProperty
,
341 base::Value::CreateStringValue(type
));
342 properties
->SetWithoutPathExpansion(
343 shill::kStateProperty
,
344 base::Value::CreateStringValue(state
));
345 if (!ipconfig_path
.empty()) {
346 properties
->SetWithoutPathExpansion(
347 shill::kIPConfigProperty
,
348 base::Value::CreateStringValue(ipconfig_path
));
351 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
352 SortManagerServices();
355 void FakeShillServiceClient::RemoveService(const std::string
& service_path
) {
356 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
357 RemoveManagerService(service_path
);
359 stub_services_
.RemoveWithoutPathExpansion(service_path
, NULL
);
360 connect_behavior_
.erase(service_path
);
363 bool FakeShillServiceClient::SetServiceProperty(const std::string
& service_path
,
364 const std::string
& property
,
365 const base::Value
& value
) {
366 base::DictionaryValue
* dict
= NULL
;
367 if (!stub_services_
.GetDictionaryWithoutPathExpansion(service_path
, &dict
))
370 VLOG(1) << "Service.SetProperty: " << property
<< " = " << value
371 << " For: " << service_path
;
373 base::DictionaryValue new_properties
;
374 std::string changed_property
;
375 bool case_sensitive
= true;
376 if (StartsWithASCII(property
, "Provider.", case_sensitive
) ||
377 StartsWithASCII(property
, "OpenVPN.", case_sensitive
) ||
378 StartsWithASCII(property
, "L2TPIPsec.", case_sensitive
)) {
379 // These properties are only nested within the Provider dictionary if read
381 base::DictionaryValue
* provider
= new base::DictionaryValue
;
382 provider
->SetWithoutPathExpansion(property
, value
.DeepCopy());
383 new_properties
.SetWithoutPathExpansion(shill::kProviderProperty
, provider
);
384 changed_property
= shill::kProviderProperty
;
386 new_properties
.SetWithoutPathExpansion(property
, value
.DeepCopy());
387 changed_property
= property
;
390 dict
->MergeDictionary(&new_properties
);
392 // Notify the Manager if the state changed (affects DefaultService).
393 if (property
== shill::kStateProperty
) {
395 value
.GetAsString(&state
);
396 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
397 ServiceStateChanged(service_path
, state
);
400 // If the State changes, the sort order of Services may change and the
401 // DefaultService property may change.
402 if (property
== shill::kStateProperty
) {
403 base::MessageLoop::current()->PostTask(
404 FROM_HERE
, base::Bind(&CallSortManagerServices
));
407 // Notifiy Chrome of the property change.
408 base::MessageLoop::current()->PostTask(
410 base::Bind(&FakeShillServiceClient::NotifyObserversPropertyChanged
,
411 weak_ptr_factory_
.GetWeakPtr(),
412 dbus::ObjectPath(service_path
), changed_property
));
416 const base::DictionaryValue
* FakeShillServiceClient::GetServiceProperties(
417 const std::string
& service_path
) const {
418 const base::DictionaryValue
* properties
= NULL
;
419 stub_services_
.GetDictionaryWithoutPathExpansion(service_path
, &properties
);
423 void FakeShillServiceClient::ClearServices() {
424 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
425 ClearManagerServices();
427 stub_services_
.Clear();
428 connect_behavior_
.clear();
431 void FakeShillServiceClient::SetConnectBehavior(const std::string
& service_path
,
432 const base::Closure
& behavior
) {
433 connect_behavior_
[service_path
] = behavior
;
436 void FakeShillServiceClient::NotifyObserversPropertyChanged(
437 const dbus::ObjectPath
& service_path
,
438 const std::string
& property
) {
439 base::DictionaryValue
* dict
= NULL
;
440 std::string path
= service_path
.value();
441 if (!stub_services_
.GetDictionaryWithoutPathExpansion(path
, &dict
)) {
442 LOG(ERROR
) << "Notify for unknown service: " << path
;
445 base::Value
* value
= NULL
;
446 if (!dict
->GetWithoutPathExpansion(property
, &value
)) {
447 LOG(ERROR
) << "Notify for unknown property: "
448 << path
<< " : " << property
;
451 FOR_EACH_OBSERVER(ShillPropertyChangedObserver
,
452 GetObserverList(service_path
),
453 OnPropertyChanged(property
, *value
));
456 base::DictionaryValue
* FakeShillServiceClient::GetModifiableServiceProperties(
457 const std::string
& service_path
, bool create_if_missing
) {
458 base::DictionaryValue
* properties
= NULL
;
459 if (!stub_services_
.GetDictionaryWithoutPathExpansion(service_path
,
462 properties
= new base::DictionaryValue
;
463 stub_services_
.Set(service_path
, properties
);
468 FakeShillServiceClient::PropertyObserverList
&
469 FakeShillServiceClient::GetObserverList(const dbus::ObjectPath
& device_path
) {
470 std::map
<dbus::ObjectPath
, PropertyObserverList
*>::iterator iter
=
471 observer_list_
.find(device_path
);
472 if (iter
!= observer_list_
.end())
473 return *(iter
->second
);
474 PropertyObserverList
* observer_list
= new PropertyObserverList();
475 observer_list_
[device_path
] = observer_list
;
476 return *observer_list
;
479 void FakeShillServiceClient::SetOtherServicesOffline(
480 const std::string
& service_path
) {
481 const base::DictionaryValue
* service_properties
= GetServiceProperties(
483 if (!service_properties
) {
484 LOG(ERROR
) << "Missing service: " << service_path
;
487 std::string service_type
;
488 service_properties
->GetString(shill::kTypeProperty
, &service_type
);
489 // Set all other services of the same type to offline (Idle).
490 for (base::DictionaryValue::Iterator
iter(stub_services_
);
491 !iter
.IsAtEnd(); iter
.Advance()) {
492 std::string path
= iter
.key();
493 if (path
== service_path
)
495 base::DictionaryValue
* properties
;
496 if (!stub_services_
.GetDictionaryWithoutPathExpansion(path
, &properties
))
500 properties
->GetString(shill::kTypeProperty
, &type
);
501 if (type
!= service_type
)
503 properties
->SetWithoutPathExpansion(
504 shill::kStateProperty
,
505 base::Value::CreateStringValue(shill::kStateIdle
));
509 void FakeShillServiceClient::SetCellularActivated(
510 const dbus::ObjectPath
& service_path
,
511 const ErrorCallback
& error_callback
) {
512 SetProperty(service_path
,
513 shill::kActivationStateProperty
,
514 base::StringValue(shill::kActivationStateActivated
),
515 base::Bind(&base::DoNothing
),
517 SetProperty(service_path
,
518 shill::kConnectableProperty
,
519 base::FundamentalValue(true),
520 base::Bind(&base::DoNothing
),
524 void FakeShillServiceClient::ContinueConnect(
525 const std::string
& service_path
) {
526 VLOG(1) << "FakeShillServiceClient::ContinueConnect: " << service_path
;
527 base::DictionaryValue
* service_properties
= NULL
;
528 if (!stub_services_
.GetDictionary(service_path
, &service_properties
)) {
529 LOG(ERROR
) << "Service not found: " << service_path
;
533 if (ContainsKey(connect_behavior_
, service_path
)) {
534 const base::Closure
& custom_connect_behavior
=
535 connect_behavior_
[service_path
];
536 custom_connect_behavior
.Run();
540 // No custom connect behavior set, continue with the default connect behavior.
541 std::string passphrase
;
542 service_properties
->GetStringWithoutPathExpansion(
543 shill::kPassphraseProperty
, &passphrase
);
544 if (passphrase
== "failure") {
545 // Simulate a password failure.
546 SetServiceProperty(service_path
,
547 shill::kStateProperty
,
548 base::StringValue(shill::kStateFailure
));
549 base::MessageLoop::current()->PostTask(
552 base::IgnoreResult(&FakeShillServiceClient::SetServiceProperty
),
553 weak_ptr_factory_
.GetWeakPtr(),
555 shill::kErrorProperty
,
556 base::StringValue(shill::kErrorBadPassphrase
)));
559 SetServiceProperty(service_path
,
560 shill::kStateProperty
,
561 base::StringValue(shill::kStateOnline
));
565 } // namespace chromeos