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       flimflam::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   if (ManagerPropertyChanged(key, value)) {
    228     std::string detail = key;
    229     detail += " = " + network_event_log::ValueAsString(value);
    230     NET_LOG_DEBUG("ManagerPropertyChanged", detail);
    231     listener_->NotifyManagerPropertyChanged();
    232   }
    233   CheckPendingStateListUpdates(key);
    234 }
    235 
    236 //------------------------------------------------------------------------------
    237 // Private methods
    238 
    239 void ShillPropertyHandler::ManagerPropertiesCallback(
    240     DBusMethodCallStatus call_status,
    241     const base::DictionaryValue& properties) {
    242   if (call_status != DBUS_METHOD_CALL_SUCCESS) {
    243     NET_LOG_ERROR("ManagerPropertiesCallback",
    244                   base::StringPrintf("Failed: %d", call_status));
    245     return;
    246   }
    247   NET_LOG_EVENT("ManagerPropertiesCallback", "Success");
    248   bool notify = false;
    249   const base::Value* update_service_value = NULL;
    250   const base::Value* update_service_complete_value = NULL;
    251   for (base::DictionaryValue::Iterator iter(properties);
    252        !iter.IsAtEnd(); iter.Advance()) {
    253     // Defer updating Services until all other properties have been updated.
    254     if (iter.key() == flimflam::kServicesProperty)
    255       update_service_value = &iter.value();
    256     else if (iter.key() == shill::kServiceCompleteListProperty)
    257       update_service_complete_value = &iter.value();
    258     else
    259       notify |= ManagerPropertyChanged(iter.key(), iter.value());
    260   }
    261   // Update Services which can safely assume other properties have been set.
    262   if (update_service_value) {
    263     notify |= ManagerPropertyChanged(flimflam::kServicesProperty,
    264                                      *update_service_value);
    265   }
    266   // Update ServiceCompleteList which skips entries that have already been
    267   // requested for Services.
    268   if (update_service_complete_value) {
    269     notify |= ManagerPropertyChanged(shill::kServiceCompleteListProperty,
    270                                      *update_service_complete_value);
    271   }
    272 
    273   if (notify)
    274     listener_->NotifyManagerPropertyChanged();
    275   CheckPendingStateListUpdates("");
    276 }
    277 
    278 void ShillPropertyHandler::CheckPendingStateListUpdates(
    279     const std::string& key) {
    280   // Once there are no pending updates, signal the state list changed callbacks.
    281   if ((key.empty() || key == flimflam::kServicesProperty) &&
    282       pending_updates_[ManagedState::MANAGED_TYPE_NETWORK].size() == 0) {
    283     listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_NETWORK);
    284   }
    285   // Both Network update requests and Favorite update requests will affect
    286   // the list of favorites, so wait for both to complete.
    287   if ((key.empty() || key == shill::kServiceCompleteListProperty) &&
    288       pending_updates_[ManagedState::MANAGED_TYPE_NETWORK].size() == 0 &&
    289       pending_updates_[ManagedState::MANAGED_TYPE_FAVORITE].size() == 0) {
    290     listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_FAVORITE);
    291   }
    292   if ((key.empty() || key == flimflam::kDevicesProperty) &&
    293       pending_updates_[ManagedState::MANAGED_TYPE_DEVICE].size() == 0) {
    294     listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_DEVICE);
    295   }
    296 }
    297 
    298 bool ShillPropertyHandler::ManagerPropertyChanged(const std::string& key,
    299                                                   const base::Value& value) {
    300   bool notify_manager_changed = false;
    301   if (key == flimflam::kServicesProperty) {
    302     const base::ListValue* vlist = GetListValue(key, value);
    303     if (vlist) {
    304       listener_->UpdateManagedList(ManagedState::MANAGED_TYPE_NETWORK, *vlist);
    305       UpdateProperties(ManagedState::MANAGED_TYPE_NETWORK, *vlist);
    306       // UpdateObserved used to use kServiceWatchListProperty for TYPE_NETWORK,
    307       // however that prevents us from receiving Strength updates from inactive
    308       // networks. The overhead for observing all services is not unreasonable
    309       // (and we limit the max number of observed services to kMaxObserved).
    310       UpdateObserved(ManagedState::MANAGED_TYPE_NETWORK, *vlist);
    311     }
    312   } else if (key == shill::kServiceCompleteListProperty) {
    313     const ListValue* vlist = GetListValue(key, value);
    314     if (vlist) {
    315       listener_->UpdateManagedList(ManagedState::MANAGED_TYPE_FAVORITE, *vlist);
    316       UpdateProperties(ManagedState::MANAGED_TYPE_FAVORITE, *vlist);
    317     }
    318   } else if (key == flimflam::kDevicesProperty) {
    319     const base::ListValue* vlist = GetListValue(key, value);
    320     if (vlist) {
    321       listener_->UpdateManagedList(ManagedState::MANAGED_TYPE_DEVICE, *vlist);
    322       UpdateProperties(ManagedState::MANAGED_TYPE_DEVICE, *vlist);
    323       UpdateObserved(ManagedState::MANAGED_TYPE_DEVICE, *vlist);
    324     }
    325   } else if (key == flimflam::kAvailableTechnologiesProperty) {
    326     const base::ListValue* vlist = GetListValue(key, value);
    327     if (vlist) {
    328       UpdateAvailableTechnologies(*vlist);
    329       notify_manager_changed = true;
    330     }
    331   } else if (key == flimflam::kEnabledTechnologiesProperty) {
    332     const base::ListValue* vlist = GetListValue(key, value);
    333     if (vlist) {
    334       UpdateEnabledTechnologies(*vlist);
    335       notify_manager_changed = true;
    336     }
    337   } else if (key == shill::kUninitializedTechnologiesProperty) {
    338     const base::ListValue* vlist = GetListValue(key, value);
    339     if (vlist) {
    340       UpdateUninitializedTechnologies(*vlist);
    341       notify_manager_changed = true;
    342     }
    343   } else if (key == flimflam::kProfilesProperty) {
    344     listener_->ProfileListChanged();
    345   } else if (key == flimflam::kCheckPortalListProperty) {
    346     std::string check_portal_list;
    347     if (value.GetAsString(&check_portal_list)) {
    348       listener_->CheckPortalListChanged(check_portal_list);
    349       notify_manager_changed = true;
    350     }
    351   } else {
    352     VLOG(2) << "Ignored Manager Property: " << key;
    353   }
    354   return notify_manager_changed;
    355 }
    356 
    357 void ShillPropertyHandler::UpdateProperties(ManagedState::ManagedType type,
    358                                             const base::ListValue& entries) {
    359   std::set<std::string>& requested_updates = requested_updates_[type];
    360   std::set<std::string>& requested_service_updates =
    361       requested_updates_[ManagedState::MANAGED_TYPE_NETWORK];  // For favorites
    362   std::set<std::string> new_requested_updates;
    363   VLOG(2) << "Update Properties: " << type << " Entries: " << entries.GetSize();
    364   for (base::ListValue::const_iterator iter = entries.begin();
    365        iter != entries.end(); ++iter) {
    366     std::string path;
    367     (*iter)->GetAsString(&path);
    368     if (path.empty())
    369       continue;
    370     if (type == ManagedState::MANAGED_TYPE_FAVORITE &&
    371         requested_service_updates.count(path) > 0)
    372       continue;  // Update already requested
    373     if (requested_updates.find(path) == requested_updates.end())
    374       RequestProperties(type, path);
    375     new_requested_updates.insert(path);
    376   }
    377   requested_updates.swap(new_requested_updates);
    378 }
    379 
    380 void ShillPropertyHandler::UpdateObserved(ManagedState::ManagedType type,
    381                                           const base::ListValue& entries) {
    382   DCHECK(type == ManagedState::MANAGED_TYPE_NETWORK ||
    383          type == ManagedState::MANAGED_TYPE_DEVICE);
    384   ShillPropertyObserverMap& observer_map =
    385       (type == ManagedState::MANAGED_TYPE_NETWORK)
    386       ? observed_networks_ : observed_devices_;
    387   ShillPropertyObserverMap new_observed;
    388   for (base::ListValue::const_iterator iter1 = entries.begin();
    389        iter1 != entries.end(); ++iter1) {
    390     std::string path;
    391     (*iter1)->GetAsString(&path);
    392     if (path.empty())
    393       continue;
    394     ShillPropertyObserverMap::iterator iter2 = observer_map.find(path);
    395     if (iter2 != observer_map.end()) {
    396       new_observed[path] = iter2->second;
    397     } else {
    398       // Create an observer for future updates.
    399       new_observed[path] = new ShillPropertyObserver(
    400           type, path, base::Bind(
    401               &ShillPropertyHandler::PropertyChangedCallback, AsWeakPtr()));
    402     }
    403     observer_map.erase(path);
    404     // Limit the number of observed services.
    405     if (new_observed.size() >= kMaxObserved)
    406       break;
    407   }
    408   // Delete network service observers still in observer_map.
    409   for (ShillPropertyObserverMap::iterator iter =  observer_map.begin();
    410        iter != observer_map.end(); ++iter) {
    411     delete iter->second;
    412   }
    413   observer_map.swap(new_observed);
    414 }
    415 
    416 void ShillPropertyHandler::UpdateAvailableTechnologies(
    417     const base::ListValue& technologies) {
    418   available_technologies_.clear();
    419   NET_LOG_EVENT("AvailableTechnologiesChanged",
    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     available_technologies_.insert(technology);
    427   }
    428 }
    429 
    430 void ShillPropertyHandler::UpdateEnabledTechnologies(
    431     const base::ListValue& technologies) {
    432   enabled_technologies_.clear();
    433   NET_LOG_EVENT("EnabledTechnologiesChanged",
    434                 base::StringPrintf("Size: %" PRIuS, technologies.GetSize()));
    435   for (base::ListValue::const_iterator iter = technologies.begin();
    436        iter != technologies.end(); ++iter) {
    437     std::string technology;
    438     (*iter)->GetAsString(&technology);
    439     DCHECK(!technology.empty());
    440     enabled_technologies_.insert(technology);
    441     enabling_technologies_.erase(technology);
    442   }
    443 }
    444 
    445 void ShillPropertyHandler::UpdateUninitializedTechnologies(
    446     const base::ListValue& technologies) {
    447   uninitialized_technologies_.clear();
    448   NET_LOG_EVENT("UninitializedTechnologiesChanged",
    449                 base::StringPrintf("Size: %" PRIuS, technologies.GetSize()));
    450   for (base::ListValue::const_iterator iter = technologies.begin();
    451        iter != technologies.end(); ++iter) {
    452     std::string technology;
    453     (*iter)->GetAsString(&technology);
    454     DCHECK(!technology.empty());
    455     uninitialized_technologies_.insert(technology);
    456   }
    457 }
    458 
    459 void ShillPropertyHandler::EnableTechnologyFailed(
    460     const std::string& technology,
    461     const network_handler::ErrorCallback& error_callback,
    462     const std::string& dbus_error_name,
    463     const std::string& dbus_error_message) {
    464   enabling_technologies_.erase(technology);
    465   network_handler::ShillErrorCallbackFunction(
    466       "EnableTechnology Failed",
    467       technology, error_callback,
    468       dbus_error_name, dbus_error_message);
    469 }
    470 
    471 void ShillPropertyHandler::GetPropertiesCallback(
    472     ManagedState::ManagedType type,
    473     const std::string& path,
    474     DBusMethodCallStatus call_status,
    475     const base::DictionaryValue& properties) {
    476   VLOG(2) << "GetPropertiesCallback: " << type << " : " << path;
    477   pending_updates_[type].erase(path);
    478   if (call_status != DBUS_METHOD_CALL_SUCCESS) {
    479     NET_LOG_ERROR("Failed to get properties",
    480                   base::StringPrintf("%s: %d", path.c_str(), call_status));
    481     return;
    482   }
    483   listener_->UpdateManagedStateProperties(type, path, properties);
    484   // Update Favorite properties for networks in the Services list.
    485   if (type == ManagedState::MANAGED_TYPE_NETWORK) {
    486     // Only networks with a ProfilePath set are Favorites.
    487     std::string profile_path;
    488     properties.GetStringWithoutPathExpansion(
    489         flimflam::kProfileProperty, &profile_path);
    490     if (!profile_path.empty()) {
    491       listener_->UpdateManagedStateProperties(
    492           ManagedState::MANAGED_TYPE_FAVORITE, path, properties);
    493     }
    494   }
    495   // Request IPConfig parameters for networks.
    496   if (type == ManagedState::MANAGED_TYPE_NETWORK &&
    497       properties.HasKey(shill::kIPConfigProperty)) {
    498     std::string ip_config_path;
    499     if (properties.GetString(shill::kIPConfigProperty, &ip_config_path)) {
    500       DBusThreadManager::Get()->GetShillIPConfigClient()->GetProperties(
    501           dbus::ObjectPath(ip_config_path),
    502           base::Bind(&ShillPropertyHandler::GetIPConfigCallback,
    503                      AsWeakPtr(), path));
    504     }
    505   }
    506 
    507   // Notify the listener only when all updates for that type have completed.
    508   if (pending_updates_[type].size() == 0) {
    509     listener_->ManagedStateListChanged(type);
    510     // Notify that Favorites have changed when notifying for Networks if there
    511     // are no additional Favorite updates pending.
    512     if (type == ManagedState::MANAGED_TYPE_NETWORK &&
    513         pending_updates_[ManagedState::MANAGED_TYPE_FAVORITE].size() == 0) {
    514       listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_FAVORITE);
    515     }
    516   }
    517 }
    518 
    519 void ShillPropertyHandler::PropertyChangedCallback(
    520     ManagedState::ManagedType type,
    521     const std::string& path,
    522     const std::string& key,
    523     const base::Value& value) {
    524   if (type == ManagedState::MANAGED_TYPE_NETWORK)
    525     NetworkServicePropertyChangedCallback(path, key, value);
    526   else if (type == ManagedState::MANAGED_TYPE_DEVICE)
    527     NetworkDevicePropertyChangedCallback(path, key, value);
    528   else
    529     NOTREACHED();
    530 }
    531 
    532 void ShillPropertyHandler::NetworkServicePropertyChangedCallback(
    533     const std::string& path,
    534     const std::string& key,
    535     const base::Value& value) {
    536   if (key == shill::kIPConfigProperty) {
    537     // Request the IPConfig for the network and update network properties
    538     // when the request completes.
    539     std::string ip_config_path;
    540     value.GetAsString(&ip_config_path);
    541     DCHECK(!ip_config_path.empty());
    542     DBusThreadManager::Get()->GetShillIPConfigClient()->GetProperties(
    543         dbus::ObjectPath(ip_config_path),
    544         base::Bind(&ShillPropertyHandler::GetIPConfigCallback,
    545                    AsWeakPtr(), path));
    546   } else {
    547     listener_->UpdateNetworkServiceProperty(path, key, value);
    548   }
    549 }
    550 
    551 void ShillPropertyHandler::GetIPConfigCallback(
    552     const std::string& service_path,
    553     DBusMethodCallStatus call_status,
    554     const base::DictionaryValue& properties)  {
    555   if (call_status != DBUS_METHOD_CALL_SUCCESS) {
    556     NET_LOG_ERROR("Failed to get IP Config properties",
    557                   base::StringPrintf("%s: %d",
    558                                      service_path.c_str(), call_status));
    559     return;
    560   }
    561   UpdateIPConfigProperty(service_path, properties,
    562                          flimflam::kAddressProperty);
    563   UpdateIPConfigProperty(service_path, properties,
    564                          flimflam::kNameServersProperty);
    565   UpdateIPConfigProperty(service_path, properties,
    566                          flimflam::kPrefixlenProperty);
    567   UpdateIPConfigProperty(service_path, properties,
    568                          flimflam::kGatewayProperty);
    569   UpdateIPConfigProperty(service_path, properties,
    570                          shill::kWebProxyAutoDiscoveryUrlProperty);
    571 }
    572 
    573 void ShillPropertyHandler::UpdateIPConfigProperty(
    574     const std::string& service_path,
    575     const base::DictionaryValue& properties,
    576     const char* property) {
    577   const base::Value* value;
    578   if (!properties.GetWithoutPathExpansion(property, &value)) {
    579     LOG(ERROR) << "Failed to get IPConfig property: " << property
    580                << ", for: " << service_path;
    581     return;
    582   }
    583   listener_->UpdateNetworkServiceProperty(
    584       service_path, NetworkState::IPConfigProperty(property), *value);
    585 }
    586 
    587 void ShillPropertyHandler::NetworkDevicePropertyChangedCallback(
    588     const std::string& path,
    589     const std::string& key,
    590     const base::Value& value) {
    591   listener_->UpdateDeviceProperty(path, key, value);
    592 }
    593 
    594 }  // namespace internal
    595 }  // namespace chromeos
    596