shill_property_handler.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
1116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch// Use of this source code is governed by a BSD-style license that can be
3116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch// found in the LICENSE file.
4116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
5116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "chromeos/network/shill_property_handler.h"
6116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
7116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "base/bind.h"
8116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "base/format_macros.h"
9116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "base/stl_util.h"
10116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "base/strings/string_util.h"
11116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "base/strings/stringprintf.h"
12116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "base/values.h"
13116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "chromeos/dbus/dbus_thread_manager.h"
14116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "chromeos/dbus/shill_device_client.h"
151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "chromeos/dbus/shill_ipconfig_client.h"
16116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "chromeos/dbus/shill_manager_client.h"
17116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "chromeos/dbus/shill_profile_client.h"
18116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#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
24namespace {
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.
28const size_t kMaxObserved = 100;
29
30const 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
42namespace chromeos {
43namespace 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.
48class 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
101ShillPropertyHandler::ShillPropertyHandler(Listener* listener)
102    : listener_(listener),
103      shill_manager_(DBusThreadManager::Get()->GetShillManagerClient()) {
104}
105
106ShillPropertyHandler::~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
116void ShillPropertyHandler::Init() {
117  UpdateManagerProperties();
118  shill_manager_->AddPropertyChangedObserver(this);
119}
120
121void ShillPropertyHandler::UpdateManagerProperties() {
122  NET_LOG_EVENT("UpdateManagerProperties", "");
123  shill_manager_->GetProperties(
124      base::Bind(&ShillPropertyHandler::ManagerPropertiesCallback,
125                 AsWeakPtr()));
126}
127
128bool ShillPropertyHandler::IsTechnologyAvailable(
129    const std::string& technology) const {
130  return available_technologies_.count(technology) != 0;
131}
132
133bool ShillPropertyHandler::IsTechnologyEnabled(
134    const std::string& technology) const {
135  return enabled_technologies_.count(technology) != 0;
136}
137
138bool ShillPropertyHandler::IsTechnologyEnabling(
139    const std::string& technology) const {
140  return enabling_technologies_.count(technology) != 0;
141}
142
143bool ShillPropertyHandler::IsTechnologyUninitialized(
144    const std::string& technology) const {
145  return uninitialized_technologies_.count(technology) != 0;
146}
147
148void 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                   technology, error_callback));
168  }
169}
170
171void ShillPropertyHandler::SetCheckPortalList(
172    const std::string& check_portal_list) {
173  base::StringValue value(check_portal_list);
174  shill_manager_->SetProperty(
175      flimflam::kCheckPortalListProperty,
176      value,
177      base::Bind(&base::DoNothing),
178      base::Bind(&network_handler::ShillErrorCallbackFunction,
179                 "", network_handler::ErrorCallback()));
180}
181
182void ShillPropertyHandler::RequestScan() const {
183  shill_manager_->RequestScan(
184      "",
185      base::Bind(&base::DoNothing),
186      base::Bind(&network_handler::ShillErrorCallbackFunction,
187                 "", network_handler::ErrorCallback()));
188}
189
190void ShillPropertyHandler::ConnectToBestServices() const {
191  NET_LOG_EVENT("ConnectToBestServices", "");
192  shill_manager_->ConnectToBestServices(
193      base::Bind(&base::DoNothing),
194      base::Bind(&network_handler::ShillErrorCallbackFunction,
195                 "", network_handler::ErrorCallback()));
196}
197
198void ShillPropertyHandler::RequestProperties(ManagedState::ManagedType type,
199                                             const std::string& path) {
200  VLOG(2) << "Request Properties: " << type << " : " << path;
201  if (pending_updates_[type].find(path) != pending_updates_[type].end())
202    return;  // Update already requested.
203
204  pending_updates_[type].insert(path);
205  if (type == ManagedState::MANAGED_TYPE_NETWORK ||
206      type == ManagedState::MANAGED_TYPE_FAVORITE) {
207    DBusThreadManager::Get()->GetShillServiceClient()->GetProperties(
208        dbus::ObjectPath(path),
209        base::Bind(&ShillPropertyHandler::GetPropertiesCallback,
210                   AsWeakPtr(), type, path));
211  } else if (type == ManagedState::MANAGED_TYPE_DEVICE) {
212    DBusThreadManager::Get()->GetShillDeviceClient()->GetProperties(
213        dbus::ObjectPath(path),
214        base::Bind(&ShillPropertyHandler::GetPropertiesCallback,
215                   AsWeakPtr(), type, path));
216  } else {
217    NOTREACHED();
218  }
219}
220
221void ShillPropertyHandler::OnPropertyChanged(const std::string& key,
222                                             const base::Value& value) {
223  if (ManagerPropertyChanged(key, value)) {
224    std::string detail = key;
225    detail += " = " + network_event_log::ValueAsString(value);
226    NET_LOG_DEBUG("ManagerPropertyChanged", detail);
227    listener_->NotifyManagerPropertyChanged();
228  }
229  CheckPendingStateListUpdates(key);
230}
231
232//------------------------------------------------------------------------------
233// Private methods
234
235void ShillPropertyHandler::ManagerPropertiesCallback(
236    DBusMethodCallStatus call_status,
237    const base::DictionaryValue& properties) {
238  if (call_status != DBUS_METHOD_CALL_SUCCESS) {
239    NET_LOG_ERROR("ManagerPropertiesCallback",
240                  base::StringPrintf("Failed: %d", call_status));
241    return;
242  }
243  NET_LOG_EVENT("ManagerPropertiesCallback", "Success");
244  bool notify = false;
245  const base::Value* update_service_value = NULL;
246  const base::Value* update_service_complete_value = NULL;
247  for (base::DictionaryValue::Iterator iter(properties);
248       !iter.IsAtEnd(); iter.Advance()) {
249    // Defer updating Services until all other properties have been updated.
250    if (iter.key() == flimflam::kServicesProperty)
251      update_service_value = &iter.value();
252    else if (iter.key() == shill::kServiceCompleteListProperty)
253      update_service_complete_value = &iter.value();
254    else
255      notify |= ManagerPropertyChanged(iter.key(), iter.value());
256  }
257  // Update Services which can safely assume other properties have been set.
258  if (update_service_value) {
259    notify |= ManagerPropertyChanged(flimflam::kServicesProperty,
260                                     *update_service_value);
261  }
262  // Update ServiceCompleteList which skips entries that have already been
263  // requested for Services.
264  if (update_service_complete_value) {
265    notify |= ManagerPropertyChanged(shill::kServiceCompleteListProperty,
266                                     *update_service_complete_value);
267  }
268
269  if (notify)
270    listener_->NotifyManagerPropertyChanged();
271  CheckPendingStateListUpdates("");
272}
273
274void ShillPropertyHandler::CheckPendingStateListUpdates(
275    const std::string& key) {
276  // Once there are no pending updates, signal the state list changed callbacks.
277  if ((key.empty() || key == flimflam::kServicesProperty) &&
278      pending_updates_[ManagedState::MANAGED_TYPE_NETWORK].size() == 0) {
279    listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_NETWORK);
280  }
281  // Both Network update requests and Favorite update requests will affect
282  // the list of favorites, so wait for both to complete.
283  if ((key.empty() || key == shill::kServiceCompleteListProperty) &&
284      pending_updates_[ManagedState::MANAGED_TYPE_NETWORK].size() == 0 &&
285      pending_updates_[ManagedState::MANAGED_TYPE_FAVORITE].size() == 0) {
286    listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_FAVORITE);
287  }
288  if ((key.empty() || key == flimflam::kDevicesProperty) &&
289      pending_updates_[ManagedState::MANAGED_TYPE_DEVICE].size() == 0) {
290    listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_DEVICE);
291  }
292}
293
294bool ShillPropertyHandler::ManagerPropertyChanged(const std::string& key,
295                                                  const base::Value& value) {
296  bool notify_manager_changed = false;
297  if (key == flimflam::kServicesProperty) {
298    const base::ListValue* vlist = GetListValue(key, value);
299    if (vlist) {
300      listener_->UpdateManagedList(ManagedState::MANAGED_TYPE_NETWORK, *vlist);
301      UpdateProperties(ManagedState::MANAGED_TYPE_NETWORK, *vlist);
302      // UpdateObserved used to use kServiceWatchListProperty for TYPE_NETWORK,
303      // however that prevents us from receiving Strength updates from inactive
304      // networks. The overhead for observing all services is not unreasonable
305      // (and we limit the max number of observed services to kMaxObserved).
306      UpdateObserved(ManagedState::MANAGED_TYPE_NETWORK, *vlist);
307    }
308  } else if (key == shill::kServiceCompleteListProperty) {
309    const ListValue* vlist = GetListValue(key, value);
310    if (vlist) {
311      listener_->UpdateManagedList(ManagedState::MANAGED_TYPE_FAVORITE, *vlist);
312      UpdateProperties(ManagedState::MANAGED_TYPE_FAVORITE, *vlist);
313    }
314  } else if (key == flimflam::kDevicesProperty) {
315    const base::ListValue* vlist = GetListValue(key, value);
316    if (vlist) {
317      listener_->UpdateManagedList(ManagedState::MANAGED_TYPE_DEVICE, *vlist);
318      UpdateProperties(ManagedState::MANAGED_TYPE_DEVICE, *vlist);
319      UpdateObserved(ManagedState::MANAGED_TYPE_DEVICE, *vlist);
320    }
321  } else if (key == flimflam::kAvailableTechnologiesProperty) {
322    const base::ListValue* vlist = GetListValue(key, value);
323    if (vlist) {
324      UpdateAvailableTechnologies(*vlist);
325      notify_manager_changed = true;
326    }
327  } else if (key == flimflam::kEnabledTechnologiesProperty) {
328    const base::ListValue* vlist = GetListValue(key, value);
329    if (vlist) {
330      UpdateEnabledTechnologies(*vlist);
331      notify_manager_changed = true;
332    }
333  } else if (key == shill::kUninitializedTechnologiesProperty) {
334    const base::ListValue* vlist = GetListValue(key, value);
335    if (vlist) {
336      UpdateUninitializedTechnologies(*vlist);
337      notify_manager_changed = true;
338    }
339  } else if (key == flimflam::kProfilesProperty) {
340    listener_->ProfileListChanged();
341  } else if (key == flimflam::kCheckPortalListProperty) {
342    std::string check_portal_list;
343    if (value.GetAsString(&check_portal_list)) {
344      listener_->CheckPortalListChanged(check_portal_list);
345      notify_manager_changed = true;
346    }
347  } else {
348    VLOG(2) << "Ignored Manager Property: " << key;
349  }
350  return notify_manager_changed;
351}
352
353void ShillPropertyHandler::UpdateProperties(ManagedState::ManagedType type,
354                                            const base::ListValue& entries) {
355  std::set<std::string>& requested_updates = requested_updates_[type];
356  std::set<std::string>& requested_service_updates =
357      requested_updates_[ManagedState::MANAGED_TYPE_NETWORK];  // For favorites
358  std::set<std::string> new_requested_updates;
359  VLOG(2) << "Update Properties: " << type << " Entries: " << entries.GetSize();
360  for (base::ListValue::const_iterator iter = entries.begin();
361       iter != entries.end(); ++iter) {
362    std::string path;
363    (*iter)->GetAsString(&path);
364    if (path.empty())
365      continue;
366    if (type == ManagedState::MANAGED_TYPE_FAVORITE &&
367        requested_service_updates.count(path) > 0)
368      continue;  // Update already requested
369    if (requested_updates.find(path) == requested_updates.end())
370      RequestProperties(type, path);
371    new_requested_updates.insert(path);
372  }
373  requested_updates.swap(new_requested_updates);
374}
375
376void ShillPropertyHandler::UpdateObserved(ManagedState::ManagedType type,
377                                          const base::ListValue& entries) {
378  DCHECK(type == ManagedState::MANAGED_TYPE_NETWORK ||
379         type == ManagedState::MANAGED_TYPE_DEVICE);
380  ShillPropertyObserverMap& observer_map =
381      (type == ManagedState::MANAGED_TYPE_NETWORK)
382      ? observed_networks_ : observed_devices_;
383  ShillPropertyObserverMap new_observed;
384  for (base::ListValue::const_iterator iter1 = entries.begin();
385       iter1 != entries.end(); ++iter1) {
386    std::string path;
387    (*iter1)->GetAsString(&path);
388    if (path.empty())
389      continue;
390    ShillPropertyObserverMap::iterator iter2 = observer_map.find(path);
391    if (iter2 != observer_map.end()) {
392      new_observed[path] = iter2->second;
393    } else {
394      // Create an observer for future updates.
395      new_observed[path] = new ShillPropertyObserver(
396          type, path, base::Bind(
397              &ShillPropertyHandler::PropertyChangedCallback, AsWeakPtr()));
398      NET_LOG_DEBUG("StartObserving", path);
399    }
400    observer_map.erase(path);
401    // Limit the number of observed services.
402    if (new_observed.size() >= kMaxObserved)
403      break;
404  }
405  // Delete network service observers still in observer_map.
406  for (ShillPropertyObserverMap::iterator iter =  observer_map.begin();
407       iter != observer_map.end(); ++iter) {
408    NET_LOG_DEBUG("StopObserving", iter->first);
409    delete iter->second;
410  }
411  observer_map.swap(new_observed);
412}
413
414void ShillPropertyHandler::UpdateAvailableTechnologies(
415    const base::ListValue& technologies) {
416  available_technologies_.clear();
417  NET_LOG_EVENT("AvailableTechnologiesChanged",
418                base::StringPrintf("Size: %"PRIuS, technologies.GetSize()));
419  for (base::ListValue::const_iterator iter = technologies.begin();
420       iter != technologies.end(); ++iter) {
421    std::string technology;
422    (*iter)->GetAsString(&technology);
423    DCHECK(!technology.empty());
424    available_technologies_.insert(technology);
425  }
426}
427
428void ShillPropertyHandler::UpdateEnabledTechnologies(
429    const base::ListValue& technologies) {
430  enabled_technologies_.clear();
431  NET_LOG_EVENT("EnabledTechnologiesChanged",
432                base::StringPrintf("Size: %"PRIuS, technologies.GetSize()));
433  for (base::ListValue::const_iterator iter = technologies.begin();
434       iter != technologies.end(); ++iter) {
435    std::string technology;
436    (*iter)->GetAsString(&technology);
437    DCHECK(!technology.empty());
438    enabled_technologies_.insert(technology);
439    enabling_technologies_.erase(technology);
440  }
441}
442
443void ShillPropertyHandler::UpdateUninitializedTechnologies(
444    const base::ListValue& technologies) {
445  uninitialized_technologies_.clear();
446  NET_LOG_EVENT("UninitializedTechnologiesChanged",
447                base::StringPrintf("Size: %"PRIuS, technologies.GetSize()));
448  for (base::ListValue::const_iterator iter = technologies.begin();
449       iter != technologies.end(); ++iter) {
450    std::string technology;
451    (*iter)->GetAsString(&technology);
452    DCHECK(!technology.empty());
453    uninitialized_technologies_.insert(technology);
454  }
455}
456
457void ShillPropertyHandler::EnableTechnologyFailed(
458    const std::string& technology,
459    const network_handler::ErrorCallback& error_callback,
460    const std::string& error_name,
461    const std::string& error_message) {
462  enabling_technologies_.erase(technology);
463  network_handler::ShillErrorCallbackFunction(
464      technology, error_callback, error_name, error_message);
465}
466
467void ShillPropertyHandler::GetPropertiesCallback(
468    ManagedState::ManagedType type,
469    const std::string& path,
470    DBusMethodCallStatus call_status,
471    const base::DictionaryValue& properties) {
472  VLOG(2) << "GetPropertiesCallback: " << type << " : " << path;
473  pending_updates_[type].erase(path);
474  if (call_status != DBUS_METHOD_CALL_SUCCESS) {
475    NET_LOG_ERROR("Failed to get properties",
476                  base::StringPrintf("%s: %d", path.c_str(), call_status));
477    return;
478  }
479  listener_->UpdateManagedStateProperties(type, path, properties);
480  // Update Favorite properties for networks in the Services list.
481  if (type == ManagedState::MANAGED_TYPE_NETWORK) {
482    listener_->UpdateManagedStateProperties(
483        ManagedState::MANAGED_TYPE_FAVORITE, path, properties);
484  }
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
509void 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
522void 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
541void 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  const base::Value* ip_address;
552  if (!properties.GetWithoutPathExpansion(flimflam::kAddressProperty,
553                                          &ip_address)) {
554    LOG(ERROR) << "Failed to get IP Address property for: " << service_path;
555    return;
556  }
557  listener_->UpdateNetworkServiceProperty(
558      service_path,
559      NetworkState::IPConfigProperty(flimflam::kAddressProperty),
560      *ip_address);
561
562  const base::Value* dns_servers = NULL;
563  if (!properties.GetWithoutPathExpansion(
564          flimflam::kNameServersProperty, &dns_servers)) {
565    LOG(ERROR) << "Failed to get Name servers property for: " << service_path;
566    return;
567  }
568  listener_->UpdateNetworkServiceProperty(
569      service_path,
570      NetworkState::IPConfigProperty(flimflam::kNameServersProperty),
571      *dns_servers);
572}
573
574void ShillPropertyHandler::NetworkDevicePropertyChangedCallback(
575    const std::string& path,
576    const std::string& key,
577    const base::Value& value) {
578  listener_->UpdateDeviceProperty(path, key, value);
579}
580
581}  // namespace internal
582}  // namespace chromeos
583