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/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"
21 #include "dbus/message.h"
22 #include "dbus/object_path.h"
23 #include "third_party/cros_system_api/dbus/service_constants.h"
29 void PassStubListValue(const ShillServiceClient::ListValueCallback
& callback
,
30 base::ListValue
* value
) {
34 void PassStubServiceProperties(
35 const ShillServiceClient::DictionaryValueCallback
& callback
,
36 DBusMethodCallStatus call_status
,
37 const base::DictionaryValue
* properties
) {
38 callback
.Run(call_status
, *properties
);
43 FakeShillServiceClient::FakeShillServiceClient() : weak_ptr_factory_(this) {
46 FakeShillServiceClient::~FakeShillServiceClient() {
47 STLDeleteContainerPairSecondPointers(
48 observer_list_
.begin(), observer_list_
.end());
52 // ShillServiceClient overrides.
54 void FakeShillServiceClient::Init(dbus::Bus
* bus
) {
57 void FakeShillServiceClient::AddPropertyChangedObserver(
58 const dbus::ObjectPath
& service_path
,
59 ShillPropertyChangedObserver
* observer
) {
60 GetObserverList(service_path
).AddObserver(observer
);
63 void FakeShillServiceClient::RemovePropertyChangedObserver(
64 const dbus::ObjectPath
& service_path
,
65 ShillPropertyChangedObserver
* observer
) {
66 GetObserverList(service_path
).RemoveObserver(observer
);
69 void FakeShillServiceClient::GetProperties(
70 const dbus::ObjectPath
& service_path
,
71 const DictionaryValueCallback
& callback
) {
72 base::DictionaryValue
* nested_dict
= NULL
;
73 scoped_ptr
<base::DictionaryValue
> result_properties
;
74 DBusMethodCallStatus call_status
;
75 stub_services_
.GetDictionaryWithoutPathExpansion(service_path
.value(),
78 result_properties
.reset(nested_dict
->DeepCopy());
79 // Remove credentials that Shill wouldn't send.
80 result_properties
->RemoveWithoutPathExpansion(shill::kPassphraseProperty
,
82 call_status
= DBUS_METHOD_CALL_SUCCESS
;
84 LOG(ERROR
) << "Properties not found for: " << service_path
.value();
85 result_properties
.reset(new base::DictionaryValue
);
86 call_status
= DBUS_METHOD_CALL_FAILURE
;
89 base::MessageLoop::current()->PostTask(
91 base::Bind(&PassStubServiceProperties
,
94 base::Owned(result_properties
.release())));
97 void FakeShillServiceClient::SetProperty(const dbus::ObjectPath
& service_path
,
98 const std::string
& name
,
99 const base::Value
& value
,
100 const base::Closure
& callback
,
101 const ErrorCallback
& error_callback
) {
102 if (!SetServiceProperty(service_path
.value(), name
, value
)) {
103 LOG(ERROR
) << "Service not found: " << service_path
.value();
104 error_callback
.Run("Error.InvalidService", "Invalid Service");
107 base::MessageLoop::current()->PostTask(FROM_HERE
, callback
);
110 void FakeShillServiceClient::SetProperties(
111 const dbus::ObjectPath
& service_path
,
112 const base::DictionaryValue
& properties
,
113 const base::Closure
& callback
,
114 const ErrorCallback
& error_callback
) {
115 for (base::DictionaryValue::Iterator
iter(properties
);
116 !iter
.IsAtEnd(); iter
.Advance()) {
117 if (!SetServiceProperty(service_path
.value(), iter
.key(), iter
.value())) {
118 LOG(ERROR
) << "Service not found: " << service_path
.value();
119 error_callback
.Run("Error.InvalidService", "Invalid Service");
123 base::MessageLoop::current()->PostTask(FROM_HERE
, callback
);
126 void FakeShillServiceClient::ClearProperty(
127 const dbus::ObjectPath
& service_path
,
128 const std::string
& name
,
129 const base::Closure
& callback
,
130 const ErrorCallback
& error_callback
) {
131 base::DictionaryValue
* dict
= NULL
;
132 if (!stub_services_
.GetDictionaryWithoutPathExpansion(
133 service_path
.value(), &dict
)) {
134 error_callback
.Run("Error.InvalidService", "Invalid Service");
137 dict
->RemoveWithoutPathExpansion(name
, NULL
);
138 // Note: Shill does not send notifications when properties are cleared.
139 base::MessageLoop::current()->PostTask(FROM_HERE
, callback
);
142 void FakeShillServiceClient::ClearProperties(
143 const dbus::ObjectPath
& service_path
,
144 const std::vector
<std::string
>& names
,
145 const ListValueCallback
& callback
,
146 const ErrorCallback
& error_callback
) {
147 base::DictionaryValue
* dict
= NULL
;
148 if (!stub_services_
.GetDictionaryWithoutPathExpansion(
149 service_path
.value(), &dict
)) {
150 error_callback
.Run("Error.InvalidService", "Invalid Service");
153 scoped_ptr
<base::ListValue
> results(new base::ListValue
);
154 for (std::vector
<std::string
>::const_iterator iter
= names
.begin();
155 iter
!= names
.end(); ++iter
) {
156 dict
->RemoveWithoutPathExpansion(*iter
, NULL
);
157 // Note: Shill does not send notifications when properties are cleared.
158 results
->AppendBoolean(true);
160 base::MessageLoop::current()->PostTask(
162 base::Bind(&PassStubListValue
,
163 callback
, base::Owned(results
.release())));
166 void FakeShillServiceClient::Connect(const dbus::ObjectPath
& service_path
,
167 const base::Closure
& callback
,
168 const ErrorCallback
& error_callback
) {
169 VLOG(1) << "FakeShillServiceClient::Connect: " << service_path
.value();
170 base::DictionaryValue
* service_properties
= NULL
;
171 if (!stub_services_
.GetDictionary(
172 service_path
.value(), &service_properties
)) {
173 LOG(ERROR
) << "Service not found: " << service_path
.value();
174 error_callback
.Run("Error.InvalidService", "Invalid Service");
178 // Set any other services of the same Type to 'offline' first, before setting
179 // State to Association which will trigger sorting Manager.Services and
180 // sending an update.
181 SetOtherServicesOffline(service_path
.value());
184 base::StringValue
associating_value(shill::kStateAssociation
);
185 SetServiceProperty(service_path
.value(),
186 shill::kStateProperty
,
189 // Stay Associating until the state is changed again after a delay.
190 base::TimeDelta delay
;
191 if (CommandLine::ForCurrentProcess()->HasSwitch(
192 chromeos::switches::kEnableStubInteractive
)) {
193 const int kConnectDelaySeconds
= 5;
194 delay
= base::TimeDelta::FromSeconds(kConnectDelaySeconds
);
196 base::MessageLoop::current()->PostDelayedTask(
198 base::Bind(&FakeShillServiceClient::ContinueConnect
,
199 weak_ptr_factory_
.GetWeakPtr(),
200 service_path
.value()),
206 void FakeShillServiceClient::Disconnect(const dbus::ObjectPath
& service_path
,
207 const base::Closure
& callback
,
208 const ErrorCallback
& error_callback
) {
209 base::Value
* service
;
210 if (!stub_services_
.Get(service_path
.value(), &service
)) {
211 error_callback
.Run("Error.InvalidService", "Invalid Service");
214 base::TimeDelta delay
;
215 if (CommandLine::ForCurrentProcess()->HasSwitch(
216 chromeos::switches::kEnableStubInteractive
)) {
217 const int kConnectDelaySeconds
= 2;
218 delay
= base::TimeDelta::FromSeconds(kConnectDelaySeconds
);
220 // Set Idle after a delay
221 base::StringValue
idle_value(shill::kStateIdle
);
222 base::MessageLoop::current()->PostDelayedTask(
224 base::Bind(&FakeShillServiceClient::SetProperty
,
225 weak_ptr_factory_
.GetWeakPtr(),
227 shill::kStateProperty
,
229 base::Bind(&base::DoNothing
),
235 void FakeShillServiceClient::Remove(const dbus::ObjectPath
& service_path
,
236 const base::Closure
& callback
,
237 const ErrorCallback
& error_callback
) {
238 base::MessageLoop::current()->PostTask(FROM_HERE
, callback
);
241 void FakeShillServiceClient::ActivateCellularModem(
242 const dbus::ObjectPath
& service_path
,
243 const std::string
& carrier
,
244 const base::Closure
& callback
,
245 const ErrorCallback
& error_callback
) {
246 base::DictionaryValue
* service_properties
=
247 GetModifiableServiceProperties(service_path
.value(), false);
248 if (!service_properties
) {
249 LOG(ERROR
) << "Service not found: " << service_path
.value();
250 error_callback
.Run("Error.InvalidService", "Invalid Service");
252 SetServiceProperty(service_path
.value(),
253 shill::kActivationStateProperty
,
254 base::StringValue(shill::kActivationStateActivating
));
255 base::TimeDelta delay
;
256 if (CommandLine::ForCurrentProcess()->HasSwitch(
257 chromeos::switches::kEnableStubInteractive
)) {
258 const int kConnectDelaySeconds
= 2;
259 delay
= base::TimeDelta::FromSeconds(kConnectDelaySeconds
);
261 // Set Activated after a delay
262 base::MessageLoop::current()->PostDelayedTask(
264 base::Bind(&FakeShillServiceClient::SetCellularActivated
,
265 weak_ptr_factory_
.GetWeakPtr(),
270 base::MessageLoop::current()->PostTask(FROM_HERE
, callback
);
273 void FakeShillServiceClient::CompleteCellularActivation(
274 const dbus::ObjectPath
& service_path
,
275 const base::Closure
& callback
,
276 const ErrorCallback
& error_callback
) {
277 base::MessageLoop::current()->PostTask(FROM_HERE
, callback
);
280 void FakeShillServiceClient::GetLoadableProfileEntries(
281 const dbus::ObjectPath
& service_path
,
282 const DictionaryValueCallback
& callback
) {
283 // Provide a dictionary with a single { profile_path, service_path } entry
284 // if the Profile property is set, or an empty dictionary.
285 scoped_ptr
<base::DictionaryValue
> result_properties(
286 new base::DictionaryValue
);
287 base::DictionaryValue
* service_properties
=
288 GetModifiableServiceProperties(service_path
.value(), false);
289 if (service_properties
) {
290 std::string profile_path
;
291 if (service_properties
->GetStringWithoutPathExpansion(
292 shill::kProfileProperty
, &profile_path
)) {
293 result_properties
->SetStringWithoutPathExpansion(
294 profile_path
, service_path
.value());
297 LOG(WARNING
) << "Service not in profile: " << service_path
.value();
300 DBusMethodCallStatus call_status
= DBUS_METHOD_CALL_SUCCESS
;
301 base::MessageLoop::current()->PostTask(
303 base::Bind(&PassStubServiceProperties
,
306 base::Owned(result_properties
.release())));
309 ShillServiceClient::TestInterface
* FakeShillServiceClient::GetTestInterface() {
313 // ShillServiceClient::TestInterface overrides.
315 void FakeShillServiceClient::AddService(const std::string
& service_path
,
316 const std::string
& name
,
317 const std::string
& type
,
318 const std::string
& state
,
319 bool add_to_visible_list
,
320 bool add_to_watch_list
) {
321 std::string nstate
= state
;
322 if (CommandLine::ForCurrentProcess()->HasSwitch(
323 chromeos::switches::kDefaultStubNetworkStateIdle
)) {
324 nstate
= shill::kStateIdle
;
326 AddServiceWithIPConfig(service_path
, name
, type
, nstate
, "",
327 add_to_visible_list
, add_to_watch_list
);
330 void FakeShillServiceClient::AddServiceWithIPConfig(
331 const std::string
& service_path
,
332 const std::string
& name
,
333 const std::string
& type
,
334 const std::string
& state
,
335 const std::string
& ipconfig_path
,
336 bool add_to_visible_list
,
337 bool add_to_watch_list
) {
338 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
339 AddManagerService(service_path
, add_to_visible_list
, add_to_watch_list
);
341 base::DictionaryValue
* properties
=
342 GetModifiableServiceProperties(service_path
, true);
343 connect_behavior_
.erase(service_path
);
344 shill_property_util::SetSSID(name
, properties
);
345 properties
->SetWithoutPathExpansion(
346 shill::kNameProperty
,
347 base::Value::CreateStringValue(name
));
348 properties
->SetWithoutPathExpansion(
349 shill::kDeviceProperty
,
350 base::Value::CreateStringValue(
351 shill_stub_helper::DevicePathForType(type
)));
352 properties
->SetWithoutPathExpansion(
353 shill::kTypeProperty
,
354 base::Value::CreateStringValue(type
));
355 properties
->SetWithoutPathExpansion(
356 shill::kStateProperty
,
357 base::Value::CreateStringValue(state
));
358 if (!ipconfig_path
.empty())
359 properties
->SetWithoutPathExpansion(
360 shill::kIPConfigProperty
,
361 base::Value::CreateStringValue(ipconfig_path
));
364 void FakeShillServiceClient::RemoveService(const std::string
& service_path
) {
365 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
366 RemoveManagerService(service_path
);
368 stub_services_
.RemoveWithoutPathExpansion(service_path
, NULL
);
369 connect_behavior_
.erase(service_path
);
372 bool FakeShillServiceClient::SetServiceProperty(const std::string
& service_path
,
373 const std::string
& property
,
374 const base::Value
& value
) {
375 base::DictionaryValue
* dict
= NULL
;
376 if (!stub_services_
.GetDictionaryWithoutPathExpansion(service_path
, &dict
))
379 VLOG(1) << "Service.SetProperty: " << property
<< " = " << value
380 << " For: " << service_path
;
382 base::DictionaryValue new_properties
;
383 std::string changed_property
;
384 bool case_sensitive
= true;
385 if (StartsWithASCII(property
, "Provider.", case_sensitive
) ||
386 StartsWithASCII(property
, "OpenVPN.", case_sensitive
) ||
387 StartsWithASCII(property
, "L2TPIPsec.", case_sensitive
)) {
388 // These properties are only nested within the Provider dictionary if read
390 base::DictionaryValue
* provider
= new base::DictionaryValue
;
391 provider
->SetWithoutPathExpansion(property
, value
.DeepCopy());
392 new_properties
.SetWithoutPathExpansion(shill::kProviderProperty
, provider
);
393 changed_property
= shill::kProviderProperty
;
395 new_properties
.SetWithoutPathExpansion(property
, value
.DeepCopy());
396 changed_property
= property
;
399 dict
->MergeDictionary(&new_properties
);
401 if (property
== shill::kStateProperty
) {
402 // When State changes the sort order of Services may change.
403 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
404 SortManagerServices();
407 base::MessageLoop::current()->PostTask(
409 base::Bind(&FakeShillServiceClient::NotifyObserversPropertyChanged
,
410 weak_ptr_factory_
.GetWeakPtr(),
411 dbus::ObjectPath(service_path
), changed_property
));
415 const base::DictionaryValue
* FakeShillServiceClient::GetServiceProperties(
416 const std::string
& service_path
) const {
417 const base::DictionaryValue
* properties
= NULL
;
418 stub_services_
.GetDictionaryWithoutPathExpansion(service_path
, &properties
);
422 void FakeShillServiceClient::ClearServices() {
423 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
424 ClearManagerServices();
426 stub_services_
.Clear();
427 connect_behavior_
.clear();
430 void FakeShillServiceClient::SetConnectBehavior(const std::string
& service_path
,
431 const base::Closure
& behavior
) {
432 connect_behavior_
[service_path
] = behavior
;
435 void FakeShillServiceClient::NotifyObserversPropertyChanged(
436 const dbus::ObjectPath
& service_path
,
437 const std::string
& property
) {
438 base::DictionaryValue
* dict
= NULL
;
439 std::string path
= service_path
.value();
440 if (!stub_services_
.GetDictionaryWithoutPathExpansion(path
, &dict
)) {
441 LOG(ERROR
) << "Notify for unknown service: " << path
;
444 base::Value
* value
= NULL
;
445 if (!dict
->GetWithoutPathExpansion(property
, &value
)) {
446 LOG(ERROR
) << "Notify for unknown property: "
447 << path
<< " : " << property
;
450 FOR_EACH_OBSERVER(ShillPropertyChangedObserver
,
451 GetObserverList(service_path
),
452 OnPropertyChanged(property
, *value
));
455 base::DictionaryValue
* FakeShillServiceClient::GetModifiableServiceProperties(
456 const std::string
& service_path
, bool create_if_missing
) {
457 base::DictionaryValue
* properties
= NULL
;
458 if (!stub_services_
.GetDictionaryWithoutPathExpansion(service_path
,
461 properties
= new base::DictionaryValue
;
462 stub_services_
.Set(service_path
, properties
);
467 FakeShillServiceClient::PropertyObserverList
&
468 FakeShillServiceClient::GetObserverList(const dbus::ObjectPath
& device_path
) {
469 std::map
<dbus::ObjectPath
, PropertyObserverList
*>::iterator iter
=
470 observer_list_
.find(device_path
);
471 if (iter
!= observer_list_
.end())
472 return *(iter
->second
);
473 PropertyObserverList
* observer_list
= new PropertyObserverList();
474 observer_list_
[device_path
] = observer_list
;
475 return *observer_list
;
478 void FakeShillServiceClient::SetOtherServicesOffline(
479 const std::string
& service_path
) {
480 const base::DictionaryValue
* service_properties
= GetServiceProperties(
482 if (!service_properties
) {
483 LOG(ERROR
) << "Missing service: " << service_path
;
486 std::string service_type
;
487 service_properties
->GetString(shill::kTypeProperty
, &service_type
);
488 // Set all other services of the same type to offline (Idle).
489 for (base::DictionaryValue::Iterator
iter(stub_services_
);
490 !iter
.IsAtEnd(); iter
.Advance()) {
491 std::string path
= iter
.key();
492 if (path
== service_path
)
494 base::DictionaryValue
* properties
;
495 if (!stub_services_
.GetDictionaryWithoutPathExpansion(path
, &properties
))
499 properties
->GetString(shill::kTypeProperty
, &type
);
500 if (type
!= service_type
)
502 properties
->SetWithoutPathExpansion(
503 shill::kStateProperty
,
504 base::Value::CreateStringValue(shill::kStateIdle
));
508 void FakeShillServiceClient::SetCellularActivated(
509 const dbus::ObjectPath
& service_path
,
510 const ErrorCallback
& error_callback
) {
511 SetProperty(service_path
,
512 shill::kActivationStateProperty
,
513 base::StringValue(shill::kActivationStateActivated
),
514 base::Bind(&base::DoNothing
),
516 SetProperty(service_path
,
517 shill::kConnectableProperty
,
518 base::FundamentalValue(true),
519 base::Bind(&base::DoNothing
),
523 void FakeShillServiceClient::ContinueConnect(
524 const std::string
& service_path
) {
525 VLOG(1) << "FakeShillServiceClient::ContinueConnect: " << service_path
;
526 base::DictionaryValue
* service_properties
= NULL
;
527 if (!stub_services_
.GetDictionary(service_path
, &service_properties
)) {
528 LOG(ERROR
) << "Service not found: " << service_path
;
532 if (ContainsKey(connect_behavior_
, service_path
)) {
533 const base::Closure
& custom_connect_behavior
=
534 connect_behavior_
[service_path
];
535 custom_connect_behavior
.Run();
539 // No custom connect behavior set, continue with the default connect behavior.
540 std::string passphrase
;
541 service_properties
->GetStringWithoutPathExpansion(
542 shill::kPassphraseProperty
, &passphrase
);
543 if (passphrase
== "failure") {
544 // Simulate a password failure.
545 SetServiceProperty(service_path
,
546 shill::kStateProperty
,
547 base::StringValue(shill::kStateFailure
));
548 base::MessageLoop::current()->PostTask(
551 base::IgnoreResult(&FakeShillServiceClient::SetServiceProperty
),
552 weak_ptr_factory_
.GetWeakPtr(),
554 shill::kErrorProperty
,
555 base::StringValue(shill::kErrorBadPassphrase
)));
558 SetServiceProperty(service_path
,
559 shill::kStateProperty
,
560 base::StringValue(shill::kStateOnline
));
564 } // namespace chromeos