Home | History | Annotate | Download | only in dbus
      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 
     28 void PassStubListValue(const ShillServiceClient::ListValueCallback& callback,
     29                        base::ListValue* value) {
     30   callback.Run(*value);
     31 }
     32 
     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 
     40 void CallSortManagerServices() {
     41   DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
     42       SortManagerServices(true);
     43 }
     44 
     45 int GetInteractiveDelay() {
     46   return DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
     47       GetInteractiveDelay();
     48 }
     49 
     50 }  // namespace
     51 
     52 FakeShillServiceClient::FakeShillServiceClient() : weak_ptr_factory_(this) {
     53 }
     54 
     55 FakeShillServiceClient::~FakeShillServiceClient() {
     56   STLDeleteContainerPairSecondPointers(
     57       observer_list_.begin(), observer_list_.end());
     58 }
     59 
     60 
     61 // ShillServiceClient overrides.
     62 
     63 void FakeShillServiceClient::Init(dbus::Bus* bus) {
     64 }
     65 
     66 void FakeShillServiceClient::AddPropertyChangedObserver(
     67     const dbus::ObjectPath& service_path,
     68     ShillPropertyChangedObserver* observer) {
     69   GetObserverList(service_path).AddObserver(observer);
     70 }
     71 
     72 void FakeShillServiceClient::RemovePropertyChangedObserver(
     73     const dbus::ObjectPath& service_path,
     74     ShillPropertyChangedObserver* observer) {
     75   GetObserverList(service_path).RemoveObserver(observer);
     76 }
     77 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    301 ShillServiceClient::TestInterface* FakeShillServiceClient::GetTestInterface() {
    302   return this;
    303 }
    304 
    305 // ShillServiceClient::TestInterface overrides.
    306 
    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 
    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 
    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 
    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 
    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 
    485 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 
    492 void FakeShillServiceClient::ClearServices() {
    493   DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
    494       ClearManagerServices();
    495 
    496   stub_services_.Clear();
    497   connect_behavior_.clear();
    498 }
    499 
    500 void FakeShillServiceClient::SetConnectBehavior(const std::string& service_path,
    501                                 const base::Closure& behavior) {
    502   connect_behavior_[service_path] = behavior;
    503 }
    504 
    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 
    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&
    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 
    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 
    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 
    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