Home | History | Annotate | Download | only in network
      1 // Copyright (c) 2012 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/network/shill_property_handler.h"
      6 
      7 #include <sstream>
      8 
      9 #include "base/bind.h"
     10 #include "base/format_macros.h"
     11 #include "base/stl_util.h"
     12 #include "base/strings/string_util.h"
     13 #include "base/strings/stringprintf.h"
     14 #include "base/values.h"
     15 #include "chromeos/dbus/dbus_thread_manager.h"
     16 #include "chromeos/dbus/shill_device_client.h"
     17 #include "chromeos/dbus/shill_ipconfig_client.h"
     18 #include "chromeos/dbus/shill_manager_client.h"
     19 #include "chromeos/dbus/shill_profile_client.h"
     20 #include "chromeos/dbus/shill_service_client.h"
     21 #include "chromeos/network/network_event_log.h"
     22 #include "chromeos/network/network_state.h"
     23 #include "dbus/object_path.h"
     24 #include "third_party/cros_system_api/dbus/service_constants.h"
     25 
     26 namespace {
     27 
     28 // Limit the number of services or devices we observe. Since they are listed in
     29 // priority order, it should be reasonable to ignore services past this.
     30 const size_t kMaxObserved = 100;
     31 
     32 const base::ListValue* GetListValue(const std::string& key,
     33                                     const base::Value& value) {
     34   const base::ListValue* vlist = NULL;
     35   if (!value.GetAsList(&vlist)) {
     36     LOG(ERROR) << "Error parsing key as list: " << key;
     37     return NULL;
     38   }
     39   return vlist;
     40 }
     41 
     42 }  // namespace
     43 
     44 namespace chromeos {
     45 namespace internal {
     46 
     47 // Class to manage Shill service property changed observers. Observers are
     48 // added on construction and removed on destruction. Runs the handler when
     49 // OnPropertyChanged is called.
     50 class ShillPropertyObserver : public ShillPropertyChangedObserver {
     51  public:
     52   typedef base::Callback<void(ManagedState::ManagedType type,
     53                               const std::string& service,
     54                               const std::string& name,
     55                               const base::Value& value)> Handler;
     56 
     57   ShillPropertyObserver(ManagedState::ManagedType type,
     58                         const std::string& path,
     59                         const Handler& handler)
     60       : type_(type),
     61         path_(path),
     62         handler_(handler) {
     63     if (type_ == ManagedState::MANAGED_TYPE_NETWORK) {
     64       DVLOG(2) << "ShillPropertyObserver: Network: " << path;
     65       DBusThreadManager::Get()->GetShillServiceClient()->
     66           AddPropertyChangedObserver(dbus::ObjectPath(path_), this);
     67     } else if (type_ == ManagedState::MANAGED_TYPE_DEVICE) {
     68       DVLOG(2) << "ShillPropertyObserver: Device: " << path;
     69       DBusThreadManager::Get()->GetShillDeviceClient()->
     70           AddPropertyChangedObserver(dbus::ObjectPath(path_), this);
     71     } else {
     72       NOTREACHED();
     73     }
     74   }
     75 
     76   virtual ~ShillPropertyObserver() {
     77     if (type_ == ManagedState::MANAGED_TYPE_NETWORK) {
     78       DBusThreadManager::Get()->GetShillServiceClient()->
     79           RemovePropertyChangedObserver(dbus::ObjectPath(path_), this);
     80     } else if (type_ == ManagedState::MANAGED_TYPE_DEVICE) {
     81       DBusThreadManager::Get()->GetShillDeviceClient()->
     82           RemovePropertyChangedObserver(dbus::ObjectPath(path_), this);
     83     } else {
     84       NOTREACHED();
     85     }
     86   }
     87 
     88   // ShillPropertyChangedObserver overrides.
     89   virtual void OnPropertyChanged(const std::string& key,
     90                                  const base::Value& value) OVERRIDE {
     91     handler_.Run(type_, path_, key, value);
     92   }
     93 
     94  private:
     95   ManagedState::ManagedType type_;
     96   std::string path_;
     97   Handler handler_;
     98 
     99   DISALLOW_COPY_AND_ASSIGN(ShillPropertyObserver);
    100 };
    101 
    102 //------------------------------------------------------------------------------
    103 // ShillPropertyHandler
    104 
    105 ShillPropertyHandler::ShillPropertyHandler(Listener* listener)
    106     : listener_(listener),
    107       shill_manager_(DBusThreadManager::Get()->GetShillManagerClient()) {
    108 }
    109 
    110 ShillPropertyHandler::~ShillPropertyHandler() {
    111   // Delete network service observers.
    112   STLDeleteContainerPairSecondPointers(
    113       observed_networks_.begin(), observed_networks_.end());
    114   STLDeleteContainerPairSecondPointers(
    115       observed_devices_.begin(), observed_devices_.end());
    116   CHECK(shill_manager_ == DBusThreadManager::Get()->GetShillManagerClient());
    117   shill_manager_->RemovePropertyChangedObserver(this);
    118 }
    119 
    120 void ShillPropertyHandler::Init() {
    121   UpdateManagerProperties();
    122   shill_manager_->AddPropertyChangedObserver(this);
    123 }
    124 
    125 void ShillPropertyHandler::UpdateManagerProperties() {
    126   NET_LOG_EVENT("UpdateManagerProperties", "");
    127   shill_manager_->GetProperties(
    128       base::Bind(&ShillPropertyHandler::ManagerPropertiesCallback,
    129                  AsWeakPtr()));
    130 }
    131 
    132 bool ShillPropertyHandler::IsTechnologyAvailable(
    133     const std::string& technology) const {
    134   return available_technologies_.count(technology) != 0;
    135 }
    136 
    137 bool ShillPropertyHandler::IsTechnologyEnabled(
    138     const std::string& technology) const {
    139   return enabled_technologies_.count(technology) != 0;
    140 }
    141 
    142 bool ShillPropertyHandler::IsTechnologyEnabling(
    143     const std::string& technology) const {
    144   return enabling_technologies_.count(technology) != 0;
    145 }
    146 
    147 bool ShillPropertyHandler::IsTechnologyUninitialized(
    148     const std::string& technology) const {
    149   return uninitialized_technologies_.count(technology) != 0;
    150 }
    151 
    152 void ShillPropertyHandler::SetTechnologyEnabled(
    153     const std::string& technology,
    154     bool enabled,
    155     const network_handler::ErrorCallback& error_callback) {
    156   if (enabled) {
    157     enabling_technologies_.insert(technology);
    158     shill_manager_->EnableTechnology(
    159         technology,
    160         base::Bind(&base::DoNothing),
    161         base::Bind(&ShillPropertyHandler::EnableTechnologyFailed,
    162                    AsWeakPtr(), technology, error_callback));
    163   } else {
    164     // Immediately clear locally from enabled and enabling lists.
    165     enabled_technologies_.erase(technology);
    166     enabling_technologies_.erase(technology);
    167     shill_manager_->DisableTechnology(
    168         technology,
    169         base::Bind(&base::DoNothing),
    170         base::Bind(&network_handler::ShillErrorCallbackFunction,
    171                    "SetTechnologyEnabled Failed",
    172                    technology, error_callback));
    173   }
    174 }
    175 
    176 void ShillPropertyHandler::SetCheckPortalList(
    177     const std::string& check_portal_list) {
    178   base::StringValue value(check_portal_list);
    179   shill_manager_->SetProperty(
    180       shill::kCheckPortalListProperty,
    181       value,
    182       base::Bind(&base::DoNothing),
    183       base::Bind(&network_handler::ShillErrorCallbackFunction,
    184                  "SetCheckPortalList Failed",
    185                  "", network_handler::ErrorCallback()));
    186 }
    187 
    188 void ShillPropertyHandler::RequestScan() const {
    189   shill_manager_->RequestScan(
    190       "",
    191       base::Bind(&base::DoNothing),
    192       base::Bind(&network_handler::ShillErrorCallbackFunction,
    193                  "RequestScan Failed",
    194                  "", network_handler::ErrorCallback()));
    195 }
    196 
    197 void ShillPropertyHandler::ConnectToBestServices() const {
    198   NET_LOG_EVENT("ConnectToBestServices", "");
    199   shill_manager_->ConnectToBestServices(
    200       base::Bind(&base::DoNothing),
    201       base::Bind(&network_handler::ShillErrorCallbackFunction,
    202                  "ConnectToBestServices Failed",
    203                  "", network_handler::ErrorCallback()));
    204 }
    205 
    206 void ShillPropertyHandler::RequestProperties(ManagedState::ManagedType type,
    207                                              const std::string& path) {
    208   if (pending_updates_[type].find(path) != pending_updates_[type].end())
    209     return;  // Update already requested.
    210 
    211   NET_LOG_DEBUG("Request Properties: " + ManagedState::TypeToString(type),
    212                 path);
    213   pending_updates_[type].insert(path);
    214   if (type == ManagedState::MANAGED_TYPE_NETWORK) {
    215     DBusThreadManager::Get()->GetShillServiceClient()->GetProperties(
    216         dbus::ObjectPath(path),
    217         base::Bind(&ShillPropertyHandler::GetPropertiesCallback,
    218                    AsWeakPtr(), type, path));
    219   } else if (type == ManagedState::MANAGED_TYPE_DEVICE) {
    220     DBusThreadManager::Get()->GetShillDeviceClient()->GetProperties(
    221         dbus::ObjectPath(path),
    222         base::Bind(&ShillPropertyHandler::GetPropertiesCallback,
    223                    AsWeakPtr(), type, path));
    224   } else {
    225     NOTREACHED();
    226   }
    227 }
    228 
    229 void ShillPropertyHandler::OnPropertyChanged(const std::string& key,
    230                                              const base::Value& value) {
    231   ManagerPropertyChanged(key, value);
    232   CheckPendingStateListUpdates(key);
    233 }
    234 
    235 //------------------------------------------------------------------------------
    236 // Private methods
    237 
    238 void ShillPropertyHandler::ManagerPropertiesCallback(
    239     DBusMethodCallStatus call_status,
    240     const base::DictionaryValue& properties) {
    241   if (call_status != DBUS_METHOD_CALL_SUCCESS) {
    242     NET_LOG_ERROR("ManagerPropertiesCallback",
    243                   base::StringPrintf("Failed: %d", call_status));
    244     return;
    245   }
    246   NET_LOG_EVENT("ManagerPropertiesCallback", "Success");
    247   for (base::DictionaryValue::Iterator iter(properties);
    248        !iter.IsAtEnd(); iter.Advance()) {
    249     ManagerPropertyChanged(iter.key(), iter.value());
    250   }
    251 
    252   CheckPendingStateListUpdates("");
    253 }
    254 
    255 void ShillPropertyHandler::CheckPendingStateListUpdates(
    256     const std::string& key) {
    257   // Once there are no pending updates, signal the state list changed callbacks.
    258   if ((key.empty() || key == shill::kServiceCompleteListProperty) &&
    259       pending_updates_[ManagedState::MANAGED_TYPE_NETWORK].size() == 0) {
    260     listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_NETWORK);
    261   }
    262   if ((key.empty() || key == shill::kDevicesProperty) &&
    263       pending_updates_[ManagedState::MANAGED_TYPE_DEVICE].size() == 0) {
    264     listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_DEVICE);
    265   }
    266 }
    267 
    268 void ShillPropertyHandler::ManagerPropertyChanged(const std::string& key,
    269                                                   const base::Value& value) {
    270   NET_LOG_DEBUG("ManagerPropertyChanged", key);
    271   if (key == shill::kDefaultServiceProperty) {
    272     std::string service_path;
    273     value.GetAsString(&service_path);
    274     listener_->DefaultNetworkServiceChanged(service_path);
    275   } else if (key == shill::kServiceCompleteListProperty) {
    276     const base::ListValue* vlist = GetListValue(key, value);
    277     if (vlist) {
    278       listener_->UpdateManagedList(ManagedState::MANAGED_TYPE_NETWORK, *vlist);
    279       UpdateProperties(ManagedState::MANAGED_TYPE_NETWORK, *vlist);
    280       UpdateObserved(ManagedState::MANAGED_TYPE_NETWORK, *vlist);
    281     }
    282   } else if (key == shill::kDevicesProperty) {
    283     const base::ListValue* vlist = GetListValue(key, value);
    284     if (vlist) {
    285       listener_->UpdateManagedList(ManagedState::MANAGED_TYPE_DEVICE, *vlist);
    286       UpdateProperties(ManagedState::MANAGED_TYPE_DEVICE, *vlist);
    287       UpdateObserved(ManagedState::MANAGED_TYPE_DEVICE, *vlist);
    288     }
    289   } else if (key == shill::kAvailableTechnologiesProperty) {
    290     const base::ListValue* vlist = GetListValue(key, value);
    291     if (vlist)
    292       UpdateAvailableTechnologies(*vlist);
    293   } else if (key == shill::kEnabledTechnologiesProperty) {
    294     const base::ListValue* vlist = GetListValue(key, value);
    295     if (vlist)
    296       UpdateEnabledTechnologies(*vlist);
    297   } else if (key == shill::kUninitializedTechnologiesProperty) {
    298     const base::ListValue* vlist = GetListValue(key, value);
    299     if (vlist)
    300       UpdateUninitializedTechnologies(*vlist);
    301   } else if (key == shill::kProfilesProperty) {
    302     listener_->ProfileListChanged();
    303   } else if (key == shill::kCheckPortalListProperty) {
    304     std::string check_portal_list;
    305     if (value.GetAsString(&check_portal_list))
    306       listener_->CheckPortalListChanged(check_portal_list);
    307   } else {
    308     VLOG(2) << "Ignored Manager Property: " << key;
    309   }
    310 }
    311 
    312 void ShillPropertyHandler::UpdateProperties(ManagedState::ManagedType type,
    313                                             const base::ListValue& entries) {
    314   std::set<std::string>& requested_updates = requested_updates_[type];
    315   std::set<std::string> new_requested_updates;
    316   NET_LOG_DEBUG("UpdateProperties: " + ManagedState::TypeToString(type),
    317                 base::StringPrintf("%" PRIuS, entries.GetSize()));
    318   for (base::ListValue::const_iterator iter = entries.begin();
    319        iter != entries.end(); ++iter) {
    320     std::string path;
    321     (*iter)->GetAsString(&path);
    322     if (path.empty())
    323       continue;
    324 
    325     // We add a special case for devices here to work around an issue in shill
    326     // that prevents it from sending property changed signals for cellular
    327     // devices (see crbug.com/321854).
    328     if (type == ManagedState::MANAGED_TYPE_DEVICE ||
    329         requested_updates.find(path) == requested_updates.end()) {
    330       RequestProperties(type, path);
    331     }
    332     new_requested_updates.insert(path);
    333   }
    334   requested_updates.swap(new_requested_updates);
    335 }
    336 
    337 void ShillPropertyHandler::UpdateObserved(ManagedState::ManagedType type,
    338                                           const base::ListValue& entries) {
    339   ShillPropertyObserverMap& observer_map =
    340       (type == ManagedState::MANAGED_TYPE_NETWORK)
    341       ? observed_networks_ : observed_devices_;
    342   ShillPropertyObserverMap new_observed;
    343   for (base::ListValue::const_iterator iter1 = entries.begin();
    344        iter1 != entries.end(); ++iter1) {
    345     std::string path;
    346     (*iter1)->GetAsString(&path);
    347     if (path.empty())
    348       continue;
    349     ShillPropertyObserverMap::iterator iter2 = observer_map.find(path);
    350     if (iter2 != observer_map.end()) {
    351       new_observed[path] = iter2->second;
    352     } else {
    353       // Create an observer for future updates.
    354       new_observed[path] = new ShillPropertyObserver(
    355           type, path, base::Bind(
    356               &ShillPropertyHandler::PropertyChangedCallback, AsWeakPtr()));
    357     }
    358     observer_map.erase(path);
    359     // Limit the number of observed services.
    360     if (new_observed.size() >= kMaxObserved)
    361       break;
    362   }
    363   // Delete network service observers still in observer_map.
    364   for (ShillPropertyObserverMap::iterator iter =  observer_map.begin();
    365        iter != observer_map.end(); ++iter) {
    366     delete iter->second;
    367   }
    368   observer_map.swap(new_observed);
    369 }
    370 
    371 void ShillPropertyHandler::UpdateAvailableTechnologies(
    372     const base::ListValue& technologies) {
    373   std::stringstream technologies_str;
    374   technologies_str << technologies;
    375   NET_LOG_EVENT("AvailableTechnologies:", technologies_str.str());
    376   available_technologies_.clear();
    377   for (base::ListValue::const_iterator iter = technologies.begin();
    378        iter != technologies.end(); ++iter) {
    379     std::string technology;
    380     (*iter)->GetAsString(&technology);
    381     DCHECK(!technology.empty());
    382     available_technologies_.insert(technology);
    383   }
    384   listener_->TechnologyListChanged();
    385 }
    386 
    387 void ShillPropertyHandler::UpdateEnabledTechnologies(
    388     const base::ListValue& technologies) {
    389   std::stringstream technologies_str;
    390   technologies_str << technologies;
    391   NET_LOG_EVENT("EnabledTechnologies:", technologies_str.str());
    392   enabled_technologies_.clear();
    393   for (base::ListValue::const_iterator iter = technologies.begin();
    394        iter != technologies.end(); ++iter) {
    395     std::string technology;
    396     (*iter)->GetAsString(&technology);
    397     DCHECK(!technology.empty());
    398     enabled_technologies_.insert(technology);
    399     enabling_technologies_.erase(technology);
    400   }
    401   listener_->TechnologyListChanged();
    402 }
    403 
    404 void ShillPropertyHandler::UpdateUninitializedTechnologies(
    405     const base::ListValue& technologies) {
    406   std::stringstream technologies_str;
    407   technologies_str << technologies;
    408   NET_LOG_EVENT("UninitializedTechnologies:", technologies_str.str());
    409   uninitialized_technologies_.clear();
    410   for (base::ListValue::const_iterator iter = technologies.begin();
    411        iter != technologies.end(); ++iter) {
    412     std::string technology;
    413     (*iter)->GetAsString(&technology);
    414     DCHECK(!technology.empty());
    415     uninitialized_technologies_.insert(technology);
    416   }
    417   listener_->TechnologyListChanged();
    418 }
    419 
    420 void ShillPropertyHandler::EnableTechnologyFailed(
    421     const std::string& technology,
    422     const network_handler::ErrorCallback& error_callback,
    423     const std::string& dbus_error_name,
    424     const std::string& dbus_error_message) {
    425   enabling_technologies_.erase(technology);
    426   network_handler::ShillErrorCallbackFunction(
    427       "EnableTechnology Failed",
    428       technology, error_callback,
    429       dbus_error_name, dbus_error_message);
    430 }
    431 
    432 void ShillPropertyHandler::GetPropertiesCallback(
    433     ManagedState::ManagedType type,
    434     const std::string& path,
    435     DBusMethodCallStatus call_status,
    436     const base::DictionaryValue& properties) {
    437   NET_LOG_DEBUG("GetPropertiesCallback: " + ManagedState::TypeToString(type),
    438                 path);
    439   pending_updates_[type].erase(path);
    440   if (call_status != DBUS_METHOD_CALL_SUCCESS) {
    441     // The shill service no longer exists.  This can happen when a network
    442     // has been removed.
    443     NET_LOG_DEBUG("Failed to get properties",
    444                   base::StringPrintf("%s: %d", path.c_str(), call_status));
    445     return;
    446   }
    447   listener_->UpdateManagedStateProperties(type, path, properties);
    448 
    449   if (type == ManagedState::MANAGED_TYPE_NETWORK) {
    450     // Request IPConfig properties.
    451     const base::Value* value;
    452     if (properties.GetWithoutPathExpansion(shill::kIPConfigProperty, &value))
    453       RequestIPConfig(type, path, *value);
    454   } else if (type == ManagedState::MANAGED_TYPE_DEVICE) {
    455     // Clear and request IPConfig properties for each entry in IPConfigs.
    456     const base::Value* value;
    457     if (properties.GetWithoutPathExpansion(shill::kIPConfigsProperty, &value))
    458       RequestIPConfigsList(type, path, *value);
    459   }
    460 
    461   // Notify the listener only when all updates for that type have completed.
    462   if (pending_updates_[type].size() == 0)
    463     listener_->ManagedStateListChanged(type);
    464 }
    465 
    466 void ShillPropertyHandler::PropertyChangedCallback(
    467     ManagedState::ManagedType type,
    468     const std::string& path,
    469     const std::string& key,
    470     const base::Value& value) {
    471   if (type == ManagedState::MANAGED_TYPE_NETWORK &&
    472       key == shill::kIPConfigProperty) {
    473     RequestIPConfig(type, path, value);
    474   } else if (type == ManagedState::MANAGED_TYPE_DEVICE &&
    475       key == shill::kIPConfigsProperty) {
    476     RequestIPConfigsList(type, path, value);
    477   }
    478 
    479   if (type == ManagedState::MANAGED_TYPE_NETWORK)
    480     listener_->UpdateNetworkServiceProperty(path, key, value);
    481   else if (type == ManagedState::MANAGED_TYPE_DEVICE)
    482     listener_->UpdateDeviceProperty(path, key, value);
    483   else
    484     NOTREACHED();
    485 }
    486 
    487 void ShillPropertyHandler::RequestIPConfig(
    488     ManagedState::ManagedType type,
    489     const std::string& path,
    490     const base::Value& ip_config_path_value) {
    491   std::string ip_config_path;
    492   if (!ip_config_path_value.GetAsString(&ip_config_path) ||
    493       ip_config_path.empty()) {
    494     NET_LOG_ERROR("Invalid IPConfig", path);
    495     return;
    496   }
    497   DBusThreadManager::Get()->GetShillIPConfigClient()->GetProperties(
    498       dbus::ObjectPath(ip_config_path),
    499       base::Bind(&ShillPropertyHandler::GetIPConfigCallback,
    500                  AsWeakPtr(), type, path, ip_config_path));
    501 }
    502 
    503 void ShillPropertyHandler::RequestIPConfigsList(
    504     ManagedState::ManagedType type,
    505     const std::string& path,
    506     const base::Value& ip_config_list_value) {
    507   const base::ListValue* ip_configs;
    508   if (!ip_config_list_value.GetAsList(&ip_configs))
    509     return;
    510   for (base::ListValue::const_iterator iter = ip_configs->begin();
    511        iter != ip_configs->end(); ++iter) {
    512     RequestIPConfig(type, path, **iter);
    513   }
    514 }
    515 
    516 void ShillPropertyHandler::GetIPConfigCallback(
    517     ManagedState::ManagedType type,
    518     const std::string& path,
    519     const std::string& ip_config_path,
    520     DBusMethodCallStatus call_status,
    521     const base::DictionaryValue& properties)  {
    522   if (call_status != DBUS_METHOD_CALL_SUCCESS) {
    523     NET_LOG_ERROR("Failed to get IP Config properties",
    524                   base::StringPrintf("%s: %d", path.c_str(), call_status));
    525     return;
    526   }
    527   NET_LOG_EVENT("IP Config properties received", path);
    528   listener_->UpdateIPConfigProperties(type, path, ip_config_path, properties);
    529 }
    530 
    531 }  // namespace internal
    532 }  // namespace chromeos
    533