shill_property_handler.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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_service_client.h"
18#include "chromeos/network/network_event_log.h"
19#include "chromeos/network/network_state.h"
20#include "dbus/object_path.h"
21#include "third_party/cros_system_api/dbus/service_constants.h"
22
23namespace {
24
25// Limit the number of services or devices we observe. Since they are listed in
26// priority order, it should be reasonable to ignore services past this.
27const size_t kMaxObserved = 100;
28
29const base::ListValue* GetListValue(const std::string& key,
30                                    const base::Value& value) {
31  const base::ListValue* vlist = NULL;
32  if (!value.GetAsList(&vlist)) {
33    LOG(ERROR) << "Error parsing key as list: " << key;
34    return NULL;
35  }
36  return vlist;
37}
38
39}  // namespace
40
41namespace chromeos {
42namespace internal {
43
44// Class to manage Shill service property changed observers. Observers are
45// added on construction and removed on destruction. Runs the handler when
46// OnPropertyChanged is called.
47class ShillPropertyObserver : public ShillPropertyChangedObserver {
48 public:
49  typedef base::Callback<void(ManagedState::ManagedType type,
50                              const std::string& service,
51                              const std::string& name,
52                              const base::Value& value)> Handler;
53
54  ShillPropertyObserver(ManagedState::ManagedType type,
55                        const std::string& path,
56                        const Handler& handler)
57      : type_(type),
58        path_(path),
59        handler_(handler) {
60    if (type_ == ManagedState::MANAGED_TYPE_NETWORK) {
61      DBusThreadManager::Get()->GetShillServiceClient()->
62          AddPropertyChangedObserver(dbus::ObjectPath(path_), this);
63    } else if (type_ == ManagedState::MANAGED_TYPE_DEVICE) {
64      DBusThreadManager::Get()->GetShillDeviceClient()->
65          AddPropertyChangedObserver(dbus::ObjectPath(path_), this);
66    } else {
67      NOTREACHED();
68    }
69  }
70
71  virtual ~ShillPropertyObserver() {
72    if (type_ == ManagedState::MANAGED_TYPE_NETWORK) {
73      DBusThreadManager::Get()->GetShillServiceClient()->
74          RemovePropertyChangedObserver(dbus::ObjectPath(path_), this);
75    } else if (type_ == ManagedState::MANAGED_TYPE_DEVICE) {
76      DBusThreadManager::Get()->GetShillDeviceClient()->
77          RemovePropertyChangedObserver(dbus::ObjectPath(path_), this);
78    } else {
79      NOTREACHED();
80    }
81  }
82
83  // ShillPropertyChangedObserver overrides.
84  virtual void OnPropertyChanged(const std::string& key,
85                                 const base::Value& value) OVERRIDE {
86    handler_.Run(type_, path_, key, value);
87  }
88
89 private:
90  ManagedState::ManagedType type_;
91  std::string path_;
92  Handler handler_;
93
94  DISALLOW_COPY_AND_ASSIGN(ShillPropertyObserver);
95};
96
97//------------------------------------------------------------------------------
98// ShillPropertyHandler
99
100ShillPropertyHandler::ShillPropertyHandler(Listener* listener)
101    : listener_(listener),
102      shill_manager_(DBusThreadManager::Get()->GetShillManagerClient()) {
103}
104
105ShillPropertyHandler::~ShillPropertyHandler() {
106  // Delete network service observers.
107  STLDeleteContainerPairSecondPointers(
108      observed_networks_.begin(), observed_networks_.end());
109  STLDeleteContainerPairSecondPointers(
110      observed_devices_.begin(), observed_devices_.end());
111  CHECK(shill_manager_ == DBusThreadManager::Get()->GetShillManagerClient());
112  shill_manager_->RemovePropertyChangedObserver(this);
113}
114
115void ShillPropertyHandler::Init() {
116  shill_manager_->GetProperties(
117      base::Bind(&ShillPropertyHandler::ManagerPropertiesCallback,
118                 AsWeakPtr()));
119  shill_manager_->AddPropertyChangedObserver(this);
120}
121
122bool ShillPropertyHandler::IsTechnologyAvailable(
123    const std::string& technology) const {
124  return available_technologies_.count(technology) != 0;
125}
126
127bool ShillPropertyHandler::IsTechnologyEnabled(
128    const std::string& technology) const {
129  return enabled_technologies_.count(technology) != 0;
130}
131
132bool ShillPropertyHandler::IsTechnologyEnabling(
133    const std::string& technology) const {
134  return enabling_technologies_.count(technology) != 0;
135}
136
137bool ShillPropertyHandler::IsTechnologyUninitialized(
138    const std::string& technology) const {
139  return uninitialized_technologies_.count(technology) != 0;
140}
141
142void ShillPropertyHandler::SetTechnologyEnabled(
143    const std::string& technology,
144    bool enabled,
145    const network_handler::ErrorCallback& error_callback) {
146  if (enabled) {
147    enabling_technologies_.insert(technology);
148    shill_manager_->EnableTechnology(
149        technology,
150        base::Bind(&base::DoNothing),
151        base::Bind(&ShillPropertyHandler::EnableTechnologyFailed,
152                   AsWeakPtr(), technology, error_callback));
153  } else {
154    // Imediately clear locally from enabled and enabling lists.
155    enabled_technologies_.erase(technology);
156    enabling_technologies_.erase(technology);
157    shill_manager_->DisableTechnology(
158        technology,
159        base::Bind(&base::DoNothing),
160        base::Bind(&network_handler::ShillErrorCallbackFunction,
161                   technology, error_callback));
162  }
163}
164
165void ShillPropertyHandler::SetCheckPortalList(
166    const std::string& check_portal_list) {
167  base::StringValue value(check_portal_list);
168  shill_manager_->SetProperty(
169      flimflam::kCheckPortalListProperty,
170      value,
171      base::Bind(&base::DoNothing),
172      base::Bind(&network_handler::ShillErrorCallbackFunction,
173                 "", network_handler::ErrorCallback()));
174}
175
176void ShillPropertyHandler::RequestScan() const {
177  shill_manager_->RequestScan(
178      "",
179      base::Bind(&base::DoNothing),
180      base::Bind(&network_handler::ShillErrorCallbackFunction,
181                 "", network_handler::ErrorCallback()));
182}
183
184void ShillPropertyHandler::ConnectToBestServices() const {
185  NET_LOG_EVENT("ConnectToBestServices", "");
186  shill_manager_->ConnectToBestServices(
187      base::Bind(&base::DoNothing),
188      base::Bind(&network_handler::ShillErrorCallbackFunction,
189                 "", network_handler::ErrorCallback()));
190}
191
192void ShillPropertyHandler::RequestProperties(ManagedState::ManagedType type,
193                                             const std::string& path) {
194  if (pending_updates_[type].find(path) != pending_updates_[type].end())
195    return;  // Update already requested.
196
197  pending_updates_[type].insert(path);
198  if (type == ManagedState::MANAGED_TYPE_NETWORK) {
199    DBusThreadManager::Get()->GetShillServiceClient()->GetProperties(
200        dbus::ObjectPath(path),
201        base::Bind(&ShillPropertyHandler::GetPropertiesCallback,
202                   AsWeakPtr(), type, path));
203  } else if (type == ManagedState::MANAGED_TYPE_DEVICE) {
204    DBusThreadManager::Get()->GetShillDeviceClient()->GetProperties(
205        dbus::ObjectPath(path),
206        base::Bind(&ShillPropertyHandler::GetPropertiesCallback,
207                   AsWeakPtr(), type, path));
208  } else {
209    NOTREACHED();
210  }
211}
212
213void ShillPropertyHandler::OnPropertyChanged(const std::string& key,
214                                             const base::Value& value) {
215  if (ManagerPropertyChanged(key, value)) {
216    std::string detail = key;
217    detail += " = " + network_event_log::ValueAsString(value);
218    NET_LOG_DEBUG("ManagerPropertyChanged", detail);
219    listener_->NotifyManagerPropertyChanged();
220  }
221  // If the service or device list changed and there are no pending
222  // updates, signal the state list changed callback.
223  if ((key == flimflam::kServicesProperty) &&
224      pending_updates_[ManagedState::MANAGED_TYPE_NETWORK].size() == 0) {
225    listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_NETWORK);
226  }
227  if (key == flimflam::kDevicesProperty &&
228      pending_updates_[ManagedState::MANAGED_TYPE_DEVICE].size() == 0) {
229    listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_DEVICE);
230  }
231}
232
233//------------------------------------------------------------------------------
234// Private methods
235
236void ShillPropertyHandler::ManagerPropertiesCallback(
237    DBusMethodCallStatus call_status,
238    const base::DictionaryValue& properties) {
239  if (call_status != DBUS_METHOD_CALL_SUCCESS) {
240    NET_LOG_ERROR("ManagerPropertiesCallback",
241                  base::StringPrintf("Failed: %d", call_status));
242    return;
243  }
244  NET_LOG_EVENT("ManagerPropertiesCallback", "Success");
245  bool notify = false;
246  bool update_service_list = false;
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_list = true;
252    else
253      notify |= ManagerPropertyChanged(iter.key(), iter.value());
254  }
255  // Now update the service list which can safely assume other properties have
256  // been initially set.
257  if (update_service_list) {
258    const base::Value* value = NULL;
259    if (properties.GetWithoutPathExpansion(flimflam::kServicesProperty, &value))
260      notify |= ManagerPropertyChanged(flimflam::kServicesProperty, *value);
261  }
262  if (notify)
263    listener_->NotifyManagerPropertyChanged();
264  // If there are no pending updates, signal the state list changed callbacks.
265  if (pending_updates_[ManagedState::MANAGED_TYPE_NETWORK].size() == 0)
266    listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_NETWORK);
267  if (pending_updates_[ManagedState::MANAGED_TYPE_DEVICE].size() == 0)
268    listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_DEVICE);
269}
270
271bool ShillPropertyHandler::ManagerPropertyChanged(const std::string& key,
272                                                  const base::Value& value) {
273  bool notify_manager_changed = false;
274  if (key == flimflam::kServicesProperty) {
275    const base::ListValue* vlist = GetListValue(key, value);
276    if (vlist) {
277      listener_->UpdateManagedList(ManagedState::MANAGED_TYPE_NETWORK, *vlist);
278      // UpdateObserved used to use kServiceWatchListProperty for TYPE_NETWORK,
279      // however that prevents us from receiving Strength updates from inactive
280      // networks. The overhead for observing all services is not unreasonable
281      // (and we limit the max number of observed services to kMaxObserved).
282      UpdateObserved(ManagedState::MANAGED_TYPE_NETWORK, *vlist);
283    }
284  } else if (key == flimflam::kDevicesProperty) {
285    const ListValue* vlist = GetListValue(key, value);
286    if (vlist) {
287      listener_->UpdateManagedList(ManagedState::MANAGED_TYPE_DEVICE, *vlist);
288      UpdateObserved(ManagedState::MANAGED_TYPE_DEVICE, *vlist);
289    }
290  } else if (key == flimflam::kAvailableTechnologiesProperty) {
291    const base::ListValue* vlist = GetListValue(key, value);
292    if (vlist) {
293      UpdateAvailableTechnologies(*vlist);
294      notify_manager_changed = true;
295    }
296  } else if (key == flimflam::kEnabledTechnologiesProperty) {
297    const base::ListValue* vlist = GetListValue(key, value);
298    if (vlist) {
299      UpdateEnabledTechnologies(*vlist);
300      notify_manager_changed = true;
301    }
302  } else if (key == shill::kUninitializedTechnologiesProperty) {
303    const base::ListValue* vlist = GetListValue(key, value);
304    if (vlist) {
305      UpdateUninitializedTechnologies(*vlist);
306      notify_manager_changed = true;
307    }
308  } else if (key == flimflam::kProfilesProperty) {
309    listener_->ProfileListChanged();
310  } else if (key == flimflam::kCheckPortalListProperty) {
311    std::string check_portal_list;
312    if (value.GetAsString(&check_portal_list)) {
313      listener_->CheckPortalListChanged(check_portal_list);
314      notify_manager_changed = true;
315    }
316  }
317  return notify_manager_changed;
318}
319
320void ShillPropertyHandler::UpdateObserved(ManagedState::ManagedType type,
321                                          const base::ListValue& entries) {
322  ShillPropertyObserverMap& observer_map =
323      (type == ManagedState::MANAGED_TYPE_NETWORK)
324      ? observed_networks_ : observed_devices_;
325  ShillPropertyObserverMap new_observed;
326  for (base::ListValue::const_iterator iter1 = entries.begin();
327       iter1 != entries.end(); ++iter1) {
328    std::string path;
329    (*iter1)->GetAsString(&path);
330    if (path.empty())
331      continue;
332    ShillPropertyObserverMap::iterator iter2 = observer_map.find(path);
333    if (iter2 != observer_map.end()) {
334      new_observed[path] = iter2->second;
335    } else {
336      // Request an update.
337      RequestProperties(type, path);
338      // Create an observer for future updates.
339      new_observed[path] = new ShillPropertyObserver(
340          type, path, base::Bind(
341              &ShillPropertyHandler::PropertyChangedCallback, AsWeakPtr()));
342      NET_LOG_DEBUG("StartObserving", path);
343    }
344    observer_map.erase(path);
345    // Limit the number of observed services.
346    if (new_observed.size() >= kMaxObserved)
347      break;
348  }
349  // Delete network service observers still in observer_map.
350  for (ShillPropertyObserverMap::iterator iter =  observer_map.begin();
351       iter != observer_map.end(); ++iter) {
352    NET_LOG_DEBUG("StopObserving", iter->first);
353    delete iter->second;
354  }
355  observer_map.swap(new_observed);
356}
357
358void ShillPropertyHandler::UpdateAvailableTechnologies(
359    const base::ListValue& technologies) {
360  available_technologies_.clear();
361  NET_LOG_EVENT("AvailableTechnologiesChanged",
362                base::StringPrintf("Size: %"PRIuS, technologies.GetSize()));
363  for (base::ListValue::const_iterator iter = technologies.begin();
364       iter != technologies.end(); ++iter) {
365    std::string technology;
366    (*iter)->GetAsString(&technology);
367    DCHECK(!technology.empty());
368    available_technologies_.insert(technology);
369  }
370}
371
372void ShillPropertyHandler::UpdateEnabledTechnologies(
373    const base::ListValue& technologies) {
374  enabled_technologies_.clear();
375  NET_LOG_EVENT("EnabledTechnologiesChanged",
376                base::StringPrintf("Size: %"PRIuS, technologies.GetSize()));
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    enabled_technologies_.insert(technology);
383    enabling_technologies_.erase(technology);
384  }
385}
386
387void ShillPropertyHandler::UpdateUninitializedTechnologies(
388    const base::ListValue& technologies) {
389  uninitialized_technologies_.clear();
390  NET_LOG_EVENT("UninitializedTechnologiesChanged",
391                base::StringPrintf("Size: %"PRIuS, technologies.GetSize()));
392  for (base::ListValue::const_iterator iter = technologies.begin();
393       iter != technologies.end(); ++iter) {
394    std::string technology;
395    (*iter)->GetAsString(&technology);
396    DCHECK(!technology.empty());
397    uninitialized_technologies_.insert(technology);
398  }
399}
400
401void ShillPropertyHandler::EnableTechnologyFailed(
402    const std::string& technology,
403    const network_handler::ErrorCallback& error_callback,
404    const std::string& error_name,
405    const std::string& error_message) {
406  enabling_technologies_.erase(technology);
407  network_handler::ShillErrorCallbackFunction(
408      technology, error_callback, error_name, error_message);
409}
410
411void ShillPropertyHandler::GetPropertiesCallback(
412    ManagedState::ManagedType type,
413    const std::string& path,
414    DBusMethodCallStatus call_status,
415    const base::DictionaryValue& properties) {
416  VLOG(2) << "GetPropertiesCallback: " << type << " : " << path;
417  pending_updates_[type].erase(path);
418  if (call_status != DBUS_METHOD_CALL_SUCCESS) {
419    LOG(ERROR) << "Failed to get properties for: " << path
420               << ": " << call_status;
421    return;
422  }
423  listener_->UpdateManagedStateProperties(type, path, properties);
424
425  if (properties.HasKey(shill::kIPConfigProperty)) {
426  // Since this is the first time we received properties for this network,
427  // also request its IPConfig parameters.
428    std::string ip_config_path;
429    if (properties.GetString(shill::kIPConfigProperty, &ip_config_path)) {
430      DBusThreadManager::Get()->GetShillIPConfigClient()->GetProperties(
431          dbus::ObjectPath(ip_config_path),
432          base::Bind(&ShillPropertyHandler::GetIPConfigCallback,
433                     AsWeakPtr(), path));
434    }
435  }
436
437  // Notify the listener only when all updates for that type have completed.
438  if (pending_updates_[type].size() == 0)
439    listener_->ManagedStateListChanged(type);
440}
441
442void ShillPropertyHandler::PropertyChangedCallback(
443    ManagedState::ManagedType type,
444    const std::string& path,
445    const std::string& key,
446    const base::Value& value) {
447  if (type == ManagedState::MANAGED_TYPE_NETWORK)
448    NetworkServicePropertyChangedCallback(path, key, value);
449  else if (type == ManagedState::MANAGED_TYPE_DEVICE)
450    NetworkDevicePropertyChangedCallback(path, key, value);
451  else
452    NOTREACHED();
453}
454
455void ShillPropertyHandler::NetworkServicePropertyChangedCallback(
456    const std::string& path,
457    const std::string& key,
458    const base::Value& value) {
459  if (key == shill::kIPConfigProperty) {
460    // Request the IPConfig for the network and update network properties
461    // when the request completes.
462    std::string ip_config_path;
463    value.GetAsString(&ip_config_path);
464    DCHECK(!ip_config_path.empty());
465    DBusThreadManager::Get()->GetShillIPConfigClient()->GetProperties(
466        dbus::ObjectPath(ip_config_path),
467        base::Bind(&ShillPropertyHandler::GetIPConfigCallback,
468                   AsWeakPtr(), path));
469  } else {
470    listener_->UpdateNetworkServiceProperty(path, key, value);
471  }
472}
473
474void ShillPropertyHandler::GetIPConfigCallback(
475    const std::string& service_path,
476    DBusMethodCallStatus call_status,
477    const base::DictionaryValue& properties)  {
478  if (call_status != DBUS_METHOD_CALL_SUCCESS) {
479    LOG(ERROR) << "Failed to get IP Config properties for: " << service_path;
480    return;
481  }
482  const base::Value* ip_address;
483  if (!properties.GetWithoutPathExpansion(flimflam::kAddressProperty,
484                                          &ip_address)) {
485    LOG(ERROR) << "Failed to get IP Address property for: " << service_path;
486    return;
487  }
488  listener_->UpdateNetworkServiceProperty(
489      service_path,
490      NetworkState::IPConfigProperty(flimflam::kAddressProperty),
491      *ip_address);
492
493  const base::Value* dns_servers = NULL;
494  if (!properties.GetWithoutPathExpansion(
495          flimflam::kNameServersProperty, &dns_servers)) {
496    LOG(ERROR) << "Failed to get Name servers property for: " << service_path;
497    return;
498  }
499  listener_->UpdateNetworkServiceProperty(
500      service_path,
501      NetworkState::IPConfigProperty(flimflam::kNameServersProperty),
502      *dns_servers);
503}
504
505void ShillPropertyHandler::NetworkDevicePropertyChangedCallback(
506    const std::string& path,
507    const std::string& key,
508    const base::Value& value) {
509  listener_->UpdateDeviceProperty(path, key, value);
510}
511
512}  // namespace internal
513}  // namespace chromeos
514