shill_property_handler.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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/string_util.h"
11#include "base/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::RequestScan() const {
166  shill_manager_->RequestScan(
167      "",
168      base::Bind(&base::DoNothing),
169      base::Bind(&network_handler::ShillErrorCallbackFunction,
170                 "", network_handler::ErrorCallback()));
171}
172
173void ShillPropertyHandler::ConnectToBestServices() const {
174  NET_LOG_EVENT("ConnectToBestServices", "");
175  shill_manager_->ConnectToBestServices(
176      base::Bind(&base::DoNothing),
177      base::Bind(&network_handler::ShillErrorCallbackFunction,
178                 "", network_handler::ErrorCallback()));
179}
180
181void ShillPropertyHandler::RequestProperties(ManagedState::ManagedType type,
182                                             const std::string& path) {
183  if (pending_updates_[type].find(path) != pending_updates_[type].end())
184    return;  // Update already requested.
185
186  pending_updates_[type].insert(path);
187  if (type == ManagedState::MANAGED_TYPE_NETWORK) {
188    DBusThreadManager::Get()->GetShillServiceClient()->GetProperties(
189        dbus::ObjectPath(path),
190        base::Bind(&ShillPropertyHandler::GetPropertiesCallback,
191                   AsWeakPtr(), type, path));
192  } else if (type == ManagedState::MANAGED_TYPE_DEVICE) {
193    DBusThreadManager::Get()->GetShillDeviceClient()->GetProperties(
194        dbus::ObjectPath(path),
195        base::Bind(&ShillPropertyHandler::GetPropertiesCallback,
196                   AsWeakPtr(), type, path));
197  } else {
198    NOTREACHED();
199  }
200}
201
202void ShillPropertyHandler::OnPropertyChanged(const std::string& key,
203                                             const base::Value& value) {
204  if (ManagerPropertyChanged(key, value)) {
205    std::string detail = key;
206    detail += " = " + network_event_log::ValueAsString(value);
207    NET_LOG_DEBUG("ManagerPropertyChanged", detail);
208    listener_->NotifyManagerPropertyChanged();
209  }
210  // If the service or device list changed and there are no pending
211  // updates, signal the state list changed callback.
212  if ((key == flimflam::kServicesProperty) &&
213      pending_updates_[ManagedState::MANAGED_TYPE_NETWORK].size() == 0) {
214    listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_NETWORK);
215  }
216  if (key == flimflam::kDevicesProperty &&
217      pending_updates_[ManagedState::MANAGED_TYPE_DEVICE].size() == 0) {
218    listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_DEVICE);
219  }
220}
221
222//------------------------------------------------------------------------------
223// Private methods
224
225void ShillPropertyHandler::ManagerPropertiesCallback(
226    DBusMethodCallStatus call_status,
227    const base::DictionaryValue& properties) {
228  if (call_status != DBUS_METHOD_CALL_SUCCESS) {
229    NET_LOG_ERROR("ManagerPropertiesCallback",
230                  base::StringPrintf("Failed: %d", call_status));
231    return;
232  }
233  NET_LOG_EVENT("ManagerPropertiesCallback", "Success");
234  bool notify = false;
235  bool update_service_list = false;
236  for (base::DictionaryValue::Iterator iter(properties);
237       !iter.IsAtEnd(); iter.Advance()) {
238    // Defer updating Services until all other properties have been updated.
239    if (iter.key() == flimflam::kServicesProperty)
240      update_service_list = true;
241    else
242      notify |= ManagerPropertyChanged(iter.key(), iter.value());
243  }
244  // Now update the service list which can safely assume other properties have
245  // been initially set.
246  if (update_service_list) {
247    const base::Value* value = NULL;
248    if (properties.GetWithoutPathExpansion(flimflam::kServicesProperty, &value))
249      notify |= ManagerPropertyChanged(flimflam::kServicesProperty, *value);
250  }
251  if (notify)
252    listener_->NotifyManagerPropertyChanged();
253  // If there are no pending updates, signal the state list changed callbacks.
254  if (pending_updates_[ManagedState::MANAGED_TYPE_NETWORK].size() == 0)
255    listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_NETWORK);
256  if (pending_updates_[ManagedState::MANAGED_TYPE_DEVICE].size() == 0)
257    listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_DEVICE);
258}
259
260bool ShillPropertyHandler::ManagerPropertyChanged(const std::string& key,
261                                                  const base::Value& value) {
262  bool notify_manager_changed = false;
263  if (key == flimflam::kServicesProperty) {
264    const base::ListValue* vlist = GetListValue(key, value);
265    if (vlist) {
266      listener_->UpdateManagedList(ManagedState::MANAGED_TYPE_NETWORK, *vlist);
267      // UpdateObserved used to use kServiceWatchListProperty for TYPE_NETWORK,
268      // however that prevents us from receiving Strength updates from inactive
269      // networks. The overhead for observing all services is not unreasonable
270      // (and we limit the max number of observed services to kMaxObserved).
271      UpdateObserved(ManagedState::MANAGED_TYPE_NETWORK, *vlist);
272    }
273  } else if (key == flimflam::kDevicesProperty) {
274    const ListValue* vlist = GetListValue(key, value);
275    if (vlist) {
276      listener_->UpdateManagedList(ManagedState::MANAGED_TYPE_DEVICE, *vlist);
277      UpdateObserved(ManagedState::MANAGED_TYPE_DEVICE, *vlist);
278    }
279  } else if (key == flimflam::kAvailableTechnologiesProperty) {
280    const base::ListValue* vlist = GetListValue(key, value);
281    if (vlist) {
282      UpdateAvailableTechnologies(*vlist);
283      notify_manager_changed = true;
284    }
285  } else if (key == flimflam::kEnabledTechnologiesProperty) {
286    const base::ListValue* vlist = GetListValue(key, value);
287    if (vlist) {
288      UpdateEnabledTechnologies(*vlist);
289      notify_manager_changed = true;
290    }
291  } else if (key == shill::kUninitializedTechnologiesProperty) {
292    const base::ListValue* vlist = GetListValue(key, value);
293    if (vlist) {
294      UpdateUninitializedTechnologies(*vlist);
295      notify_manager_changed = true;
296    }
297  } else if (key == flimflam::kProfilesProperty) {
298    listener_->ProfileListChanged();
299  }
300  return notify_manager_changed;
301}
302
303void ShillPropertyHandler::UpdateObserved(ManagedState::ManagedType type,
304                                          const base::ListValue& entries) {
305  ShillPropertyObserverMap& observer_map =
306      (type == ManagedState::MANAGED_TYPE_NETWORK)
307      ? observed_networks_ : observed_devices_;
308  ShillPropertyObserverMap new_observed;
309  for (base::ListValue::const_iterator iter1 = entries.begin();
310       iter1 != entries.end(); ++iter1) {
311    std::string path;
312    (*iter1)->GetAsString(&path);
313    if (path.empty())
314      continue;
315    ShillPropertyObserverMap::iterator iter2 = observer_map.find(path);
316    if (iter2 != observer_map.end()) {
317      new_observed[path] = iter2->second;
318    } else {
319      // Request an update.
320      RequestProperties(type, path);
321      // Create an observer for future updates.
322      new_observed[path] = new ShillPropertyObserver(
323          type, path, base::Bind(
324              &ShillPropertyHandler::PropertyChangedCallback, AsWeakPtr()));
325      NET_LOG_DEBUG("StartObserving", path);
326    }
327    observer_map.erase(path);
328    // Limit the number of observed services.
329    if (new_observed.size() >= kMaxObserved)
330      break;
331  }
332  // Delete network service observers still in observer_map.
333  for (ShillPropertyObserverMap::iterator iter =  observer_map.begin();
334       iter != observer_map.end(); ++iter) {
335    NET_LOG_DEBUG("StopObserving", iter->first);
336    delete iter->second;
337  }
338  observer_map.swap(new_observed);
339}
340
341void ShillPropertyHandler::UpdateAvailableTechnologies(
342    const base::ListValue& technologies) {
343  available_technologies_.clear();
344  NET_LOG_EVENT("AvailableTechnologiesChanged",
345                base::StringPrintf("Size: %"PRIuS, technologies.GetSize()));
346  for (base::ListValue::const_iterator iter = technologies.begin();
347       iter != technologies.end(); ++iter) {
348    std::string technology;
349    (*iter)->GetAsString(&technology);
350    DCHECK(!technology.empty());
351    available_technologies_.insert(technology);
352  }
353}
354
355void ShillPropertyHandler::UpdateEnabledTechnologies(
356    const base::ListValue& technologies) {
357  enabled_technologies_.clear();
358  NET_LOG_EVENT("EnabledTechnologiesChanged",
359                base::StringPrintf("Size: %"PRIuS, technologies.GetSize()));
360  for (base::ListValue::const_iterator iter = technologies.begin();
361       iter != technologies.end(); ++iter) {
362    std::string technology;
363    (*iter)->GetAsString(&technology);
364    DCHECK(!technology.empty());
365    enabled_technologies_.insert(technology);
366    enabling_technologies_.erase(technology);
367  }
368}
369
370void ShillPropertyHandler::UpdateUninitializedTechnologies(
371    const base::ListValue& technologies) {
372  uninitialized_technologies_.clear();
373  NET_LOG_EVENT("UninitializedTechnologiesChanged",
374                base::StringPrintf("Size: %"PRIuS, technologies.GetSize()));
375  for (base::ListValue::const_iterator iter = technologies.begin();
376       iter != technologies.end(); ++iter) {
377    std::string technology;
378    (*iter)->GetAsString(&technology);
379    DCHECK(!technology.empty());
380    uninitialized_technologies_.insert(technology);
381  }
382}
383
384void ShillPropertyHandler::EnableTechnologyFailed(
385    const std::string& technology,
386    const network_handler::ErrorCallback& error_callback,
387    const std::string& error_name,
388    const std::string& error_message) {
389  enabling_technologies_.erase(technology);
390  network_handler::ShillErrorCallbackFunction(
391      technology, error_callback, error_name, error_message);
392}
393
394void ShillPropertyHandler::GetPropertiesCallback(
395    ManagedState::ManagedType type,
396    const std::string& path,
397    DBusMethodCallStatus call_status,
398    const base::DictionaryValue& properties) {
399  VLOG(2) << "GetPropertiesCallback: " << type << " : " << path;
400  pending_updates_[type].erase(path);
401  if (call_status != DBUS_METHOD_CALL_SUCCESS) {
402    LOG(ERROR) << "Failed to get properties for: " << path
403               << ": " << call_status;
404    return;
405  }
406  listener_->UpdateManagedStateProperties(type, path, properties);
407
408  if (properties.HasKey(shill::kIPConfigProperty)) {
409  // Since this is the first time we received properties for this network,
410  // also request its IPConfig parameters.
411    std::string ip_config_path;
412    if (properties.GetString(shill::kIPConfigProperty, &ip_config_path)) {
413      DBusThreadManager::Get()->GetShillIPConfigClient()->GetProperties(
414          dbus::ObjectPath(ip_config_path),
415          base::Bind(&ShillPropertyHandler::GetIPConfigCallback,
416                     AsWeakPtr(), path));
417    }
418  }
419
420  // Notify the listener only when all updates for that type have completed.
421  if (pending_updates_[type].size() == 0)
422    listener_->ManagedStateListChanged(type);
423}
424
425void ShillPropertyHandler::PropertyChangedCallback(
426    ManagedState::ManagedType type,
427    const std::string& path,
428    const std::string& key,
429    const base::Value& value) {
430  if (type == ManagedState::MANAGED_TYPE_NETWORK)
431    NetworkServicePropertyChangedCallback(path, key, value);
432  else if (type == ManagedState::MANAGED_TYPE_DEVICE)
433    NetworkDevicePropertyChangedCallback(path, key, value);
434  else
435    NOTREACHED();
436}
437
438void ShillPropertyHandler::NetworkServicePropertyChangedCallback(
439    const std::string& path,
440    const std::string& key,
441    const base::Value& value) {
442  if (key == shill::kIPConfigProperty) {
443    // Request the IPConfig for the network and update network properties
444    // when the request completes.
445    std::string ip_config_path;
446    value.GetAsString(&ip_config_path);
447    DCHECK(!ip_config_path.empty());
448    DBusThreadManager::Get()->GetShillIPConfigClient()->GetProperties(
449        dbus::ObjectPath(ip_config_path),
450        base::Bind(&ShillPropertyHandler::GetIPConfigCallback,
451                   AsWeakPtr(), path));
452  } else {
453    listener_->UpdateNetworkServiceProperty(path, key, value);
454  }
455}
456
457void ShillPropertyHandler::GetIPConfigCallback(
458    const std::string& service_path,
459    DBusMethodCallStatus call_status,
460    const base::DictionaryValue& properties)  {
461  if (call_status != DBUS_METHOD_CALL_SUCCESS) {
462    LOG(ERROR) << "Failed to get IP Config properties for: " << service_path;
463    return;
464  }
465  const base::Value* ip_address;
466  if (!properties.GetWithoutPathExpansion(flimflam::kAddressProperty,
467                                          &ip_address)) {
468    LOG(ERROR) << "Failed to get IP Address property for: " << service_path;
469    return;
470  }
471  listener_->UpdateNetworkServiceProperty(
472      service_path,
473      NetworkState::IPConfigProperty(flimflam::kAddressProperty),
474      *ip_address);
475
476  const base::Value* dns_servers = NULL;
477  if (!properties.GetWithoutPathExpansion(
478          flimflam::kNameServersProperty, &dns_servers)) {
479    LOG(ERROR) << "Failed to get Name servers property for: " << service_path;
480    return;
481  }
482  listener_->UpdateNetworkServiceProperty(
483      service_path,
484      NetworkState::IPConfigProperty(flimflam::kNameServersProperty),
485      *dns_servers);
486}
487
488void ShillPropertyHandler::NetworkDevicePropertyChangedCallback(
489    const std::string& path,
490    const std::string& key,
491    const base::Value& value) {
492  listener_->UpdateDeviceProperty(path, key, value);
493}
494
495}  // namespace internal
496}  // namespace chromeos
497