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