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