1 // Copyright (c) 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/shill_service_client_stub.h"
8 #include "base/command_line.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/chromeos_switches.h"
14 #include "chromeos/dbus/dbus_thread_manager.h"
15 #include "chromeos/dbus/shill_manager_client.h"
16 #include "chromeos/dbus/shill_profile_client_stub.h"
17 #include "chromeos/dbus/shill_property_changed_observer.h"
19 #include "dbus/message.h"
20 #include "dbus/object_proxy.h"
21 #include "third_party/cros_system_api/dbus/service_constants.h"
27 const char kStubPortalledWifiPath
[] = "portalled_wifi";
28 const char kStubPortalledWifiName
[] = "Portalled Wifi";
30 void ErrorFunction(const std::string
& error_name
,
31 const std::string
& error_message
) {
32 LOG(ERROR
) << "Shill Error: " << error_name
<< " : " << error_message
;
35 void PassStubListValue(const ShillServiceClient::ListValueCallback
& callback
,
36 base::ListValue
* value
) {
40 void PassStubServiceProperties(
41 const ShillServiceClient::DictionaryValueCallback
& callback
,
42 DBusMethodCallStatus call_status
,
43 const base::DictionaryValue
* properties
) {
44 callback
.Run(call_status
, *properties
);
49 ShillServiceClientStub::ShillServiceClientStub() : weak_ptr_factory_(this) {
52 ShillServiceClientStub::~ShillServiceClientStub() {
53 STLDeleteContainerPairSecondPointers(
54 observer_list_
.begin(), observer_list_
.end());
58 bool ShillServiceClientStub::IsStubPortalledWifiEnabled(
59 const std::string
& path
) {
60 if (!CommandLine::ForCurrentProcess()->HasSwitch(
61 chromeos::switches::kEnableStubPortalledWifi
)) {
64 return path
== kStubPortalledWifiPath
;
67 // ShillServiceClient overrides.
69 void ShillServiceClientStub::AddPropertyChangedObserver(
70 const dbus::ObjectPath
& service_path
,
71 ShillPropertyChangedObserver
* observer
) {
72 GetObserverList(service_path
).AddObserver(observer
);
75 void ShillServiceClientStub::RemovePropertyChangedObserver(
76 const dbus::ObjectPath
& service_path
,
77 ShillPropertyChangedObserver
* observer
) {
78 GetObserverList(service_path
).RemoveObserver(observer
);
81 void ShillServiceClientStub::GetProperties(
82 const dbus::ObjectPath
& service_path
,
83 const DictionaryValueCallback
& callback
) {
84 base::DictionaryValue
* nested_dict
= NULL
;
85 scoped_ptr
<base::DictionaryValue
> result_properties
;
86 DBusMethodCallStatus call_status
;
87 stub_services_
.GetDictionaryWithoutPathExpansion(service_path
.value(),
90 result_properties
.reset(nested_dict
->DeepCopy());
91 // Remove credentials that Shill wouldn't send.
92 result_properties
->RemoveWithoutPathExpansion(flimflam::kPassphraseProperty
,
94 call_status
= DBUS_METHOD_CALL_SUCCESS
;
96 result_properties
.reset(new base::DictionaryValue
);
97 call_status
= DBUS_METHOD_CALL_FAILURE
;
100 base::MessageLoop::current()->PostTask(
102 base::Bind(&PassStubServiceProperties
,
105 base::Owned(result_properties
.release())));
108 void ShillServiceClientStub::SetProperty(const dbus::ObjectPath
& service_path
,
109 const std::string
& name
,
110 const base::Value
& value
,
111 const base::Closure
& callback
,
112 const ErrorCallback
& error_callback
) {
113 if (!SetServiceProperty(service_path
.value(), name
, value
)) {
114 LOG(ERROR
) << "Service not found: " << service_path
.value();
115 error_callback
.Run("Error.InvalidService", "Invalid Service");
118 base::MessageLoop::current()->PostTask(FROM_HERE
, callback
);
121 void ShillServiceClientStub::SetProperties(
122 const dbus::ObjectPath
& service_path
,
123 const base::DictionaryValue
& properties
,
124 const base::Closure
& callback
,
125 const ErrorCallback
& error_callback
) {
126 for (base::DictionaryValue::Iterator
iter(properties
);
127 !iter
.IsAtEnd(); iter
.Advance()) {
128 if (!SetServiceProperty(service_path
.value(), iter
.key(), iter
.value())) {
129 LOG(ERROR
) << "Service not found: " << service_path
.value();
130 error_callback
.Run("Error.InvalidService", "Invalid Service");
134 base::MessageLoop::current()->PostTask(FROM_HERE
, callback
);
137 void ShillServiceClientStub::ClearProperty(
138 const dbus::ObjectPath
& service_path
,
139 const std::string
& name
,
140 const base::Closure
& callback
,
141 const ErrorCallback
& error_callback
) {
142 base::DictionaryValue
* dict
= NULL
;
143 if (!stub_services_
.GetDictionaryWithoutPathExpansion(
144 service_path
.value(), &dict
)) {
145 error_callback
.Run("Error.InvalidService", "Invalid Service");
148 dict
->RemoveWithoutPathExpansion(name
, NULL
);
149 // Note: Shill does not send notifications when properties are cleared.
150 base::MessageLoop::current()->PostTask(FROM_HERE
, callback
);
153 void ShillServiceClientStub::ClearProperties(
154 const dbus::ObjectPath
& service_path
,
155 const std::vector
<std::string
>& names
,
156 const ListValueCallback
& callback
,
157 const ErrorCallback
& error_callback
) {
158 base::DictionaryValue
* dict
= NULL
;
159 if (!stub_services_
.GetDictionaryWithoutPathExpansion(
160 service_path
.value(), &dict
)) {
161 error_callback
.Run("Error.InvalidService", "Invalid Service");
164 scoped_ptr
<base::ListValue
> results(new base::ListValue
);
165 for (std::vector
<std::string
>::const_iterator iter
= names
.begin();
166 iter
!= names
.end(); ++iter
) {
167 dict
->RemoveWithoutPathExpansion(*iter
, NULL
);
168 // Note: Shill does not send notifications when properties are cleared.
169 results
->AppendBoolean(true);
171 base::MessageLoop::current()->PostTask(
173 base::Bind(&PassStubListValue
,
174 callback
, base::Owned(results
.release())));
177 void ShillServiceClientStub::Connect(const dbus::ObjectPath
& service_path
,
178 const base::Closure
& callback
,
179 const ErrorCallback
& error_callback
) {
180 VLOG(1) << "ShillServiceClientStub::Connect: " << service_path
.value();
181 base::DictionaryValue
* service_properties
;
182 if (!stub_services_
.GetDictionary(
183 service_path
.value(), &service_properties
)) {
184 LOG(ERROR
) << "Service not found: " << service_path
.value();
185 error_callback
.Run("Error.InvalidService", "Invalid Service");
189 // Set any other services of the same Type to 'offline' first, before setting
190 // State to Association which will trigger sorting Manager.Services and
191 // sending an update.
192 SetOtherServicesOffline(service_path
.value());
195 base::StringValue
associating_value(flimflam::kStateAssociation
);
196 SetServiceProperty(service_path
.value(),
197 flimflam::kStateProperty
,
200 // Set Online after a delay.
201 base::TimeDelta delay
;
202 if (CommandLine::ForCurrentProcess()->HasSwitch(
203 chromeos::switches::kEnableStubInteractive
)) {
204 const int kConnectDelaySeconds
= 5;
205 delay
= base::TimeDelta::FromSeconds(kConnectDelaySeconds
);
207 base::StringValue
online_value(flimflam::kStateOnline
);
208 if (service_path
.value() == kStubPortalledWifiPath
)
209 online_value
= base::StringValue(flimflam::kStatePortal
);
210 std::string passphrase
;
211 service_properties
->GetStringWithoutPathExpansion(
212 flimflam::kPassphraseProperty
, &passphrase
);
213 if (passphrase
== "failure")
214 online_value
= base::StringValue(flimflam::kStateFailure
);
215 base::MessageLoop::current()->PostDelayedTask(
217 base::Bind(&ShillServiceClientStub::SetProperty
,
218 weak_ptr_factory_
.GetWeakPtr(),
220 flimflam::kStateProperty
,
222 base::Bind(&base::DoNothing
),
226 // On failure, also set the Error property.
227 if (passphrase
== "failure") {
228 base::MessageLoop::current()->PostDelayedTask(
230 base::Bind(&ShillServiceClientStub::SetProperty
,
231 weak_ptr_factory_
.GetWeakPtr(),
233 flimflam::kErrorProperty
,
234 base::StringValue(flimflam::kErrorBadPassphrase
),
235 base::Bind(&base::DoNothing
),
241 void ShillServiceClientStub::Disconnect(const dbus::ObjectPath
& service_path
,
242 const base::Closure
& callback
,
243 const ErrorCallback
& error_callback
) {
244 base::Value
* service
;
245 if (!stub_services_
.Get(service_path
.value(), &service
)) {
246 error_callback
.Run("Error.InvalidService", "Invalid Service");
249 base::TimeDelta delay
;
250 if (CommandLine::ForCurrentProcess()->HasSwitch(
251 chromeos::switches::kEnableStubInteractive
)) {
252 const int kConnectDelaySeconds
= 2;
253 delay
= base::TimeDelta::FromSeconds(kConnectDelaySeconds
);
255 // Set Idle after a delay
256 base::StringValue
idle_value(flimflam::kStateIdle
);
257 base::MessageLoop::current()->PostDelayedTask(
259 base::Bind(&ShillServiceClientStub::SetProperty
,
260 weak_ptr_factory_
.GetWeakPtr(),
262 flimflam::kStateProperty
,
264 base::Bind(&base::DoNothing
),
270 void ShillServiceClientStub::Remove(const dbus::ObjectPath
& service_path
,
271 const base::Closure
& callback
,
272 const ErrorCallback
& error_callback
) {
273 base::MessageLoop::current()->PostTask(FROM_HERE
, callback
);
276 void ShillServiceClientStub::ActivateCellularModem(
277 const dbus::ObjectPath
& service_path
,
278 const std::string
& carrier
,
279 const base::Closure
& callback
,
280 const ErrorCallback
& error_callback
) {
281 base::DictionaryValue
* service_properties
=
282 GetModifiableServiceProperties(service_path
.value());
283 if (!service_properties
) {
284 LOG(ERROR
) << "Service not found: " << service_path
.value();
285 error_callback
.Run("Error.InvalidService", "Invalid Service");
287 SetServiceProperty(service_path
.value(),
288 flimflam::kActivationStateProperty
,
289 base::StringValue(flimflam::kActivationStateActivating
));
290 base::TimeDelta delay
;
291 if (CommandLine::ForCurrentProcess()->HasSwitch(
292 chromeos::switches::kEnableStubInteractive
)) {
293 const int kConnectDelaySeconds
= 2;
294 delay
= base::TimeDelta::FromSeconds(kConnectDelaySeconds
);
296 // Set Activated after a delay
297 base::MessageLoop::current()->PostDelayedTask(
299 base::Bind(&ShillServiceClientStub::SetCellularActivated
,
300 weak_ptr_factory_
.GetWeakPtr(),
305 base::MessageLoop::current()->PostTask(FROM_HERE
, callback
);
308 void ShillServiceClientStub::CompleteCellularActivation(
309 const dbus::ObjectPath
& service_path
,
310 const base::Closure
& callback
,
311 const ErrorCallback
& error_callback
) {
312 base::MessageLoop::current()->PostTask(FROM_HERE
, callback
);
315 bool ShillServiceClientStub::CallActivateCellularModemAndBlock(
316 const dbus::ObjectPath
& service_path
,
317 const std::string
& carrier
) {
321 void ShillServiceClientStub::GetLoadableProfileEntries(
322 const dbus::ObjectPath
& service_path
,
323 const DictionaryValueCallback
& callback
) {
324 // Provide a dictionary with a single { profile_path, service_path } entry
325 // if the Profile property is set, or an empty dictionary.
326 scoped_ptr
<base::DictionaryValue
> result_properties(
327 new base::DictionaryValue
);
328 base::DictionaryValue
* service_properties
=
329 GetModifiableServiceProperties(service_path
.value());
330 if (service_properties
) {
331 std::string profile_path
;
332 if (service_properties
->GetStringWithoutPathExpansion(
333 flimflam::kProfileProperty
, &profile_path
)) {
334 result_properties
->SetStringWithoutPathExpansion(
335 profile_path
, service_path
.value());
338 LOG(WARNING
) << "Service not in profile: " << service_path
.value();
341 DBusMethodCallStatus call_status
= DBUS_METHOD_CALL_SUCCESS
;
342 base::MessageLoop::current()->PostTask(
344 base::Bind(&PassStubServiceProperties
,
347 base::Owned(result_properties
.release())));
350 ShillServiceClient::TestInterface
* ShillServiceClientStub::GetTestInterface() {
354 // ShillServiceClient::TestInterface overrides.
356 void ShillServiceClientStub::AddService(const std::string
& service_path
,
357 const std::string
& name
,
358 const std::string
& type
,
359 const std::string
& state
,
360 bool add_to_visible_list
,
361 bool add_to_watch_list
) {
362 std::string nstate
= state
;
363 if (CommandLine::ForCurrentProcess()->HasSwitch(
364 chromeos::switches::kDefaultStubNetworkStateIdle
)) {
365 nstate
= flimflam::kStateIdle
;
367 AddServiceWithIPConfig(service_path
, name
, type
, nstate
, "",
368 add_to_visible_list
, add_to_watch_list
);
371 void ShillServiceClientStub::AddServiceWithIPConfig(
372 const std::string
& service_path
,
373 const std::string
& name
,
374 const std::string
& type
,
375 const std::string
& state
,
376 const std::string
& ipconfig_path
,
377 bool add_to_visible_list
,
378 bool add_to_watch_list
) {
379 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
380 AddManagerService(service_path
, add_to_visible_list
, add_to_watch_list
);
382 base::DictionaryValue
* properties
=
383 GetModifiableServiceProperties(service_path
);
384 properties
->SetWithoutPathExpansion(
385 flimflam::kSSIDProperty
,
386 base::Value::CreateStringValue(service_path
));
387 properties
->SetWithoutPathExpansion(
388 flimflam::kNameProperty
,
389 base::Value::CreateStringValue(name
));
390 properties
->SetWithoutPathExpansion(
391 flimflam::kTypeProperty
,
392 base::Value::CreateStringValue(type
));
393 properties
->SetWithoutPathExpansion(
394 flimflam::kStateProperty
,
395 base::Value::CreateStringValue(state
));
396 if (!ipconfig_path
.empty())
397 properties
->SetWithoutPathExpansion(
398 shill::kIPConfigProperty
,
399 base::Value::CreateStringValue(ipconfig_path
));
402 void ShillServiceClientStub::RemoveService(const std::string
& service_path
) {
403 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
404 RemoveManagerService(service_path
);
406 stub_services_
.RemoveWithoutPathExpansion(service_path
, NULL
);
409 bool ShillServiceClientStub::SetServiceProperty(const std::string
& service_path
,
410 const std::string
& property
,
411 const base::Value
& value
) {
412 base::DictionaryValue
* dict
= NULL
;
413 if (!stub_services_
.GetDictionaryWithoutPathExpansion(service_path
, &dict
))
416 VLOG(1) << "Service.SetProperty: " << property
<< " = " << value
417 << " For: " << service_path
;
419 base::DictionaryValue new_properties
;
420 std::string changed_property
;
421 bool case_sensitive
= true;
422 if (StartsWithASCII(property
, "Provider.", case_sensitive
) ||
423 StartsWithASCII(property
, "OpenVPN.", case_sensitive
) ||
424 StartsWithASCII(property
, "L2TPIPsec.", case_sensitive
)) {
425 // These properties are only nested within the Provider dictionary if read
427 base::DictionaryValue
* provider
= new base::DictionaryValue
;
428 provider
->SetWithoutPathExpansion(property
, value
.DeepCopy());
429 new_properties
.SetWithoutPathExpansion(flimflam::kProviderProperty
,
431 changed_property
= flimflam::kProviderProperty
;
433 new_properties
.SetWithoutPathExpansion(property
, value
.DeepCopy());
434 changed_property
= property
;
437 dict
->MergeDictionary(&new_properties
);
439 if (property
== flimflam::kStateProperty
) {
440 // When State changes the sort order of Services may change.
441 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
442 SortManagerServices();
445 base::MessageLoop::current()->PostTask(
447 base::Bind(&ShillServiceClientStub::NotifyObserversPropertyChanged
,
448 weak_ptr_factory_
.GetWeakPtr(),
449 dbus::ObjectPath(service_path
), changed_property
));
453 const base::DictionaryValue
* ShillServiceClientStub::GetServiceProperties(
454 const std::string
& service_path
) const {
455 const base::DictionaryValue
* properties
= NULL
;
456 stub_services_
.GetDictionaryWithoutPathExpansion(service_path
, &properties
);
460 void ShillServiceClientStub::ClearServices() {
461 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
462 ClearManagerServices();
464 stub_services_
.Clear();
467 void ShillServiceClientStub::AddDefaultServices() {
468 const bool add_to_visible
= true;
469 const bool add_to_watchlist
= true;
471 if (!CommandLine::ForCurrentProcess()->HasSwitch(
472 chromeos::switches::kDisableStubEthernet
)) {
473 AddService("eth1", "eth1",
474 flimflam::kTypeEthernet
,
475 flimflam::kStateOnline
,
476 add_to_visible
, add_to_watchlist
);
481 AddService("wifi1", "wifi1",
483 flimflam::kStateOnline
,
484 add_to_visible
, add_to_watchlist
);
485 SetServiceProperty("wifi1",
486 flimflam::kSecurityProperty
,
487 base::StringValue(flimflam::kSecurityWep
));
489 AddService("wifi2", "wifi2_PSK",
491 flimflam::kStateIdle
,
492 add_to_visible
, add_to_watchlist
);
493 SetServiceProperty("wifi2",
494 flimflam::kSecurityProperty
,
495 base::StringValue(flimflam::kSecurityPsk
));
496 base::FundamentalValue
strength_value(80);
497 SetServiceProperty("wifi2",
498 flimflam::kSignalStrengthProperty
,
501 if (CommandLine::ForCurrentProcess()->HasSwitch(
502 chromeos::switches::kEnableStubPortalledWifi
)) {
503 AddService(kStubPortalledWifiPath
, kStubPortalledWifiName
,
505 flimflam::kStatePortal
,
506 add_to_visible
, add_to_watchlist
);
507 SetServiceProperty(kStubPortalledWifiPath
,
508 flimflam::kSecurityProperty
,
509 base::StringValue(flimflam::kSecurityNone
));
514 AddService("wimax1", "wimax1",
515 flimflam::kTypeWimax
,
516 flimflam::kStateIdle
,
517 add_to_visible
, add_to_watchlist
);
518 SetServiceProperty("wimax1",
519 flimflam::kConnectableProperty
,
520 base::FundamentalValue(true));
524 AddService("cellular1", "cellular1",
525 flimflam::kTypeCellular
,
526 flimflam::kStateIdle
,
527 add_to_visible
, add_to_watchlist
);
528 base::StringValue
technology_value(flimflam::kNetworkTechnologyGsm
);
529 SetServiceProperty("cellular1",
530 flimflam::kNetworkTechnologyProperty
,
532 SetServiceProperty("cellular1",
533 flimflam::kActivationStateProperty
,
534 base::StringValue(flimflam::kActivationStateNotActivated
));
535 SetServiceProperty("cellular1",
536 flimflam::kRoamingStateProperty
,
537 base::StringValue(flimflam::kRoamingStateHome
));
541 // Set the "Provider" dictionary properties. Note: when setting these in
542 // Shill, "Provider.Type", etc keys are used, but when reading the values
543 // "Provider" . "Type", etc keys are used. Here we are setting the values
544 // that will be read (by the UI, tests, etc).
545 base::DictionaryValue provider_properties
;
546 provider_properties
.SetString(flimflam::kTypeProperty
,
547 flimflam::kProviderOpenVpn
);
548 provider_properties
.SetString(flimflam::kHostProperty
, "vpn_host");
550 AddService("vpn1", "vpn1",
552 flimflam::kStateOnline
,
553 add_to_visible
, add_to_watchlist
);
554 SetServiceProperty("vpn1",
555 flimflam::kProviderProperty
,
556 provider_properties
);
558 AddService("vpn2", "vpn2",
560 flimflam::kStateOffline
,
561 add_to_visible
, add_to_watchlist
);
562 SetServiceProperty("vpn2",
563 flimflam::kProviderProperty
,
564 provider_properties
);
566 DBusThreadManager::Get()->GetShillProfileClient()->GetTestInterface()->
567 AddService(ShillProfileClientStub::kSharedProfilePath
, "wifi2");
569 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
570 SortManagerServices();
573 void ShillServiceClientStub::NotifyObserversPropertyChanged(
574 const dbus::ObjectPath
& service_path
,
575 const std::string
& property
) {
576 base::DictionaryValue
* dict
= NULL
;
577 std::string path
= service_path
.value();
578 if (!stub_services_
.GetDictionaryWithoutPathExpansion(path
, &dict
)) {
579 LOG(ERROR
) << "Notify for unknown service: " << path
;
582 base::Value
* value
= NULL
;
583 if (!dict
->GetWithoutPathExpansion(property
, &value
)) {
584 LOG(ERROR
) << "Notify for unknown property: "
585 << path
<< " : " << property
;
588 FOR_EACH_OBSERVER(ShillPropertyChangedObserver
,
589 GetObserverList(service_path
),
590 OnPropertyChanged(property
, *value
));
593 base::DictionaryValue
* ShillServiceClientStub::GetModifiableServiceProperties(
594 const std::string
& service_path
) {
595 base::DictionaryValue
* properties
= NULL
;
596 if (!stub_services_
.GetDictionaryWithoutPathExpansion(
597 service_path
, &properties
)) {
598 properties
= new base::DictionaryValue
;
599 stub_services_
.Set(service_path
, properties
);
604 ShillServiceClientStub::PropertyObserverList
&
605 ShillServiceClientStub::GetObserverList(const dbus::ObjectPath
& device_path
) {
606 std::map
<dbus::ObjectPath
, PropertyObserverList
*>::iterator iter
=
607 observer_list_
.find(device_path
);
608 if (iter
!= observer_list_
.end())
609 return *(iter
->second
);
610 PropertyObserverList
* observer_list
= new PropertyObserverList();
611 observer_list_
[device_path
] = observer_list
;
612 return *observer_list
;
615 void ShillServiceClientStub::SetOtherServicesOffline(
616 const std::string
& service_path
) {
617 const base::DictionaryValue
* service_properties
= GetServiceProperties(
619 if (!service_properties
) {
620 LOG(ERROR
) << "Missing service: " << service_path
;
623 std::string service_type
;
624 service_properties
->GetString(flimflam::kTypeProperty
, &service_type
);
625 // Set all other services of the same type to offline (Idle).
626 for (base::DictionaryValue::Iterator
iter(stub_services_
);
627 !iter
.IsAtEnd(); iter
.Advance()) {
628 std::string path
= iter
.key();
629 if (path
== service_path
)
631 base::DictionaryValue
* properties
;
632 if (!stub_services_
.GetDictionaryWithoutPathExpansion(path
, &properties
))
636 properties
->GetString(flimflam::kTypeProperty
, &type
);
637 if (type
!= service_type
)
639 properties
->SetWithoutPathExpansion(
640 flimflam::kStateProperty
,
641 base::Value::CreateStringValue(flimflam::kStateIdle
));
645 void ShillServiceClientStub::SetCellularActivated(
646 const dbus::ObjectPath
& service_path
,
647 const ErrorCallback
& error_callback
) {
648 SetProperty(service_path
,
649 flimflam::kActivationStateProperty
,
650 base::StringValue(flimflam::kActivationStateActivated
),
651 base::Bind(&base::DoNothing
),
653 SetProperty(service_path
,
654 flimflam::kConnectableProperty
,
655 base::FundamentalValue(true),
656 base::Bind(&base::DoNothing
),
660 } // namespace chromeos