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