• 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 & name,const std::string & type,const std::string & state,bool visible)307 void FakeShillServiceClient::AddService(const std::string& service_path,
308                                         const std::string& name,
309                                         const std::string& type,
310                                         const std::string& state,
311                                         bool visible) {
312   AddServiceWithIPConfig(service_path, "" /* guid */, name,
313                          type, state, "" /* ipconfig_path */,
314                          visible);
315 }
316 
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)317 void FakeShillServiceClient::AddServiceWithIPConfig(
318     const std::string& service_path,
319     const std::string& guid,
320     const std::string& name,
321     const std::string& type,
322     const std::string& state,
323     const std::string& ipconfig_path,
324     bool visible) {
325   base::DictionaryValue* properties = SetServiceProperties(
326       service_path, guid, name, type, state, visible);
327 
328   std::string profile_path;
329   if (properties->GetStringWithoutPathExpansion(shill::kProfileProperty,
330                                                 &profile_path) &&
331       !profile_path.empty()) {
332     DBusThreadManager::Get()->GetShillProfileClient()->GetTestInterface()->
333         UpdateService(profile_path, service_path);
334   }
335 
336   if (!ipconfig_path.empty()) {
337     properties->SetWithoutPathExpansion(
338         shill::kIPConfigProperty,
339         new base::StringValue(ipconfig_path));
340   }
341 
342   DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
343       AddManagerService(service_path, true);
344 }
345 
346 
SetServiceProperties(const std::string & service_path,const std::string & guid,const std::string & name,const std::string & type,const std::string & state,bool visible)347 base::DictionaryValue* FakeShillServiceClient::SetServiceProperties(
348     const std::string& service_path,
349     const std::string& guid,
350     const std::string& name,
351     const std::string& type,
352     const std::string& state,
353     bool visible) {
354   base::DictionaryValue* properties =
355       GetModifiableServiceProperties(service_path, true);
356   connect_behavior_.erase(service_path);
357 
358   std::string profile_path;
359   base::DictionaryValue profile_properties;
360   if (DBusThreadManager::Get()->GetShillProfileClient()->GetTestInterface()->
361       GetService(service_path, &profile_path, &profile_properties)) {
362     properties->SetWithoutPathExpansion(
363         shill::kProfileProperty,
364         new base::StringValue(profile_path));
365   }
366 
367   // If |guid| is provided, set Service.GUID to that. Otherwise if a GUID is
368   // stored in a profile entry, use that. Otherwise leave it blank. Shill does
369   // not enforce a valid guid, we do that at the NetworkStateHandler layer.
370   std::string guid_to_set = guid;
371   if (guid_to_set.empty()) {
372     profile_properties.GetStringWithoutPathExpansion(
373         shill::kGuidProperty, &guid_to_set);
374   }
375   if (!guid_to_set.empty()) {
376     properties->SetWithoutPathExpansion(shill::kGuidProperty,
377                                         new base::StringValue(guid_to_set));
378   }
379   shill_property_util::SetSSID(name, properties);
380   properties->SetWithoutPathExpansion(
381       shill::kNameProperty,
382       new base::StringValue(name));
383   std::string device_path =
384       DBusThreadManager::Get()->GetShillDeviceClient()->GetTestInterface()->
385       GetDevicePathForType(type);
386   properties->SetWithoutPathExpansion(
387       shill::kDeviceProperty,
388       new base::StringValue(device_path));
389   properties->SetWithoutPathExpansion(
390       shill::kTypeProperty,
391       new base::StringValue(type));
392   properties->SetWithoutPathExpansion(
393       shill::kStateProperty,
394       new base::StringValue(state));
395   properties->SetWithoutPathExpansion(
396       shill::kVisibleProperty,
397       new base::FundamentalValue(visible));
398   if (type == shill::kTypeWifi) {
399     properties->SetWithoutPathExpansion(
400         shill::kSecurityProperty,
401         new base::StringValue(shill::kSecurityNone));
402   }
403   return properties;
404 }
405 
RemoveService(const std::string & service_path)406 void FakeShillServiceClient::RemoveService(const std::string& service_path) {
407   stub_services_.RemoveWithoutPathExpansion(service_path, NULL);
408   connect_behavior_.erase(service_path);
409   DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
410       RemoveManagerService(service_path);
411 }
412 
SetServiceProperty(const std::string & service_path,const std::string & property,const base::Value & value)413 bool FakeShillServiceClient::SetServiceProperty(const std::string& service_path,
414                                                 const std::string& property,
415                                                 const base::Value& value) {
416   base::DictionaryValue* dict = NULL;
417   if (!stub_services_.GetDictionaryWithoutPathExpansion(service_path, &dict))
418     return false;
419 
420   VLOG(1) << "Service.SetProperty: " << property << " = " << value
421           << " For: " << service_path;
422 
423   base::DictionaryValue new_properties;
424   std::string changed_property;
425   bool case_sensitive = true;
426   if (StartsWithASCII(property, "Provider.", case_sensitive) ||
427       StartsWithASCII(property, "OpenVPN.", case_sensitive) ||
428       StartsWithASCII(property, "L2TPIPsec.", case_sensitive)) {
429     // These properties are only nested within the Provider dictionary if read
430     // from Shill.
431     base::DictionaryValue* provider = new base::DictionaryValue;
432     provider->SetWithoutPathExpansion(property, value.DeepCopy());
433     new_properties.SetWithoutPathExpansion(shill::kProviderProperty, provider);
434     changed_property = shill::kProviderProperty;
435   } else {
436     new_properties.SetWithoutPathExpansion(property, value.DeepCopy());
437     changed_property = property;
438   }
439 
440   dict->MergeDictionary(&new_properties);
441 
442   // Add or update the profile entry.
443   if (property == shill::kProfileProperty) {
444     std::string profile_path;
445     if (value.GetAsString(&profile_path)) {
446       DBusThreadManager::Get()->GetShillProfileClient()->GetTestInterface()->
447           AddService(profile_path, service_path);
448     } else {
449       LOG(ERROR) << "Profile value is not a String!";
450     }
451   } else {
452     std::string profile_path;
453     if (dict->GetStringWithoutPathExpansion(
454             shill::kProfileProperty, &profile_path) && !profile_path.empty()) {
455       DBusThreadManager::Get()->GetShillProfileClient()->GetTestInterface()->
456           UpdateService(profile_path, service_path);
457     }
458   }
459 
460   // Notify the Manager if the state changed (affects DefaultService).
461   if (property == shill::kStateProperty) {
462     std::string state;
463     value.GetAsString(&state);
464     DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
465         ServiceStateChanged(service_path, state);
466   }
467 
468   // If the State or Visibility changes, the sort order of service lists may
469   // change and the DefaultService property may change.
470   if (property == shill::kStateProperty ||
471       property == shill::kVisibleProperty) {
472     base::MessageLoop::current()->PostTask(
473         FROM_HERE, base::Bind(&CallSortManagerServices));
474   }
475 
476   // Notifiy Chrome of the property change.
477   base::MessageLoop::current()->PostTask(
478       FROM_HERE,
479       base::Bind(&FakeShillServiceClient::NotifyObserversPropertyChanged,
480                  weak_ptr_factory_.GetWeakPtr(),
481                  dbus::ObjectPath(service_path), changed_property));
482   return true;
483 }
484 
GetServiceProperties(const std::string & service_path) const485 const base::DictionaryValue* FakeShillServiceClient::GetServiceProperties(
486     const std::string& service_path) const {
487   const base::DictionaryValue* properties = NULL;
488   stub_services_.GetDictionaryWithoutPathExpansion(service_path, &properties);
489   return properties;
490 }
491 
ClearServices()492 void FakeShillServiceClient::ClearServices() {
493   DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
494       ClearManagerServices();
495 
496   stub_services_.Clear();
497   connect_behavior_.clear();
498 }
499 
SetConnectBehavior(const std::string & service_path,const base::Closure & behavior)500 void FakeShillServiceClient::SetConnectBehavior(const std::string& service_path,
501                                 const base::Closure& behavior) {
502   connect_behavior_[service_path] = behavior;
503 }
504 
NotifyObserversPropertyChanged(const dbus::ObjectPath & service_path,const std::string & property)505 void FakeShillServiceClient::NotifyObserversPropertyChanged(
506     const dbus::ObjectPath& service_path,
507     const std::string& property) {
508   base::DictionaryValue* dict = NULL;
509   std::string path = service_path.value();
510   if (!stub_services_.GetDictionaryWithoutPathExpansion(path, &dict)) {
511     LOG(ERROR) << "Notify for unknown service: " << path;
512     return;
513   }
514   base::Value* value = NULL;
515   if (!dict->GetWithoutPathExpansion(property, &value)) {
516     LOG(ERROR) << "Notify for unknown property: "
517                << path << " : " << property;
518     return;
519   }
520   FOR_EACH_OBSERVER(ShillPropertyChangedObserver,
521                     GetObserverList(service_path),
522                     OnPropertyChanged(property, *value));
523 }
524 
GetModifiableServiceProperties(const std::string & service_path,bool create_if_missing)525 base::DictionaryValue* FakeShillServiceClient::GetModifiableServiceProperties(
526     const std::string& service_path, bool create_if_missing) {
527   base::DictionaryValue* properties = NULL;
528   if (!stub_services_.GetDictionaryWithoutPathExpansion(service_path,
529                                                         &properties) &&
530       create_if_missing) {
531     properties = new base::DictionaryValue;
532     stub_services_.Set(service_path, properties);
533   }
534   return properties;
535 }
536 
537 FakeShillServiceClient::PropertyObserverList&
GetObserverList(const dbus::ObjectPath & device_path)538 FakeShillServiceClient::GetObserverList(const dbus::ObjectPath& device_path) {
539   std::map<dbus::ObjectPath, PropertyObserverList*>::iterator iter =
540       observer_list_.find(device_path);
541   if (iter != observer_list_.end())
542     return *(iter->second);
543   PropertyObserverList* observer_list = new PropertyObserverList();
544   observer_list_[device_path] = observer_list;
545   return *observer_list;
546 }
547 
SetOtherServicesOffline(const std::string & service_path)548 void FakeShillServiceClient::SetOtherServicesOffline(
549     const std::string& service_path) {
550   const base::DictionaryValue* service_properties = GetServiceProperties(
551       service_path);
552   if (!service_properties) {
553     LOG(ERROR) << "Missing service: " << service_path;
554     return;
555   }
556   std::string service_type;
557   service_properties->GetString(shill::kTypeProperty, &service_type);
558   // Set all other services of the same type to offline (Idle).
559   for (base::DictionaryValue::Iterator iter(stub_services_);
560        !iter.IsAtEnd(); iter.Advance()) {
561     std::string path = iter.key();
562     if (path == service_path)
563       continue;
564     base::DictionaryValue* properties;
565     if (!stub_services_.GetDictionaryWithoutPathExpansion(path, &properties))
566       NOTREACHED();
567 
568     std::string type;
569     properties->GetString(shill::kTypeProperty, &type);
570     if (type != service_type)
571       continue;
572     properties->SetWithoutPathExpansion(
573         shill::kStateProperty,
574         new base::StringValue(shill::kStateIdle));
575   }
576 }
577 
SetCellularActivated(const dbus::ObjectPath & service_path,const ErrorCallback & error_callback)578 void FakeShillServiceClient::SetCellularActivated(
579     const dbus::ObjectPath& service_path,
580     const ErrorCallback& error_callback) {
581   SetProperty(service_path,
582               shill::kActivationStateProperty,
583               base::StringValue(shill::kActivationStateActivated),
584               base::Bind(&base::DoNothing),
585               error_callback);
586   SetProperty(service_path,
587               shill::kConnectableProperty,
588               base::FundamentalValue(true),
589               base::Bind(&base::DoNothing),
590               error_callback);
591 }
592 
ContinueConnect(const std::string & service_path)593 void FakeShillServiceClient::ContinueConnect(
594     const std::string& service_path) {
595   VLOG(1) << "FakeShillServiceClient::ContinueConnect: " << service_path;
596   base::DictionaryValue* service_properties = NULL;
597   if (!stub_services_.GetDictionary(service_path, &service_properties)) {
598     LOG(ERROR) << "Service not found: " << service_path;
599     return;
600   }
601 
602   if (ContainsKey(connect_behavior_, service_path)) {
603     const base::Closure& custom_connect_behavior =
604         connect_behavior_[service_path];
605     custom_connect_behavior.Run();
606     return;
607   }
608 
609   // No custom connect behavior set, continue with the default connect behavior.
610   std::string passphrase;
611   service_properties->GetStringWithoutPathExpansion(
612       shill::kPassphraseProperty, &passphrase);
613   if (passphrase == "failure") {
614     // Simulate a password failure.
615     SetServiceProperty(service_path,
616                        shill::kStateProperty,
617                        base::StringValue(shill::kStateFailure));
618     base::MessageLoop::current()->PostTask(
619         FROM_HERE,
620         base::Bind(
621             base::IgnoreResult(&FakeShillServiceClient::SetServiceProperty),
622             weak_ptr_factory_.GetWeakPtr(),
623             service_path,
624             shill::kErrorProperty,
625             base::StringValue(shill::kErrorBadPassphrase)));
626   } else {
627     // Set Online.
628     SetServiceProperty(service_path,
629                        shill::kStateProperty,
630                        base::StringValue(shill::kStateOnline));
631   }
632 }
633 
634 }  // namespace chromeos
635