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