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