• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
26 namespace {
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.
30 const size_t kMaxObserved = 100;
31 
GetListValue(const std::string & key,const base::Value & value)32 const 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 
44 namespace chromeos {
45 namespace 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.
50 class 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 
ShillPropertyObserver(ManagedState::ManagedType type,const std::string & path,const Handler & handler)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 
~ShillPropertyObserver()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.
OnPropertyChanged(const std::string & key,const base::Value & value)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 
ShillPropertyHandler(Listener * listener)105 ShillPropertyHandler::ShillPropertyHandler(Listener* listener)
106     : listener_(listener),
107       shill_manager_(DBusThreadManager::Get()->GetShillManagerClient()) {
108 }
109 
~ShillPropertyHandler()110 ShillPropertyHandler::~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 
Init()120 void ShillPropertyHandler::Init() {
121   UpdateManagerProperties();
122   shill_manager_->AddPropertyChangedObserver(this);
123 }
124 
UpdateManagerProperties()125 void ShillPropertyHandler::UpdateManagerProperties() {
126   NET_LOG_EVENT("UpdateManagerProperties", "");
127   shill_manager_->GetProperties(
128       base::Bind(&ShillPropertyHandler::ManagerPropertiesCallback,
129                  AsWeakPtr()));
130 }
131 
IsTechnologyAvailable(const std::string & technology) const132 bool ShillPropertyHandler::IsTechnologyAvailable(
133     const std::string& technology) const {
134   return available_technologies_.count(technology) != 0;
135 }
136 
IsTechnologyEnabled(const std::string & technology) const137 bool ShillPropertyHandler::IsTechnologyEnabled(
138     const std::string& technology) const {
139   return enabled_technologies_.count(technology) != 0;
140 }
141 
IsTechnologyEnabling(const std::string & technology) const142 bool ShillPropertyHandler::IsTechnologyEnabling(
143     const std::string& technology) const {
144   return enabling_technologies_.count(technology) != 0;
145 }
146 
IsTechnologyUninitialized(const std::string & technology) const147 bool ShillPropertyHandler::IsTechnologyUninitialized(
148     const std::string& technology) const {
149   return uninitialized_technologies_.count(technology) != 0;
150 }
151 
SetTechnologyEnabled(const std::string & technology,bool enabled,const network_handler::ErrorCallback & error_callback)152 void 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 
SetCheckPortalList(const std::string & check_portal_list)176 void 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 
RequestScan() const188 void 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 
ConnectToBestServices() const197 void 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 
RequestProperties(ManagedState::ManagedType type,const std::string & path)206 void 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 
OnPropertyChanged(const std::string & key,const base::Value & value)229 void 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 
ManagerPropertiesCallback(DBusMethodCallStatus call_status,const base::DictionaryValue & properties)238 void 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   for (base::DictionaryValue::Iterator iter(properties);
248        !iter.IsAtEnd(); iter.Advance()) {
249     ManagerPropertyChanged(iter.key(), iter.value());
250   }
251 
252   CheckPendingStateListUpdates("");
253 }
254 
CheckPendingStateListUpdates(const std::string & key)255 void ShillPropertyHandler::CheckPendingStateListUpdates(
256     const std::string& key) {
257   // Once there are no pending updates, signal the state list changed callbacks.
258   if ((key.empty() || key == shill::kServiceCompleteListProperty) &&
259       pending_updates_[ManagedState::MANAGED_TYPE_NETWORK].size() == 0) {
260     listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_NETWORK);
261   }
262   if ((key.empty() || key == shill::kDevicesProperty) &&
263       pending_updates_[ManagedState::MANAGED_TYPE_DEVICE].size() == 0) {
264     listener_->ManagedStateListChanged(ManagedState::MANAGED_TYPE_DEVICE);
265   }
266 }
267 
ManagerPropertyChanged(const std::string & key,const base::Value & value)268 void ShillPropertyHandler::ManagerPropertyChanged(const std::string& key,
269                                                   const base::Value& value) {
270   NET_LOG_DEBUG("ManagerPropertyChanged", key);
271   if (key == shill::kDefaultServiceProperty) {
272     std::string service_path;
273     value.GetAsString(&service_path);
274     listener_->DefaultNetworkServiceChanged(service_path);
275   } else if (key == shill::kServiceCompleteListProperty) {
276     const base::ListValue* vlist = GetListValue(key, value);
277     if (vlist) {
278       listener_->UpdateManagedList(ManagedState::MANAGED_TYPE_NETWORK, *vlist);
279       UpdateProperties(ManagedState::MANAGED_TYPE_NETWORK, *vlist);
280       UpdateObserved(ManagedState::MANAGED_TYPE_NETWORK, *vlist);
281     }
282   } else if (key == shill::kDevicesProperty) {
283     const base::ListValue* vlist = GetListValue(key, value);
284     if (vlist) {
285       listener_->UpdateManagedList(ManagedState::MANAGED_TYPE_DEVICE, *vlist);
286       UpdateProperties(ManagedState::MANAGED_TYPE_DEVICE, *vlist);
287       UpdateObserved(ManagedState::MANAGED_TYPE_DEVICE, *vlist);
288     }
289   } else if (key == shill::kAvailableTechnologiesProperty) {
290     const base::ListValue* vlist = GetListValue(key, value);
291     if (vlist)
292       UpdateAvailableTechnologies(*vlist);
293   } else if (key == shill::kEnabledTechnologiesProperty) {
294     const base::ListValue* vlist = GetListValue(key, value);
295     if (vlist)
296       UpdateEnabledTechnologies(*vlist);
297   } else if (key == shill::kUninitializedTechnologiesProperty) {
298     const base::ListValue* vlist = GetListValue(key, value);
299     if (vlist)
300       UpdateUninitializedTechnologies(*vlist);
301   } else if (key == shill::kProfilesProperty) {
302     listener_->ProfileListChanged();
303   } else if (key == shill::kCheckPortalListProperty) {
304     std::string check_portal_list;
305     if (value.GetAsString(&check_portal_list))
306       listener_->CheckPortalListChanged(check_portal_list);
307   } else {
308     VLOG(2) << "Ignored Manager Property: " << key;
309   }
310 }
311 
UpdateProperties(ManagedState::ManagedType type,const base::ListValue & entries)312 void ShillPropertyHandler::UpdateProperties(ManagedState::ManagedType type,
313                                             const base::ListValue& entries) {
314   std::set<std::string>& requested_updates = requested_updates_[type];
315   std::set<std::string> new_requested_updates;
316   NET_LOG_DEBUG("UpdateProperties: " + ManagedState::TypeToString(type),
317                 base::StringPrintf("%" PRIuS, entries.GetSize()));
318   for (base::ListValue::const_iterator iter = entries.begin();
319        iter != entries.end(); ++iter) {
320     std::string path;
321     (*iter)->GetAsString(&path);
322     if (path.empty())
323       continue;
324 
325     // We add a special case for devices here to work around an issue in shill
326     // that prevents it from sending property changed signals for cellular
327     // devices (see crbug.com/321854).
328     if (type == ManagedState::MANAGED_TYPE_DEVICE ||
329         requested_updates.find(path) == requested_updates.end()) {
330       RequestProperties(type, path);
331     }
332     new_requested_updates.insert(path);
333   }
334   requested_updates.swap(new_requested_updates);
335 }
336 
UpdateObserved(ManagedState::ManagedType type,const base::ListValue & entries)337 void ShillPropertyHandler::UpdateObserved(ManagedState::ManagedType type,
338                                           const base::ListValue& entries) {
339   ShillPropertyObserverMap& observer_map =
340       (type == ManagedState::MANAGED_TYPE_NETWORK)
341       ? observed_networks_ : observed_devices_;
342   ShillPropertyObserverMap new_observed;
343   for (base::ListValue::const_iterator iter1 = entries.begin();
344        iter1 != entries.end(); ++iter1) {
345     std::string path;
346     (*iter1)->GetAsString(&path);
347     if (path.empty())
348       continue;
349     ShillPropertyObserverMap::iterator iter2 = observer_map.find(path);
350     if (iter2 != observer_map.end()) {
351       new_observed[path] = iter2->second;
352     } else {
353       // Create an observer for future updates.
354       new_observed[path] = new ShillPropertyObserver(
355           type, path, base::Bind(
356               &ShillPropertyHandler::PropertyChangedCallback, AsWeakPtr()));
357     }
358     observer_map.erase(path);
359     // Limit the number of observed services.
360     if (new_observed.size() >= kMaxObserved)
361       break;
362   }
363   // Delete network service observers still in observer_map.
364   for (ShillPropertyObserverMap::iterator iter =  observer_map.begin();
365        iter != observer_map.end(); ++iter) {
366     delete iter->second;
367   }
368   observer_map.swap(new_observed);
369 }
370 
UpdateAvailableTechnologies(const base::ListValue & technologies)371 void ShillPropertyHandler::UpdateAvailableTechnologies(
372     const base::ListValue& technologies) {
373   std::stringstream technologies_str;
374   technologies_str << technologies;
375   NET_LOG_EVENT("AvailableTechnologies:", technologies_str.str());
376   available_technologies_.clear();
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     available_technologies_.insert(technology);
383   }
384   listener_->TechnologyListChanged();
385 }
386 
UpdateEnabledTechnologies(const base::ListValue & technologies)387 void ShillPropertyHandler::UpdateEnabledTechnologies(
388     const base::ListValue& technologies) {
389   std::stringstream technologies_str;
390   technologies_str << technologies;
391   NET_LOG_EVENT("EnabledTechnologies:", technologies_str.str());
392   enabled_technologies_.clear();
393   for (base::ListValue::const_iterator iter = technologies.begin();
394        iter != technologies.end(); ++iter) {
395     std::string technology;
396     (*iter)->GetAsString(&technology);
397     DCHECK(!technology.empty());
398     enabled_technologies_.insert(technology);
399     enabling_technologies_.erase(technology);
400   }
401   listener_->TechnologyListChanged();
402 }
403 
UpdateUninitializedTechnologies(const base::ListValue & technologies)404 void ShillPropertyHandler::UpdateUninitializedTechnologies(
405     const base::ListValue& technologies) {
406   std::stringstream technologies_str;
407   technologies_str << technologies;
408   NET_LOG_EVENT("UninitializedTechnologies:", technologies_str.str());
409   uninitialized_technologies_.clear();
410   for (base::ListValue::const_iterator iter = technologies.begin();
411        iter != technologies.end(); ++iter) {
412     std::string technology;
413     (*iter)->GetAsString(&technology);
414     DCHECK(!technology.empty());
415     uninitialized_technologies_.insert(technology);
416   }
417   listener_->TechnologyListChanged();
418 }
419 
EnableTechnologyFailed(const std::string & technology,const network_handler::ErrorCallback & error_callback,const std::string & dbus_error_name,const std::string & dbus_error_message)420 void ShillPropertyHandler::EnableTechnologyFailed(
421     const std::string& technology,
422     const network_handler::ErrorCallback& error_callback,
423     const std::string& dbus_error_name,
424     const std::string& dbus_error_message) {
425   enabling_technologies_.erase(technology);
426   network_handler::ShillErrorCallbackFunction(
427       "EnableTechnology Failed",
428       technology, error_callback,
429       dbus_error_name, dbus_error_message);
430 }
431 
GetPropertiesCallback(ManagedState::ManagedType type,const std::string & path,DBusMethodCallStatus call_status,const base::DictionaryValue & properties)432 void ShillPropertyHandler::GetPropertiesCallback(
433     ManagedState::ManagedType type,
434     const std::string& path,
435     DBusMethodCallStatus call_status,
436     const base::DictionaryValue& properties) {
437   NET_LOG_DEBUG("GetPropertiesCallback: " + ManagedState::TypeToString(type),
438                 path);
439   pending_updates_[type].erase(path);
440   if (call_status != DBUS_METHOD_CALL_SUCCESS) {
441     // The shill service no longer exists.  This can happen when a network
442     // has been removed.
443     NET_LOG_DEBUG("Failed to get properties",
444                   base::StringPrintf("%s: %d", path.c_str(), call_status));
445     return;
446   }
447   listener_->UpdateManagedStateProperties(type, path, properties);
448 
449   if (type == ManagedState::MANAGED_TYPE_NETWORK) {
450     // Request IPConfig properties.
451     const base::Value* value;
452     if (properties.GetWithoutPathExpansion(shill::kIPConfigProperty, &value))
453       RequestIPConfig(type, path, *value);
454   } else if (type == ManagedState::MANAGED_TYPE_DEVICE) {
455     // Clear and request IPConfig properties for each entry in IPConfigs.
456     const base::Value* value;
457     if (properties.GetWithoutPathExpansion(shill::kIPConfigsProperty, &value))
458       RequestIPConfigsList(type, path, *value);
459   }
460 
461   // Notify the listener only when all updates for that type have completed.
462   if (pending_updates_[type].size() == 0)
463     listener_->ManagedStateListChanged(type);
464 }
465 
PropertyChangedCallback(ManagedState::ManagedType type,const std::string & path,const std::string & key,const base::Value & value)466 void ShillPropertyHandler::PropertyChangedCallback(
467     ManagedState::ManagedType type,
468     const std::string& path,
469     const std::string& key,
470     const base::Value& value) {
471   if (type == ManagedState::MANAGED_TYPE_NETWORK &&
472       key == shill::kIPConfigProperty) {
473     RequestIPConfig(type, path, value);
474   } else if (type == ManagedState::MANAGED_TYPE_DEVICE &&
475       key == shill::kIPConfigsProperty) {
476     RequestIPConfigsList(type, path, value);
477   }
478 
479   if (type == ManagedState::MANAGED_TYPE_NETWORK)
480     listener_->UpdateNetworkServiceProperty(path, key, value);
481   else if (type == ManagedState::MANAGED_TYPE_DEVICE)
482     listener_->UpdateDeviceProperty(path, key, value);
483   else
484     NOTREACHED();
485 }
486 
RequestIPConfig(ManagedState::ManagedType type,const std::string & path,const base::Value & ip_config_path_value)487 void ShillPropertyHandler::RequestIPConfig(
488     ManagedState::ManagedType type,
489     const std::string& path,
490     const base::Value& ip_config_path_value) {
491   std::string ip_config_path;
492   if (!ip_config_path_value.GetAsString(&ip_config_path) ||
493       ip_config_path.empty()) {
494     NET_LOG_ERROR("Invalid IPConfig", path);
495     return;
496   }
497   DBusThreadManager::Get()->GetShillIPConfigClient()->GetProperties(
498       dbus::ObjectPath(ip_config_path),
499       base::Bind(&ShillPropertyHandler::GetIPConfigCallback,
500                  AsWeakPtr(), type, path, ip_config_path));
501 }
502 
RequestIPConfigsList(ManagedState::ManagedType type,const std::string & path,const base::Value & ip_config_list_value)503 void ShillPropertyHandler::RequestIPConfigsList(
504     ManagedState::ManagedType type,
505     const std::string& path,
506     const base::Value& ip_config_list_value) {
507   const base::ListValue* ip_configs;
508   if (!ip_config_list_value.GetAsList(&ip_configs))
509     return;
510   for (base::ListValue::const_iterator iter = ip_configs->begin();
511        iter != ip_configs->end(); ++iter) {
512     RequestIPConfig(type, path, **iter);
513   }
514 }
515 
GetIPConfigCallback(ManagedState::ManagedType type,const std::string & path,const std::string & ip_config_path,DBusMethodCallStatus call_status,const base::DictionaryValue & properties)516 void ShillPropertyHandler::GetIPConfigCallback(
517     ManagedState::ManagedType type,
518     const std::string& path,
519     const std::string& ip_config_path,
520     DBusMethodCallStatus call_status,
521     const base::DictionaryValue& properties)  {
522   if (call_status != DBUS_METHOD_CALL_SUCCESS) {
523     // IP Config properties not availabe. Shill will emit a property change
524     // when they are.
525     NET_LOG_EVENT(
526         base::StringPrintf("Failed to get IP Config properties: %s: %d",
527                            ip_config_path.c_str(), call_status), path);
528     return;
529   }
530   NET_LOG_EVENT("IP Config properties received", path);
531   listener_->UpdateIPConfigProperties(type, path, ip_config_path, properties);
532 }
533 
534 }  // namespace internal
535 }  // namespace chromeos
536