summaryrefslogtreecommitdiffstats
path: root/chromeos/network
diff options
context:
space:
mode:
authorstevenjb@chromium.org <stevenjb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-07 10:14:59 +0000
committerstevenjb@chromium.org <stevenjb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-07 10:14:59 +0000
commit3c8bd113b0da75a274072e543c314a8c19288429 (patch)
tree70993526ffd965ea57989435fafe33fbc50788f3 /chromeos/network
parent51ed599857bf45c7ea54761dfce58691fe9c657e (diff)
downloadchromium_src-3c8bd113b0da75a274072e543c314a8c19288429.zip
chromium_src-3c8bd113b0da75a274072e543c314a8c19288429.tar.gz
chromium_src-3c8bd113b0da75a274072e543c314a8c19288429.tar.bz2
Add chromeos::NetworkStateManager to src/chromeos/network.
The intention for this class is to provide up-to-date state information about the network for the majority of the network related UI. It should also serve as a foundation for classes monitoring the state of the active network. This code is not designed to provide detailed information about specific network service or device properties, not should it provide the ability to set (configure) those properties. BUG=154072 Review URL: https://chromiumcodereview.appspot.com/11192024 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@166375 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chromeos/network')
-rw-r--r--chromeos/network/device_state.cc60
-rw-r--r--chromeos/network/device_state.h41
-rw-r--r--chromeos/network/managed_state.cc83
-rw-r--r--chromeos/network/managed_state.h91
-rw-r--r--chromeos/network/network_state.cc66
-rw-r--r--chromeos/network/network_state.h70
-rw-r--r--chromeos/network/network_state_handler.cc389
-rw-r--r--chromeos/network/network_state_handler.h190
-rw-r--r--chromeos/network/network_state_handler_observer.cc37
-rw-r--r--chromeos/network/network_state_handler_observer.h55
-rw-r--r--chromeos/network/network_state_handler_unittest.cc213
-rw-r--r--chromeos/network/shill_property_handler.cc272
-rw-r--r--chromeos/network/shill_property_handler.h175
-rw-r--r--chromeos/network/shill_property_handler_unittest.cc368
-rw-r--r--chromeos/network/shill_service_observer.cc33
-rw-r--r--chromeos/network/shill_service_observer.h44
16 files changed, 2187 insertions, 0 deletions
diff --git a/chromeos/network/device_state.cc b/chromeos/network/device_state.cc
new file mode 100644
index 0000000..3ba9c87
--- /dev/null
+++ b/chromeos/network/device_state.cc
@@ -0,0 +1,60 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/network/device_state.h"
+
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "base/values.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+namespace chromeos {
+
+DeviceState::DeviceState(const std::string& path)
+ : ManagedState(MANAGED_TYPE_DEVICE, path),
+ provider_requires_roaming_(false),
+ support_network_scan_(false) {
+}
+
+DeviceState::~DeviceState() {
+}
+
+bool DeviceState::PropertyChanged(const std::string& key,
+ const base::Value& value) {
+ if (ManagedStatePropertyChanged(key, value))
+ return true;
+ if (key == flimflam::kAddressProperty) {
+ return GetStringValue(key, value, &mac_address_);
+ } else if (key == flimflam::kSupportNetworkScanProperty) {
+ return GetBooleanValue(key, value, &support_network_scan_);
+ } else if (key == shill::kProviderRequiresRoamingProperty) {
+ return GetBooleanValue(key, value, &provider_requires_roaming_);
+ } else if (key == flimflam::kHomeProviderProperty) {
+ const DictionaryValue* dict = NULL;
+ if (!value.GetAsDictionary(&dict))
+ return false;
+ std::string home_provider_country;
+ std::string home_provider_name;
+ dict->GetStringWithoutPathExpansion(flimflam::kOperatorCountryKey,
+ &home_provider_country);
+ dict->GetStringWithoutPathExpansion(flimflam::kOperatorNameKey,
+ &home_provider_name);
+ // Set home_provider_id_
+ if (!home_provider_name.empty() && !home_provider_country.empty()) {
+ home_provider_id_ = base::StringPrintf(
+ "%s (%s)",
+ home_provider_name.c_str(),
+ home_provider_country.c_str());
+ } else {
+ dict->GetStringWithoutPathExpansion(flimflam::kOperatorCodeKey,
+ &home_provider_id_);
+ LOG(WARNING) << "Carrier ID not defined, using code instead: "
+ << home_provider_id_;
+ }
+ return true;
+ }
+ return false;
+}
+
+} // namespace chromeos
diff --git a/chromeos/network/device_state.h b/chromeos/network/device_state.h
new file mode 100644
index 0000000..3545b45
--- /dev/null
+++ b/chromeos/network/device_state.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_NETWORK_DEVICE_STATE_H_
+#define CHROMEOS_NETWORK_DEVICE_STATE_H_
+
+#include "chromeos/network/managed_state.h"
+
+namespace chromeos {
+
+// Simple class to provide device state information. Similar to NetworkState;
+// see network_state.h for usage guidelines.
+class CHROMEOS_EXPORT DeviceState : public ManagedState {
+ public:
+ explicit DeviceState(const std::string& path);
+ virtual ~DeviceState();
+
+ // ManagedState overrides
+ virtual bool PropertyChanged(const std::string& key,
+ const base::Value& value) OVERRIDE;
+
+ // Accessors
+ const std::string& mac_address() const { return mac_address_; }
+ bool provider_requires_roaming() const { return provider_requires_roaming_; }
+ bool support_network_scan() const { return support_network_scan_; }
+
+ private:
+ // Common Device Properties
+ std::string mac_address_;
+ // Cellular specific propeties
+ std::string home_provider_id_;
+ bool provider_requires_roaming_;
+ bool support_network_scan_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeviceState);
+};
+
+} // namespace chromeos
+
+#endif // CHROMEOS_NETWORK_DEVICE_STATE_H_
diff --git a/chromeos/network/managed_state.cc b/chromeos/network/managed_state.cc
new file mode 100644
index 0000000..556d200
--- /dev/null
+++ b/chromeos/network/managed_state.cc
@@ -0,0 +1,83 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/network/managed_state.h"
+
+#include "base/logging.h"
+#include "base/values.h"
+#include "chromeos/network/device_state.h"
+#include "chromeos/network/network_state.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+namespace chromeos {
+
+ManagedState::ManagedState(ManagedType type, const std::string& path)
+ : managed_type_(type),
+ path_(path),
+ is_observed_(false) {
+}
+
+ManagedState::~ManagedState() {
+}
+
+ManagedState* ManagedState::Create(ManagedType type, const std::string& path) {
+ switch(type) {
+ case MANAGED_TYPE_NETWORK:
+ return new NetworkState(path);
+ case MANAGED_TYPE_DEVICE:
+ return new DeviceState(path);
+ }
+ return NULL;
+}
+
+NetworkState* ManagedState::AsNetworkState() {
+ if (managed_type() == MANAGED_TYPE_NETWORK)
+ return static_cast<NetworkState*>(this);
+ return NULL;
+}
+
+DeviceState* ManagedState::AsDeviceState() {
+ if (managed_type() == MANAGED_TYPE_DEVICE)
+ return static_cast<DeviceState*>(this);
+ return NULL;
+}
+
+bool ManagedState::ManagedStatePropertyChanged(const std::string& key,
+ const base::Value& value) {
+ if (key == flimflam::kNameProperty) {
+ return GetStringValue(key, value, &name_);
+ } else if (key == flimflam::kTypeProperty) {
+ return GetStringValue(key, value, &type_);
+ }
+ return false;
+}
+
+bool ManagedState::GetBooleanValue(const std::string& key,
+ const base::Value& value,
+ bool* out_value) {
+ bool res = value.GetAsBoolean(out_value);
+ if (!res)
+ LOG(WARNING) << "Failed to parse boolean value for:" << key;
+ return res;
+}
+
+bool ManagedState::GetIntegerValue(const std::string& key,
+ const base::Value& value,
+ int* out_value) {
+ bool res = value.GetAsInteger(out_value);
+ if (!res)
+ LOG(WARNING) << "Failed to parse integer value for:" << key;
+ return res;
+}
+
+bool ManagedState::GetStringValue(const std::string& key,
+ const base::Value& value,
+ std::string* out_value) {
+ bool res = value.GetAsString(out_value);
+ if (!res)
+ LOG(WARNING) << "Failed to parse string value for:" << key;
+ return res;
+}
+
+} // namespace chromeos
diff --git a/chromeos/network/managed_state.h b/chromeos/network/managed_state.h
new file mode 100644
index 0000000..0138df64
--- /dev/null
+++ b/chromeos/network/managed_state.h
@@ -0,0 +1,91 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_NETWORK_MANAGED_STATE_H_
+#define CHROMEOS_NETWORK_MANAGED_STATE_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "chromeos/chromeos_export.h"
+
+namespace base {
+class Value;
+}
+
+namespace chromeos {
+
+class DeviceState;
+class NetworkState;
+
+// Base class for states managed by NetworkStateManger which are associated
+// with a Shill path (e.g. service path or device path).
+class ManagedState {
+ public:
+ enum ManagedType {
+ MANAGED_TYPE_NETWORK,
+ MANAGED_TYPE_DEVICE
+ };
+
+ virtual ~ManagedState();
+
+ // This will construct and return a new instance of the approprate class
+ // based on |type|.
+ static ManagedState* Create(ManagedType type, const std::string& path);
+
+ // Returns the specific class pointer if this is the correct type, or
+ // NULL if it is not.
+ NetworkState* AsNetworkState();
+ DeviceState* AsDeviceState();
+
+ // Called by NetworkStateHandler when a property changes. Returns true if
+ // the property was recognized and parsed successfully.
+ virtual bool PropertyChanged(const std::string& key,
+ const base::Value& value) = 0;
+
+ const ManagedType managed_type() const { return managed_type_; }
+ const std::string& path() const { return path_; }
+ const std::string& name() const { return name_; }
+ const std::string& type() const { return type_; }
+ bool is_observed() const { return is_observed_; }
+ void set_is_observed(bool is_observed) { is_observed_ = is_observed; }
+
+ protected:
+ ManagedState(ManagedType type, const std::string& path);
+
+ // Parses common property keys (name, type).
+ bool ManagedStatePropertyChanged(const std::string& key,
+ const base::Value& value);
+
+ // Helper methods that log warnings and return false if parsing failed.
+ bool GetBooleanValue(const std::string& key,
+ const base::Value& value,
+ bool* out_value);
+ bool GetIntegerValue(const std::string& key,
+ const base::Value& value,
+ int* out_value);
+ bool GetStringValue(const std::string& key,
+ const base::Value& value,
+ std::string* out_value);
+
+ private:
+ ManagedType managed_type_;
+
+ // The path (e.g. service path or device path) of the managed state object.
+ std::string path_;
+
+ // Common properties shared by all managed state objects.
+ std::string name_; // flimflam::kNameProperty
+ std::string type_; // flimflam::kTypeProperty
+
+ // Tracks when the state is being observed.
+ bool is_observed_;
+
+ DISALLOW_COPY_AND_ASSIGN(ManagedState);
+};
+
+} // namespace chromeos
+
+#endif // CHROMEOS_NETWORK_MANAGED_STATE_H_
diff --git a/chromeos/network/network_state.cc b/chromeos/network/network_state.cc
new file mode 100644
index 0000000..c87bdcb4
--- /dev/null
+++ b/chromeos/network/network_state.cc
@@ -0,0 +1,66 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/network/network_state.h"
+
+#include "base/values.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+namespace chromeos {
+
+NetworkState::NetworkState(const std::string& path)
+ : ManagedState(MANAGED_TYPE_NETWORK, path),
+ signal_strength_(0) {
+}
+
+NetworkState::~NetworkState() {
+}
+
+bool NetworkState::PropertyChanged(const std::string& key,
+ const base::Value& value) {
+ if (ManagedStatePropertyChanged(key, value))
+ return true;
+ if (key == flimflam::kSignalStrengthProperty) {
+ return GetIntegerValue(key, value, &signal_strength_);
+ } else if (key == flimflam::kStateProperty) {
+ return GetStringValue(key, value, &state_);
+ } else if (key == flimflam::kErrorProperty) {
+ return GetStringValue(key, value, &error_);
+ } else if (key == flimflam::kActivationStateProperty) {
+ return GetStringValue(key, value, &activation_state_);
+ } else if (key == flimflam::kRoamingStateProperty) {
+ return GetStringValue(key, value, &roaming_);
+ } else if (key == flimflam::kSecurityProperty) {
+ return GetStringValue(key, value, &security_);
+ } else if (key == flimflam::kNetworkTechnologyProperty) {
+ return GetStringValue(key, value, &technology_);
+ } else if (key == flimflam::kDeviceProperty) {
+ return GetStringValue(key, value, &device_path_);
+ }
+ return false;
+}
+
+bool NetworkState::IsConnectedState() const {
+ return StateIsConnected(state_);
+}
+
+bool NetworkState::IsConnectingState() const {
+ return StateIsConnecting(state_);
+}
+
+// static
+bool NetworkState::StateIsConnected(const std::string& state) {
+ return (state == flimflam::kStateReady ||
+ state == flimflam::kStateOnline ||
+ state == flimflam::kStatePortal);
+}
+
+// static
+bool NetworkState::StateIsConnecting(const std::string& state) {
+ return (state == flimflam::kStateAssociation ||
+ state == flimflam::kStateConfiguration ||
+ state == flimflam::kStateCarrier);
+}
+
+} // namespace chromeos
diff --git a/chromeos/network/network_state.h b/chromeos/network/network_state.h
new file mode 100644
index 0000000..b5f7376
--- /dev/null
+++ b/chromeos/network/network_state.h
@@ -0,0 +1,70 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_NETWORK_NETWORK_STATE_H_
+#define CHROMEOS_NETWORK_NETWORK_STATE_H_
+
+#include "chromeos/network/managed_state.h"
+
+namespace chromeos {
+
+// Simple class to provide network state information about a network service.
+// This class should always be passed as a const* and should never be held
+// on to. Store network_state->path() (defined in ManagedState) instead and
+// call NetworkStateHandler::GetNetworkState(path) to retrieve the state for
+// the network.
+class CHROMEOS_EXPORT NetworkState : public ManagedState {
+ public:
+ explicit NetworkState(const std::string& path);
+ virtual ~NetworkState();
+
+ // ManagedState overrides
+ virtual bool PropertyChanged(const std::string& key,
+ const base::Value& value) OVERRIDE;
+
+ // Accessors
+ const std::string& security() const { return security_; }
+ const std::string& technology() const { return technology_; }
+ const std::string& ip_address() const { return ip_address_; }
+ const std::string& device_path() const { return device_path_; }
+ const std::string& state() const { return state_; }
+ const std::string& error() const { return error_; }
+ const std::string& activation_state() const { return activation_state_; }
+ const std::string& roaming() const { return roaming_; }
+ int signal_strength() const { return signal_strength_; }
+
+ bool IsConnectedState() const;
+ bool IsConnectingState() const;
+
+ // Helpers (used e.g. when a state is cached)
+ static bool StateIsConnected(const std::string& state);
+ static bool StateIsConnecting(const std::string& state);
+
+ private:
+ friend class NetworkStateHandler;
+
+ // Called by NetworkStateHandler when the ip config changes.
+ void set_ip_address(const std::string& ip_address) {
+ ip_address_ = ip_address;
+ }
+
+ // Common Network Service properties
+ std::string security_;
+ std::string device_path_;
+ std::string ip_address_;
+ std::string state_;
+ std::string error_;
+ // Wireless properties
+ int signal_strength_;
+ // Cellular properties
+ std::string technology_;
+ std::string activation_state_;
+ std::string roaming_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetworkState);
+};
+
+} // namespace chromeos
+
+#endif // CHROMEOS_NETWORK_NETWORK_STATE_H_
diff --git a/chromeos/network/network_state_handler.cc b/chromeos/network/network_state_handler.cc
new file mode 100644
index 0000000..7caf2f7
--- /dev/null
+++ b/chromeos/network/network_state_handler.cc
@@ -0,0 +1,389 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/network/network_state_handler.h"
+
+#include "base/stl_util.h"
+#include "base/string_util.h"
+#include "base/values.h"
+#include "chromeos/network/device_state.h"
+#include "chromeos/network/managed_state.h"
+#include "chromeos/network/network_state.h"
+#include "chromeos/network/network_state_handler_observer.h"
+#include "chromeos/network/shill_property_handler.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+namespace chromeos {
+
+NetworkStateHandler::NetworkStateHandler() {
+}
+
+NetworkStateHandler::~NetworkStateHandler() {
+ STLDeleteContainerPointers(network_list_.begin(), network_list_.end());
+ STLDeleteContainerPointers(device_list_.begin(), device_list_.end());
+}
+
+void NetworkStateHandler::Init() {
+ shill_property_handler_.reset(new internal::ShillPropertyHandler(this));
+ shill_property_handler_->Init();
+}
+
+void NetworkStateHandler::AddObserver(NetworkStateHandlerObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void NetworkStateHandler::RemoveObserver(
+ NetworkStateHandlerObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+bool NetworkStateHandler::TechnologyAvailable(
+ const std::string& technology) const {
+ return available_technologies_.find(technology) !=
+ available_technologies_.end();
+}
+
+bool NetworkStateHandler::TechnologyEnabled(
+ const std::string& technology) const {
+ return enabled_technologies_.find(technology) != enabled_technologies_.end();
+}
+
+void NetworkStateHandler::SetTechnologyEnabled(const std::string& technology,
+ bool enabled) {
+ shill_property_handler_->SetTechnologyEnabled(technology, enabled);
+}
+
+const DeviceState* NetworkStateHandler::GetDeviceState(
+ const std::string& device_path) const {
+ return GetModifiableDeviceState(device_path);
+}
+
+const DeviceState* NetworkStateHandler::GetDeviceStateByType(
+ const std::string& type) const {
+ for (ManagedStateList::const_iterator iter = device_list_.begin();
+ iter != device_list_.end(); ++iter) {
+ ManagedState* device = *iter;
+ if (device->type() == type)
+ return device->AsDeviceState();
+ }
+ return NULL;
+}
+
+const NetworkState* NetworkStateHandler::GetNetworkState(
+ const std::string& service_path) const {
+ return GetModifiableNetworkState(service_path);
+}
+
+const NetworkState* NetworkStateHandler::ActiveNetwork() const {
+ if (network_list_.empty())
+ return NULL;
+ const NetworkState* network = network_list_.front()->AsNetworkState();
+ DCHECK(network);
+ if (!network->IsConnectedState())
+ return NULL;
+ return network;
+}
+
+const NetworkState* NetworkStateHandler::ConnectedNetworkByType(
+ const std::string& type) const {
+ for (ManagedStateList::const_iterator iter = network_list_.begin();
+ iter != network_list_.end(); ++iter) {
+ const NetworkState* network = (*iter)->AsNetworkState();
+ DCHECK(network);
+ if (!network->IsConnectedState())
+ break; // Connected networks are listed first.
+ if (network->type() == type)
+ return network;
+ }
+ return NULL;
+}
+
+const NetworkState* NetworkStateHandler::ConnectingNetworkByType(
+ const std::string& type) const {
+ for (ManagedStateList::const_iterator iter = network_list_.begin();
+ iter != network_list_.end(); ++iter) {
+ const NetworkState* network = (*iter)->AsNetworkState();
+ DCHECK(network);
+ if (network->IsConnectedState())
+ continue;
+ if (!network->IsConnectingState())
+ break; // Connected and connecting networks are listed first.
+ if (network->type() == type ||
+ (type.empty() && type != flimflam::kTypeEthernet)) {
+ return network;
+ }
+ }
+ return NULL;
+}
+
+std::string NetworkStateHandler::HardwareAddressForType(
+ const std::string& type) const {
+ std::string result;
+ const NetworkState* network = ConnectedNetworkByType(type);
+ if (network) {
+ const DeviceState* device = GetDeviceState(network->device_path());
+ if (device)
+ result = device->mac_address();
+ }
+ StringToUpperASCII(&result);
+ return result;
+}
+
+std::string NetworkStateHandler::FormattedHardwareAddressForType(
+ const std::string& type) const {
+ std::string address = HardwareAddressForType(type);
+ if (address.size() % 2 != 0)
+ return address;
+ std::string result;
+ for (size_t i = 0; i < address.size(); ++i) {
+ if ((i != 0) && (i % 2 == 0))
+ result.push_back(':');
+ result.push_back(address[i]);
+ }
+ return result;
+}
+
+void NetworkStateHandler::GetNetworkList(NetworkStateList* list) const {
+ DCHECK(list);
+ shill_property_handler_->RequestScan();
+ NetworkStateList result;
+ list->clear();
+ for (ManagedStateList::const_iterator iter = network_list_.begin();
+ iter != network_list_.end(); ++iter) {
+ const NetworkState* network = (*iter)->AsNetworkState();
+ DCHECK(network);
+ list->push_back(network);
+ }
+}
+
+//------------------------------------------------------------------------------
+// ShillPropertyHandler::Delegate overrides
+
+void NetworkStateHandler::UpdateManagedList(ManagedState::ManagedType type,
+ const base::ListValue& entries) {
+ ManagedStateList* managed_list = GetManagedList(type);
+ VLOG(2) << "UpdateManagedList: " << type;
+ // Create a map of existing entries.
+ std::map<std::string, ManagedState*> managed_map;
+ for (ManagedStateList::iterator iter = managed_list->begin();
+ iter != managed_list->end(); ++iter) {
+ ManagedState* managed = *iter;
+ managed_map[managed->path()] = managed;
+ }
+ // Clear the list (pointers are owned by managed_map).
+ managed_list->clear();
+ // Updates managed_list and request updates for new entries.
+ for (base::ListValue::const_iterator iter = entries.begin();
+ iter != entries.end(); ++iter) {
+ std::string path;
+ (*iter)->GetAsString(&path);
+ DCHECK(!path.empty());
+ std::map<std::string, ManagedState*>::iterator found =
+ managed_map.find(path);
+ bool request_properties = false;
+ ManagedState* managed;
+ bool is_observing = shill_property_handler_->IsObservingNetwork(path);
+ if (found == managed_map.end()) {
+ request_properties = true;
+ managed = ManagedState::Create(type, path);
+ managed_list->push_back(managed);
+ } else {
+ managed = found->second;
+ managed_list->push_back(managed);
+ managed_map.erase(found);
+ if (!managed->is_observed() && is_observing)
+ request_properties = true;
+ }
+ if (is_observing)
+ managed->set_is_observed(true);
+ if (request_properties)
+ shill_property_handler_->RequestProperties(type, path);
+ }
+ // Delete any remaning entries in managed_map.
+ STLDeleteContainerPairSecondPointers(managed_map.begin(), managed_map.end());
+}
+
+void NetworkStateHandler::UpdateAvailableTechnologies(
+ const base::ListValue& technologies) {
+ available_technologies_.clear();
+ for (base::ListValue::const_iterator iter = technologies.begin();
+ iter != technologies.end(); ++iter) {
+ std::string technology;
+ (*iter)->GetAsString(&technology);
+ DCHECK(!technology.empty());
+ available_technologies_.insert(technology);
+ }
+}
+
+void NetworkStateHandler::UpdateEnabledTechnologies(
+ const base::ListValue& technologies) {
+ enabled_technologies_.clear();
+ for (base::ListValue::const_iterator iter = technologies.begin();
+ iter != technologies.end(); ++iter) {
+ std::string technology;
+ (*iter)->GetAsString(&technology);
+ DCHECK(!technology.empty());
+ enabled_technologies_.insert(technology);
+ }
+}
+
+void NetworkStateHandler::UpdateManagedStateProperties(
+ ManagedState::ManagedType type,
+ const std::string& path,
+ const base::DictionaryValue& properties) {
+ ManagedState* managed = GetModifiableManagedState(GetManagedList(type), path);
+ if (!managed) {
+ LOG(ERROR) << "GetPropertiesCallback: " << path << " Not found!";
+ return;
+ }
+ bool network_property_changed = false;
+ for (base::DictionaryValue::Iterator iter(properties);
+ iter.HasNext(); iter.Advance()) {
+ if (type == ManagedState::MANAGED_TYPE_NETWORK) {
+ if (ParseNetworkServiceProperty(managed->AsNetworkState(),
+ iter.key(), iter.value()))
+ network_property_changed = true;
+ } else {
+ managed->PropertyChanged(iter.key(), iter.value());
+ }
+ }
+ // Notify observers.
+ if (network_property_changed) {
+ NetworkState* network = managed->AsNetworkState();
+ DCHECK(network);
+ FOR_EACH_OBSERVER(NetworkStateHandlerObserver, observers_,
+ NetworkServiceChanged(network));
+ }
+}
+
+void NetworkStateHandler::UpdateNetworkServiceProperty(
+ const std::string& service_path,
+ const std::string& key,
+ const base::Value& value) {
+ NetworkState* network = GetModifiableNetworkState(service_path);
+ if (!network)
+ return;
+ if (ParseNetworkServiceProperty(network, key, value)) {
+ FOR_EACH_OBSERVER(NetworkStateHandlerObserver, observers_,
+ NetworkServiceChanged(network));
+ }
+}
+
+void NetworkStateHandler::UpdateNetworkServiceIPAddress(
+ const std::string& service_path,
+ const std::string& ip_address) {
+ NetworkState* network = GetModifiableNetworkState(service_path);
+ if (!network)
+ return;
+ network->set_ip_address(ip_address);
+ FOR_EACH_OBSERVER(
+ NetworkStateHandlerObserver, observers_,
+ NetworkServiceChanged(network));
+}
+
+void NetworkStateHandler::ManagerPropertyChanged() {
+ FOR_EACH_OBSERVER(NetworkStateHandlerObserver, observers_,
+ NetworkManagerChanged());
+}
+
+void NetworkStateHandler::ManagedStateListChanged(
+ ManagedState::ManagedType type) {
+ if (type == ManagedState::MANAGED_TYPE_NETWORK) {
+ // Notify observers that the list of networks has changed.
+ NetworkStateList network_list;
+ GetNetworkList(&network_list);
+ FOR_EACH_OBSERVER(NetworkStateHandlerObserver, observers_,
+ NetworkListChanged(network_list));
+ // Update the active network and notify observers if it has changed.
+ NetworkState* new_active_network =
+ network_list_.empty() ? NULL : network_list_.front()->AsNetworkState();
+ std::string new_active_network_path;
+ if (new_active_network)
+ new_active_network_path = new_active_network->path();
+ if (new_active_network_path != active_network_path_) {
+ active_network_path_ = new_active_network_path;
+ FOR_EACH_OBSERVER(NetworkStateHandlerObserver, observers_,
+ ActiveNetworkChanged(new_active_network));
+ }
+ } else if (type == ManagedState::MANAGED_TYPE_DEVICE) {
+ FOR_EACH_OBSERVER(NetworkStateHandlerObserver, observers_,
+ DeviceListChanged());
+ } else {
+ NOTREACHED();
+ }
+}
+
+//------------------------------------------------------------------------------
+// Private methods
+
+DeviceState* NetworkStateHandler::GetModifiableDeviceState(
+ const std::string& device_path) const {
+ ManagedState* managed = GetModifiableManagedState(&device_list_, device_path);
+ if (!managed)
+ return NULL;
+ return managed->AsDeviceState();
+}
+
+NetworkState* NetworkStateHandler::GetModifiableNetworkState(
+ const std::string& service_path) const {
+ ManagedState* managed =
+ GetModifiableManagedState(&network_list_, service_path);
+ if (!managed)
+ return NULL;
+ return managed->AsNetworkState();
+}
+
+ManagedState* NetworkStateHandler::GetModifiableManagedState(
+ const ManagedStateList* managed_list,
+ const std::string& path) const {
+ for (ManagedStateList::const_iterator iter = managed_list->begin();
+ iter != managed_list->end(); ++iter) {
+ ManagedState* managed = *iter;
+ if (managed->path() == path)
+ return managed;
+ }
+ return NULL;
+}
+
+NetworkStateHandler::ManagedStateList* NetworkStateHandler::GetManagedList(
+ ManagedState::ManagedType type) {
+ switch(type) {
+ case ManagedState::MANAGED_TYPE_NETWORK:
+ return &network_list_;
+ case ManagedState::MANAGED_TYPE_DEVICE:
+ return &device_list_;
+ }
+ NOTREACHED();
+ return NULL;
+}
+
+bool NetworkStateHandler::ParseNetworkServiceProperty(
+ NetworkState* network,
+ const std::string& key,
+ const base::Value& value) {
+ DCHECK(network);
+ bool property_changed = false;
+ if (key == shill::kIPConfigProperty) {
+ // Handle IPConfig here instead of in NetworkState::PropertyChanged since
+ // we need to call into shill_property_handler_ to fetch them. This will
+ // trigger a call to UpdateNetworkServiceIPAddress(), which will notify
+ // any observers.
+ std::string ip_config_path;
+ value.GetAsString(&ip_config_path);
+ DCHECK(!ip_config_path.empty());
+ shill_property_handler_->RequestIPConfig(network->path(), ip_config_path);
+ } else {
+ if (network->PropertyChanged(key, value)) {
+ property_changed = true;
+ if (network->path() == active_network_path_ &&
+ key == flimflam::kStateProperty) {
+ FOR_EACH_OBSERVER(NetworkStateHandlerObserver, observers_,
+ ActiveNetworkStateChanged(network));
+ }
+ }
+ }
+ return property_changed;
+}
+
+} // namespace chromeos
diff --git a/chromeos/network/network_state_handler.h b/chromeos/network/network_state_handler.h
new file mode 100644
index 0000000..91d54bf
--- /dev/null
+++ b/chromeos/network/network_state_handler.h
@@ -0,0 +1,190 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_NETWORK_NETWORK_STATE_HANDLER_H_
+#define CHROMEOS_NETWORK_NETWORK_STATE_HANDLER_H_
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/observer_list.h"
+#include "chromeos/chromeos_export.h"
+#include "chromeos/network/managed_state.h"
+#include "chromeos/network/shill_property_handler.h"
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+class Value;
+}
+
+namespace chromeos {
+
+class DeviceState;
+class NetworkState;
+class NetworkStateHandlerObserver;
+
+// Class for tracking the list of visible networks and their state.
+//
+// This class maps essential state from the connection manager (Shill) for
+// each visible network. It is not used to change the state of services or
+// devices, only global (manager) state.
+//
+// All getters return the currently cached state. This class is expected to
+// keep states up to date by managing the appropriate Shill observers.
+// It will invoke its own more specific observer methods when the specified
+// changes occur.
+class CHROMEOS_EXPORT NetworkStateHandler
+ : public internal::ShillPropertyHandler::Listener {
+ public:
+ typedef std::vector<ManagedState*> ManagedStateList;
+ typedef std::vector<const NetworkState*> NetworkStateList;
+
+ NetworkStateHandler();
+ virtual ~NetworkStateHandler();
+
+ // Initialize ShillPropertyHandler.
+ void Init();
+
+ // Add/remove observers.
+ void AddObserver(NetworkStateHandlerObserver* observer);
+ void RemoveObserver(NetworkStateHandlerObserver* observer);
+
+ // Returns true if |technology| is enabled / available.
+ bool TechnologyAvailable(const std::string& technology) const;
+ bool TechnologyEnabled(const std::string& technology) const;
+
+ // Asynchronously sets the enabled state for |technology|.
+ // Note: Modifes Manager state. TODO(stevenjb): Add a completion callback.
+ void SetTechnologyEnabled(const std::string& technology, bool enabled);
+
+ // Finds and returns a device state by |device_path| or NULL if not found.
+ const DeviceState* GetDeviceState(const std::string& device_path) const;
+
+ // Finds and returns a device state by |type|. Returns NULL if not found.
+ const DeviceState* GetDeviceStateByType(const std::string& type) const;
+
+ // Finds and returns a network state by |service_path| or NULL if not found.
+ // Note: NetworkState is frequently updated asynchronously, i.e. properties
+ // are not always updated all at once. This will contain the most recent
+ // value for each state. To receive notifications when the state changes,
+ // observer this class and implement NetworkServiceChanged().
+ const NetworkState* GetNetworkState(const std::string& service_path) const;
+
+ // Returns the "active" network (first network in the list if connected),
+ // NULL if none.
+ const NetworkState* ActiveNetwork() const;
+
+ // Returns the first connected network of type |type|, otherwise NULL.
+ const NetworkState* ConnectedNetworkByType(const std::string& type) const;
+
+ // Returns the first connecting network of type |type|, otherwise NULL.
+ // An empty type will return any connecting non-ethernet network.
+ const NetworkState* ConnectingNetworkByType(const std::string& type) const;
+
+ // Returns the hardware (MAC) address for the first connected network
+ // matching |type|, or an empty string if none.
+ std::string HardwareAddressForType(const std::string& type) const;
+ // Same as above but in aa:bb format.
+ std::string FormattedHardwareAddressForType(const std::string& type) const;
+
+ // Sets |list| to contain the list of networks. The returned list contains
+ // a copy of NetworkState pointers which should not be stored or used beyond
+ // the scope of the calling function (i.e. they may later become invalid, but
+ // only on the UI thread). This also sends a scan request to Shill which may
+ // trigger updates to the networks (which will trigger the appropriate
+ // observer calls).
+ void GetNetworkList(NetworkStateList* list) const;
+
+ // ShillPropertyHandler::Listener overrides.
+
+ // This adds new entries to the managed list specified by |type| and deletes
+ // any entries that are no longer in the list.
+ virtual void UpdateManagedList(ManagedState::ManagedType type,
+ const base::ListValue& entries) OVERRIDE;
+
+ // Sets |available_technologies_| to contain only entries in |technologies|.
+ virtual void UpdateAvailableTechnologies(
+ const base::ListValue& technologies) OVERRIDE;
+
+ // Sets |enabled_technologies_| to contain only entries in |technologies|.
+ virtual void UpdateEnabledTechnologies(
+ const base::ListValue& technologies) OVERRIDE;
+
+ // Parses the properties for the network service or device. Mostly calls
+ // managed->PropertyChanged(key, value) for each dictionary entry.
+ virtual void UpdateManagedStateProperties(
+ ManagedState::ManagedType type,
+ const std::string& path,
+ const base::DictionaryValue& properties) OVERRIDE;
+
+ // Called by ShillPropertyHandler when a watched service property changes.
+ // Calls ParseNetworkServiceProperty() and signals observers.
+ virtual void UpdateNetworkServiceProperty(
+ const std::string& service_path,
+ const std::string& key,
+ const base::Value& value) OVERRIDE;
+
+ // Sets the IP Address for the network associated with |service_path|.
+ virtual void UpdateNetworkServiceIPAddress(
+ const std::string& service_path,
+ const std::string& ip_address) OVERRIDE;
+
+ // Sends NetworkManagerChanged() to observers.
+ virtual void ManagerPropertyChanged() OVERRIDE;
+
+ // Called by |shill_property_handler_| when the service or device list has
+ // changed and all entries have been updated. If |type| == TYPE_NETWORK,
+ // this notifies observers that the network list has changed, and if the
+ // active network has changed sends that notification also.
+ virtual void ManagedStateListChanged(
+ ManagedState::ManagedType type) OVERRIDE;
+
+private:
+ FRIEND_TEST_ALL_PREFIXES(NetworkStateHandlerTest, NetworkStateHandlerStub);
+
+ // Non-const getters for managed entries. These are const so that they can
+ // be called by Get[Network|Device]State, even though they return non-const
+ // pointers.
+ DeviceState* GetModifiableDeviceState(const std::string& device_path) const;
+ NetworkState* GetModifiableNetworkState(
+ const std::string& service_path) const;
+ ManagedState* GetModifiableManagedState(const ManagedStateList* managed_list,
+ const std::string& path) const;
+
+ // Gets the list specified by |type|.
+ ManagedStateList* GetManagedList(ManagedState::ManagedType type);
+
+ // Helper function called to parse |network| properties.
+ bool ParseNetworkServiceProperty(NetworkState* network,
+ const std::string& key,
+ const base::Value& value);
+
+ // Shill property handler instance, owned by this class.
+ scoped_ptr<internal::ShillPropertyHandler> shill_property_handler_;
+
+ // Observer list
+ ObserverList<NetworkStateHandlerObserver> observers_;
+
+ // Lists of managed states
+ ManagedStateList network_list_;
+ ManagedStateList device_list_;
+
+ // Lists of available / enabled technologies
+ std::set<std::string> available_technologies_;
+ std::set<std::string> enabled_technologies_;
+
+ // Keeps track of the active network for notifying observers when it changes.
+ std::string active_network_path_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetworkStateHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROMEOS_NETWORK_NETWORK_STATE_HANDLER_H_
diff --git a/chromeos/network/network_state_handler_observer.cc b/chromeos/network/network_state_handler_observer.cc
new file mode 100644
index 0000000..e573683
--- /dev/null
+++ b/chromeos/network/network_state_handler_observer.cc
@@ -0,0 +1,37 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/network/network_state_handler_observer.h"
+
+namespace chromeos {
+
+NetworkStateHandlerObserver::NetworkStateHandlerObserver() {
+}
+
+NetworkStateHandlerObserver::~NetworkStateHandlerObserver() {
+}
+
+void NetworkStateHandlerObserver::NetworkManagerChanged() {
+}
+
+void NetworkStateHandlerObserver::NetworkListChanged(
+ const NetworkStateList& networks) {
+}
+
+void NetworkStateHandlerObserver::DeviceListChanged() {
+}
+
+void NetworkStateHandlerObserver::ActiveNetworkChanged(
+ const NetworkState* network) {
+}
+
+void NetworkStateHandlerObserver::ActiveNetworkStateChanged(
+ const NetworkState* network) {
+}
+
+void NetworkStateHandlerObserver::NetworkServiceChanged(
+ const NetworkState* network) {
+}
+
+} // namespace chromeos
diff --git a/chromeos/network/network_state_handler_observer.h b/chromeos/network/network_state_handler_observer.h
new file mode 100644
index 0000000..07a4a77
--- /dev/null
+++ b/chromeos/network/network_state_handler_observer.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_NETWORK_NETWORK_STATE_HANDLER_OBSERVER_H_
+#define CHROMEOS_NETWORK_NETWORK_STATE_HANDLER_OBSERVER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "chromeos/chromeos_export.h"
+
+namespace chromeos {
+
+class NetworkState;
+
+// Observer class for all network state changes, including changes to
+// active (connecting or connected) services.
+class CHROMEOS_EXPORT NetworkStateHandlerObserver {
+ public:
+ typedef std::vector<const NetworkState*> NetworkStateList;
+
+ NetworkStateHandlerObserver();
+ virtual ~NetworkStateHandlerObserver();
+
+ // Called when one or more network manager properties changes.
+ virtual void NetworkManagerChanged();
+
+ // The list of networks changed.
+ virtual void NetworkListChanged(const NetworkStateList& networks);
+
+ // The list of devices changed. Typically we don't care about the list
+ // of devices, so they are not passed in the method.
+ virtual void DeviceListChanged();
+
+ // The active network changed. |network| will be NULL if there is no longer
+ // an active network.
+ virtual void ActiveNetworkChanged(const NetworkState* network);
+
+ // The state of the active network changed.
+ virtual void ActiveNetworkStateChanged(const NetworkState* network);
+
+ // One or more network service properties changed. Note: for the active
+ // network, this will be called in *addition* to ActiveNetworkStateChanged()
+ // if the state property changed.
+ virtual void NetworkServiceChanged(const NetworkState* network);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NetworkStateHandlerObserver);
+};
+
+} // namespace chromeos
+
+#endif // CHROMEOS_NETWORK_NETWORK_STATE_HANDLER_OBSERVER_H_
diff --git a/chromeos/network/network_state_handler_unittest.cc b/chromeos/network/network_state_handler_unittest.cc
new file mode 100644
index 0000000..fa68dac
--- /dev/null
+++ b/chromeos/network/network_state_handler_unittest.cc
@@ -0,0 +1,213 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/network/network_state_handler.h"
+
+#include <map>
+#include <set>
+#include <string>
+
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/values.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/shill_manager_client.h"
+#include "chromeos/dbus/shill_service_client.h"
+#include "chromeos/network/network_state.h"
+#include "chromeos/network/network_state_handler_observer.h"
+#include "dbus/object_path.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+namespace {
+
+void ErrorCallbackFunction(const std::string& error_name,
+ const std::string& error_message) {
+ LOG(ERROR) << "Shill Error: " << error_name << " : " << error_message;
+}
+
+using chromeos::NetworkState;
+
+class TestObserver : public chromeos::NetworkStateHandlerObserver {
+ public:
+ TestObserver()
+ : manager_changed_count_(0),
+ network_count_(0) {
+ }
+
+ virtual ~TestObserver() {
+ }
+
+ virtual void NetworkManagerChanged() {
+ ++manager_changed_count_;
+ }
+
+ virtual void NetworkListChanged(
+ const chromeos::NetworkStateHandler::NetworkStateList& networks) {
+ network_count_ = networks.size();
+ }
+
+ virtual void ActiveNetworkChanged(const NetworkState* network) {
+ active_network_ = network ? network->path() : "";
+ active_network_state_ = network ? network->state() : "";
+ }
+
+ virtual void ActiveNetworkStateChanged(const NetworkState* network) {
+ active_network_state_ = network ? network->state() : "";
+ }
+
+ virtual void NetworkServiceChanged(const NetworkState* network) {
+ DCHECK(network);
+ std::map<std::string, int>::iterator iter =
+ property_changes_.find(network->path());
+ if (iter == property_changes_.end())
+ property_changes_[network->path()] = 1;
+ else
+ iter->second++;
+ }
+
+ size_t manager_changed_count() { return manager_changed_count_; }
+ size_t network_count() { return network_count_; }
+ std::string active_network() { return active_network_; }
+ std::string active_network_state() { return active_network_state_; }
+
+ int PropertyChangesForService(const std::string& service_path) {
+ std::map<std::string, int>::iterator iter =
+ property_changes_.find(service_path);
+ if (iter == property_changes_.end())
+ return 0;
+ return iter->second;
+ }
+
+ private:
+ size_t manager_changed_count_;
+ size_t network_count_;
+ std::string active_network_;
+ std::string active_network_state_;
+ std::map<std::string, int> property_changes_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestObserver);
+};
+
+} // namespace
+
+namespace chromeos {
+
+class NetworkStateHandlerTest : public testing::Test {
+ public:
+ NetworkStateHandlerTest() {}
+ virtual ~NetworkStateHandlerTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ // Initialize DBusThreadManager with a stub implementation.
+ DBusThreadManager::InitializeWithStub();
+ }
+
+ virtual void TearDown() OVERRIDE {
+ network_state_handler_.reset();
+ test_observer_.reset();
+ DBusThreadManager::Shutdown();
+ }
+
+ void SetupNetworkStateHandler() {
+ test_observer_.reset(new TestObserver);
+ network_state_handler_.reset(new NetworkStateHandler);
+ network_state_handler_->AddObserver(test_observer_.get());
+ network_state_handler_->Init();
+ }
+
+ protected:
+ MessageLoopForUI message_loop_;
+ scoped_ptr<NetworkStateHandler> network_state_handler_;
+ scoped_ptr<TestObserver> test_observer_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NetworkStateHandlerTest);
+};
+
+TEST_F(NetworkStateHandlerTest, NetworkStateHandlerStub) {
+ // This relies on the stub dbus implementations for ShillManagerClient,
+ SetupNetworkStateHandler();
+ message_loop_.RunUntilIdle();
+ EXPECT_EQ(1u, test_observer_->manager_changed_count());
+ // Ensure that the network list is the expected size.
+ const size_t kNumShillManagerClientStubImplServices = 4;
+ EXPECT_EQ(kNumShillManagerClientStubImplServices,
+ test_observer_->network_count());
+ // Ensure that the first stub network is the active network.
+ const std::string kShillManagerClientStubActiveService = "stub_ethernet";
+ EXPECT_EQ(kShillManagerClientStubActiveService,
+ test_observer_->active_network());
+ EXPECT_EQ(kShillManagerClientStubActiveService,
+ network_state_handler_->ActiveNetwork()->path());
+ EXPECT_EQ(kShillManagerClientStubActiveService,
+ network_state_handler_->ConnectedNetworkByType(
+ flimflam::kTypeEthernet)->path());
+ EXPECT_EQ(flimflam::kStateOnline, test_observer_->active_network_state());
+}
+
+TEST_F(NetworkStateHandlerTest, NetworkStateHandlerTechnologyChanged) {
+ // This relies on the stub dbus implementations for ShillManagerClient,
+ SetupNetworkStateHandler();
+ message_loop_.RunUntilIdle();
+ EXPECT_EQ(1u, test_observer_->manager_changed_count());
+ // Enable a technology.
+ EXPECT_FALSE(network_state_handler_->TechnologyEnabled(flimflam::kTypeWimax));
+ network_state_handler_->SetTechnologyEnabled(flimflam::kTypeWimax, true);
+ message_loop_.RunUntilIdle();
+ // Ensure we get a manager changed callback when we change a property.
+ EXPECT_EQ(2u, test_observer_->manager_changed_count());
+ EXPECT_TRUE(network_state_handler_->TechnologyEnabled(flimflam::kTypeWimax));
+}
+
+TEST_F(NetworkStateHandlerTest, NetworkStateHandlerServicePropertyChanged) {
+ // This relies on the stub dbus implementations for ShillManagerClient,
+ SetupNetworkStateHandler();
+ message_loop_.RunUntilIdle();
+ // Set a service property.
+ const std::string eth0 = "stub_ethernet";
+ EXPECT_EQ("", network_state_handler_->GetNetworkState(eth0)->security());
+ EXPECT_EQ(1, test_observer_->PropertyChangesForService(eth0));
+ base::StringValue security_value("TestSecurity");
+ DBusThreadManager::Get()->GetShillServiceClient()->SetProperty(
+ dbus::ObjectPath(eth0),
+ flimflam::kSecurityProperty, security_value,
+ base::Bind(&base::DoNothing), base::Bind(&ErrorCallbackFunction));
+ message_loop_.RunUntilIdle();
+ EXPECT_EQ("TestSecurity",
+ network_state_handler_->GetNetworkState(eth0)->security());
+ EXPECT_EQ(2, test_observer_->PropertyChangesForService(eth0));
+}
+
+TEST_F(NetworkStateHandlerTest, NetworkStateHandlerActiveServiceChanged) {
+ // This relies on the stub dbus implementations for ShillManagerClient,
+ SetupNetworkStateHandler();
+ message_loop_.RunUntilIdle();
+
+ // Change the active network by inserting wifi1 at the front of the list.
+ ShillManagerClient::TestInterface* manager_test =
+ DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface();
+ ASSERT_TRUE(manager_test);
+ const std::string wifi1 = "stub_wifi1";
+ manager_test->AddServiceAtIndex(wifi1, 0, true);
+ message_loop_.RunUntilIdle();
+ EXPECT_EQ(wifi1, test_observer_->active_network());
+ EXPECT_EQ(flimflam::kStateOnline, test_observer_->active_network_state());
+
+ // Change the state of wifi1, ensure that triggers the active state changed
+ // observer.
+ ShillServiceClient::TestInterface* service_test =
+ DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface();
+ ASSERT_TRUE(service_test);
+ base::StringValue state_value(flimflam::kStateConfiguration);
+ service_test->SetServiceProperty(wifi1, flimflam::kStateProperty,
+ state_value);
+ message_loop_.RunUntilIdle();
+ EXPECT_EQ(wifi1, test_observer_->active_network());
+ EXPECT_EQ(flimflam::kStateConfiguration,
+ test_observer_->active_network_state());
+}
+
+} // namespace chromeos
diff --git a/chromeos/network/shill_property_handler.cc b/chromeos/network/shill_property_handler.cc
new file mode 100644
index 0000000..9fecfd6
--- /dev/null
+++ b/chromeos/network/shill_property_handler.cc
@@ -0,0 +1,272 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/network/shill_property_handler.h"
+
+#include "base/bind.h"
+#include "base/stl_util.h"
+#include "base/string_util.h"
+#include "base/values.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/shill_device_client.h"
+#include "chromeos/dbus/shill_ipconfig_client.h"
+#include "chromeos/dbus/shill_manager_client.h"
+#include "chromeos/dbus/shill_service_client.h"
+#include "chromeos/network/shill_service_observer.h"
+#include "dbus/object_path.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+namespace {
+
+// Limit the number of services we observe. Since they are listed in priority
+// order, it should be reasonable to ignore services past this.
+const size_t kMaxObservedServices = 100;
+
+void ErrorCallbackFunction(const std::string& error_name,
+ const std::string& error_message) {
+ // TODO(stevenjb): Add error logging.
+ LOG(ERROR) << "Shill Error: " << error_name << " : " << error_message;
+}
+
+const base::ListValue* GetListValue(const std::string& key,
+ const base::Value& value) {
+ const base::ListValue* vlist = NULL;
+ if (!value.GetAsList(&vlist)) {
+ LOG(ERROR) << "Error parsing key as list: " << key;
+ return NULL;
+ }
+ return vlist;
+}
+
+} // namespace
+
+namespace chromeos {
+namespace internal {
+
+ShillPropertyHandler::ShillPropertyHandler(Listener* listener)
+ : listener_(listener),
+ shill_manager_(DBusThreadManager::Get()->GetShillManagerClient()),
+ weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
+}
+
+ShillPropertyHandler::~ShillPropertyHandler() {
+ // Delete network service observers.
+ STLDeleteContainerPairSecondPointers(
+ observed_networks_.begin(), observed_networks_.end());
+ CHECK(shill_manager_ == DBusThreadManager::Get()->GetShillManagerClient());
+ shill_manager_->RemovePropertyChangedObserver(this);
+}
+
+void ShillPropertyHandler::Init() {
+ shill_manager_->GetProperties(
+ base::Bind(&ShillPropertyHandler::ManagerPropertiesCallback,
+ weak_ptr_factory_.GetWeakPtr()));
+ shill_manager_->AddPropertyChangedObserver(this);
+}
+
+void ShillPropertyHandler::SetTechnologyEnabled(
+ const std::string& technology,
+ bool enabled) {
+ if (enabled) {
+ shill_manager_->EnableTechnology(technology,
+ base::Bind(&base::DoNothing),
+ base::Bind(&ErrorCallbackFunction));
+ } else {
+ shill_manager_->DisableTechnology(technology,
+ base::Bind(&base::DoNothing),
+ base::Bind(&ErrorCallbackFunction));
+ }
+}
+
+void ShillPropertyHandler::RequestScan() const {
+ shill_manager_->RequestScan("",
+ base::Bind(&base::DoNothing),
+ base::Bind(&ErrorCallbackFunction));
+}
+
+void ShillPropertyHandler::RequestProperties(ManagedState::ManagedType type,
+ const std::string& path) {
+ ++pending_updates_[type];
+ if (type == ManagedState::MANAGED_TYPE_NETWORK) {
+ DBusThreadManager::Get()->GetShillServiceClient()->GetProperties(
+ dbus::ObjectPath(path),
+ base::Bind(&ShillPropertyHandler::GetPropertiesCallback,
+ weak_ptr_factory_.GetWeakPtr(), type, path));
+ } else if (type == ManagedState::MANAGED_TYPE_DEVICE) {
+ DBusThreadManager::Get()->GetShillDeviceClient()->GetProperties(
+ dbus::ObjectPath(path),
+ base::Bind(&ShillPropertyHandler::GetPropertiesCallback,
+ weak_ptr_factory_.GetWeakPtr(), type, path));
+ } else {
+ NOTREACHED();
+ }
+}
+
+void ShillPropertyHandler::RequestIPConfig(
+ const std::string& service_path,
+ const std::string& ip_config_path) {
+ DBusThreadManager::Get()->GetShillIPConfigClient()->GetProperties(
+ dbus::ObjectPath(ip_config_path),
+ base::Bind(&ShillPropertyHandler::GetIPConfigCallback,
+ weak_ptr_factory_.GetWeakPtr(),
+ service_path));
+}
+
+void ShillPropertyHandler::OnPropertyChanged(const std::string& key,
+ const base::Value& value) {
+ if (ManagerPropertyChanged(key, value))
+ listener_->ManagerPropertyChanged();
+}
+
+//------------------------------------------------------------------------------
+// Private methods
+
+void ShillPropertyHandler::ManagerPropertiesCallback(
+ DBusMethodCallStatus call_status,
+ const base::DictionaryValue& properties) {
+ if (call_status != DBUS_METHOD_CALL_SUCCESS) {
+ LOG(ERROR) << "Failed to get Manager properties:" << call_status;
+ return;
+ }
+ bool notify = false;
+ bool update_service_list = false;
+ for (base::DictionaryValue::Iterator iter(properties);
+ iter.HasNext(); iter.Advance()) {
+ // Defer updating Services until all other properties have been updated.
+ if (iter.key() == flimflam::kServicesProperty)
+ update_service_list = true;
+ else
+ notify |= ManagerPropertyChanged(iter.key(), iter.value());
+ }
+ // Now update the service list which can safely assume other properties have
+ // been initially set.
+ if (update_service_list) {
+ const base::Value* value = NULL;
+ if (properties.GetWithoutPathExpansion(flimflam::kServicesProperty, &value))
+ notify |= ManagerPropertyChanged(flimflam::kServicesProperty, *value);
+ }
+ if (notify)
+ listener_->ManagerPropertyChanged();
+}
+
+bool ShillPropertyHandler::ManagerPropertyChanged(const std::string& key,
+ const base::Value& value) {
+ bool notify_manager_changed = false;
+ if (key == flimflam::kServicesProperty) {
+ const base::ListValue* vlist = GetListValue(key, value);
+ if (vlist)
+ UpdateManagedList(ManagedState::MANAGED_TYPE_NETWORK, *vlist);
+ } else if (key == flimflam::kServiceWatchListProperty) {
+ const base::ListValue* vlist = GetListValue(key, value);
+ if (vlist) {
+ UpdateObservedNetworkServices(*vlist);
+ }
+ } else if (key == flimflam::kDevicesProperty) {
+ const ListValue* vlist = GetListValue(key, value);
+ if (vlist)
+ UpdateManagedList(ManagedState::MANAGED_TYPE_DEVICE, *vlist);
+ } else if (key == flimflam::kAvailableTechnologiesProperty) {
+ const base::ListValue* vlist = GetListValue(key, value);
+ if (vlist ) {
+ listener_->UpdateAvailableTechnologies(*vlist);
+ notify_manager_changed = true;
+ }
+ } else if (key == flimflam::kEnabledTechnologiesProperty) {
+ const base::ListValue* vlist = GetListValue(key, value);
+ if (vlist) {
+ listener_->UpdateEnabledTechnologies(*vlist);
+ notify_manager_changed = true;
+ }
+ }
+ return notify_manager_changed;
+}
+
+void ShillPropertyHandler::UpdateManagedList(ManagedState::ManagedType type,
+ const base::ListValue& entries) {
+ listener_->UpdateManagedList(type, entries);
+ // Do not send a ManagerPropertyChanged notification to the Listener if
+ // RequestProperties has been called (ManagedStateListChanged will be
+ // called when the update requests have completed). If no requests
+ // have been made, call ManagedStateListChanged to indicate that the
+ // order of the list has changed.
+ if (pending_updates_[type] == 0)
+ listener_->ManagedStateListChanged(type);
+}
+
+void ShillPropertyHandler::UpdateObservedNetworkServices(
+ const base::ListValue& entries) {
+ // Watch all networks in the watch list.
+ ShillServiceObserverMap new_observed;
+ for (base::ListValue::const_iterator iter1 = entries.begin();
+ iter1 != entries.end(); ++iter1) {
+ std::string path;
+ (*iter1)->GetAsString(&path);
+ if (path.empty())
+ continue;
+ ShillServiceObserverMap::iterator iter2 = observed_networks_.find(path);
+ if (iter2 != observed_networks_.end()) {
+ new_observed[path] = observed_networks_[path];
+ } else {
+ new_observed[path] = new ShillServiceObserver(
+ path, base::Bind(
+ &ShillPropertyHandler::NetworkServicePropertyChangedCallback,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
+ observed_networks_.erase(path);
+ // Limit the number of observed services.
+ if (new_observed.size() >= kMaxObservedServices)
+ break;
+ }
+ VLOG(2) << "UpdateObservedNetworkServices, new observed: "
+ << new_observed.size();
+ // Delete network service observers still in observed_networks_.
+ STLDeleteContainerPairSecondPointers(
+ observed_networks_.begin(), observed_networks_.end());
+ observed_networks_.swap(new_observed);
+}
+
+void ShillPropertyHandler::GetPropertiesCallback(
+ ManagedState::ManagedType type,
+ const std::string& path,
+ DBusMethodCallStatus call_status,
+ const base::DictionaryValue& properties) {
+ VLOG(2) << "GetPropertiesCallback: " << type << " : " << path;
+ --pending_updates_[type];
+ if (call_status != DBUS_METHOD_CALL_SUCCESS) {
+ LOG(ERROR) << "Failed to get properties for: " << path
+ << ": " << call_status;
+ return;
+ }
+ listener_->UpdateManagedStateProperties(type, path, properties);
+ // Notify the listener only when all updates for that type have completed.
+ if (pending_updates_[type] == 0)
+ listener_->ManagedStateListChanged(type);
+}
+
+void ShillPropertyHandler::NetworkServicePropertyChangedCallback(
+ const std::string& path,
+ const std::string& key,
+ const base::Value& value) {
+ listener_->UpdateNetworkServiceProperty(path, key, value);
+}
+
+void ShillPropertyHandler::GetIPConfigCallback(
+ const std::string& service_path,
+ DBusMethodCallStatus call_status,
+ const base::DictionaryValue& properties) {
+ if (call_status != DBUS_METHOD_CALL_SUCCESS) {
+ LOG(ERROR) << "Failed to get IP properties for: " << service_path;
+ return;
+ }
+ std::string ip_address;
+ if (!properties.GetStringWithoutPathExpansion(flimflam::kAddressProperty,
+ &ip_address)) {
+ LOG(ERROR) << "Failed to get IP Address property for: " << service_path;
+ return;
+ }
+ listener_->UpdateNetworkServiceIPAddress(service_path, ip_address);
+}
+
+} // namespace internal
+} // namespace chromeos
diff --git a/chromeos/network/shill_property_handler.h b/chromeos/network/shill_property_handler.h
new file mode 100644
index 0000000..3ab6bb8
--- /dev/null
+++ b/chromeos/network/shill_property_handler.h
@@ -0,0 +1,175 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_NETWORK_NETWORK_STATE_HANDLER_IMPL_H_
+#define CHROMEOS_NETWORK_NETWORK_STATE_HANDLER_IMPL_H_
+
+#include <list>
+#include <map>
+#include <set>
+#include <string>
+
+#include "base/memory/weak_ptr.h"
+#include "chromeos/dbus/dbus_method_call_status.h"
+#include "chromeos/dbus/shill_property_changed_observer.h"
+#include "chromeos/network/managed_state.h"
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+class Value;
+}
+
+namespace chromeos {
+
+class ShillManagerClient;
+
+namespace internal {
+
+class ShillServiceObserver;
+
+// This class handles Shill calls and observers to reflect the state of the
+// Shill Manager and its services and devices. It observes Shill.Manager and
+// requests properties for new devices/networks. It takes a Listener in its
+// constructor (e.g. NetworkStateHandler) that it calls when properties change
+// (including once to set their initial state after Init() gets called).
+// It also observes Shill.Service for all services in Manager.ServiceWatchList.
+// This class must not outlive the ShillManagerClient instance.
+class CHROMEOS_EXPORT ShillPropertyHandler
+ : public ShillPropertyChangedObserver {
+ public:
+ typedef std::map<std::string, ShillServiceObserver*> ShillServiceObserverMap;
+
+ class CHROMEOS_EXPORT Listener {
+ public:
+ // Called when the entries in a managed list have changed.
+ virtual void UpdateManagedList(ManagedState::ManagedType type,
+ const base::ListValue& entries) = 0;
+
+ // Called when the available technologies are set or have changed.
+ virtual void UpdateAvailableTechnologies(
+ const base::ListValue& technologies) = 0;
+
+ // Called when the enabled technologies are set or have changed.
+ virtual void UpdateEnabledTechnologies(
+ const base::ListValue& technologies) = 0;
+
+ // Called when the properties for a managed state have changed.
+ virtual void UpdateManagedStateProperties(
+ ManagedState::ManagedType type,
+ const std::string& path,
+ const base::DictionaryValue& properties) = 0;
+
+ // Called when a property for a watched network service has changed.
+ virtual void UpdateNetworkServiceProperty(
+ const std::string& service_path,
+ const std::string& key,
+ const base::Value& value) = 0;
+
+ // Called when one or more manager properties (e.g. a technology list)
+ // changes.
+ virtual void ManagerPropertyChanged() = 0;
+
+ // Called whent the IP address of a service has been updated. Occurs after
+ // UpdateManagedStateProperties is called for the service.
+ virtual void UpdateNetworkServiceIPAddress(
+ const std::string& service_path,
+ const std::string& ip_address) = 0;
+
+ // Called when a managed state list has changed, after properties for any
+ // new entries in the list have been received and
+ // UpdateManagedStateProperties has been called for each new entry.
+ virtual void ManagedStateListChanged(ManagedState::ManagedType type) = 0;
+
+ protected:
+ virtual ~Listener() {}
+ };
+
+ explicit ShillPropertyHandler(Listener* listener);
+ virtual ~ShillPropertyHandler();
+
+ // Sends an initial property request and sets up the observer.
+ void Init();
+
+ // Asynchronously sets the enabled state for |technology|.
+ // Note: Modifes Manager state. TODO(stevenjb): Add a completion callback.
+ void SetTechnologyEnabled(const std::string& technology, bool enabled);
+
+ // Requests an immediate network scan.
+ void RequestScan() const;
+
+ // Requests all properties for the service or device (called for new items).
+ void RequestProperties(ManagedState::ManagedType type,
+ const std::string& path);
+
+ // Requests the IP config specified by |ip_config_path| for |service_path|.
+ void RequestIPConfig(const std::string& service_path,
+ const std::string& ip_config_path);
+
+ bool IsObservingNetwork(const std::string& service_path) {
+ return observed_networks_.count(service_path) != 0;
+ }
+
+ // ShillPropertyChangedObserver overrides
+ virtual void OnPropertyChanged(const std::string& key,
+ const base::Value& value) OVERRIDE;
+
+ private:
+ // Callback for dbus method fetching properties.
+ void ManagerPropertiesCallback(DBusMethodCallStatus call_status,
+ const base::DictionaryValue& properties);
+ // Called form OnPropertyChanged() and ManagerPropertiesCallback().
+ // Returns true if observers should be notified.
+ bool ManagerPropertyChanged(const std::string& key, const base::Value& value);
+
+ // Calls listener_->UpdateManagedList and triggers ManagedStateListChanged if
+ // no new property requests have been made.
+ void UpdateManagedList(ManagedState::ManagedType type,
+ const base::ListValue& entries);
+
+ // Updates the Shill service property observers to observe any entries
+ // in the service watch list.
+ void UpdateObservedNetworkServices(const base::ListValue& entries);
+
+ // Called when Shill returns the properties for a service or device.
+ void GetPropertiesCallback(ManagedState::ManagedType type,
+ const std::string& path,
+ DBusMethodCallStatus call_status,
+ const base::DictionaryValue& properties);
+
+ // Callback invoked when a watched service property changes. Calls
+ // network->PropertyChanged(key, value) and signals observers.
+ void NetworkServicePropertyChangedCallback(const std::string& path,
+ const std::string& key,
+ const base::Value& value);
+
+ // Callback for getting the IPConfig property of a Network. Handled here
+ // instead of in NetworkState so that all asynchronous requests are done
+ // in a single place (also simplifies NetworkState considerably).
+ void GetIPConfigCallback(const std::string& service_path,
+ DBusMethodCallStatus call_status,
+ const base::DictionaryValue& properties);
+
+ // Pointer to containing class (owns this)
+ Listener* listener_;
+
+ // Convenience pointer for ShillManagerClient
+ ShillManagerClient* shill_manager_;
+
+ // Pending update count for each managed state type
+ std::map<ManagedState::ManagedType, int> pending_updates_;
+
+ // List of network services with Shill property changed observers
+ ShillServiceObserverMap observed_networks_;
+
+ // For Shill client callbacks
+ base::WeakPtrFactory<ShillPropertyHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ShillPropertyHandler);
+};
+
+} // namespace internal
+} // namespace chromeos
+
+#endif // CHROMEOS_NETWORK_NETWORK_STATE_HANDLER_IMPL_H_
diff --git a/chromeos/network/shill_property_handler_unittest.cc b/chromeos/network/shill_property_handler_unittest.cc
new file mode 100644
index 0000000..f9954d6
--- /dev/null
+++ b/chromeos/network/shill_property_handler_unittest.cc
@@ -0,0 +1,368 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/network/shill_property_handler.h"
+
+#include <map>
+#include <set>
+#include <string>
+
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/values.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/shill_device_client.h"
+#include "chromeos/dbus/shill_manager_client.h"
+#include "chromeos/dbus/shill_service_client.h"
+#include "dbus/object_path.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+namespace chromeos {
+
+namespace {
+
+void ErrorCallbackFunction(const std::string& error_name,
+ const std::string& error_message) {
+ LOG(ERROR) << "Shill Error: " << error_name << " : " << error_message;
+}
+
+class TestListener : public internal::ShillPropertyHandler::Listener {
+ public:
+ TestListener() : manager_updates_(0), errors_(0) {
+ }
+
+ virtual void UpdateManagedList(ManagedState::ManagedType type,
+ const base::ListValue& entries) OVERRIDE {
+ UpdateEntries(GetTypeString(type), entries);
+ }
+
+ virtual void UpdateAvailableTechnologies(
+ const base::ListValue& technologies) OVERRIDE {
+ UpdateEntries(flimflam::kAvailableTechnologiesProperty, technologies);
+ }
+
+ virtual void UpdateEnabledTechnologies(
+ const base::ListValue& technologies) OVERRIDE {
+ UpdateEntries(flimflam::kEnabledTechnologiesProperty, technologies);
+ }
+
+ virtual void UpdateManagedStateProperties(
+ ManagedState::ManagedType type,
+ const std::string& path,
+ const base::DictionaryValue& properties) OVERRIDE {
+ AddPropertyUpdate(GetTypeString(type), path);
+ }
+
+ virtual void UpdateNetworkServiceProperty(
+ const std::string& service_path,
+ const std::string& key,
+ const base::Value& value) OVERRIDE {
+ AddPropertyUpdate(flimflam::kServicesProperty, service_path);
+ }
+
+ virtual void ManagerPropertyChanged() OVERRIDE {
+ ++manager_updates_;
+ }
+
+ virtual void UpdateNetworkServiceIPAddress(
+ const std::string& service_path,
+ const std::string& ip_address) OVERRIDE {
+ AddPropertyUpdate(flimflam::kServicesProperty, service_path);
+ }
+
+ virtual void ManagedStateListChanged(
+ ManagedState::ManagedType type) OVERRIDE {
+ AddStateListUpdate(GetTypeString(type));
+ }
+
+ std::vector<std::string>& entries(const std::string& type) {
+ return entries_[type];
+ }
+ std::map<std::string, int>& property_updates(const std::string& type) {
+ return property_updates_[type];
+ }
+ int list_updates(const std::string& type) { return list_updates_[type]; }
+ int manager_updates() { return manager_updates_; }
+ int errors() { return errors_; }
+
+ private:
+ std::string GetTypeString(ManagedState::ManagedType type) {
+ if (type == ManagedState::MANAGED_TYPE_NETWORK) {
+ return flimflam::kServicesProperty;
+ } else if (type == ManagedState::MANAGED_TYPE_DEVICE) {
+ return flimflam::kDevicesProperty;
+ }
+ LOG(ERROR) << "UpdateManagedList called with unrecognized type: " << type;
+ ++errors_;
+ return std::string();
+ }
+
+ void UpdateEntries(const std::string& type, const base::ListValue& entries) {
+ if (type.empty())
+ return;
+ entries_[type].clear();
+ for (base::ListValue::const_iterator iter = entries.begin();
+ iter != entries.end(); ++iter) {
+ std::string path;
+ if ((*iter)->GetAsString(&path))
+ entries_[type].push_back(path);
+ }
+ }
+
+ void AddPropertyUpdate(const std::string& type, const std::string& path) {
+ if (type.empty())
+ return;
+ property_updates(type)[path] += 1;
+ }
+
+ void AddStateListUpdate(const std::string& type) {
+ if (type.empty())
+ return;
+ list_updates_[type] += 1;
+ }
+
+ // Map of list-type -> paths
+ std::map<std::string, std::vector<std::string> > entries_;
+ // Map of list-type -> map of paths -> update counts
+ std::map<std::string, std::map<std::string, int> > property_updates_;
+ // Map of list-type -> list update counts
+ std::map<std::string, int > list_updates_;
+ int manager_updates_;
+ int errors_;
+};
+
+} // namespace
+
+class ShillPropertyHandlerTest : public testing::Test {
+ public:
+ ShillPropertyHandlerTest()
+ : manager_test_(NULL),
+ device_test_(NULL),
+ service_test_(NULL) {
+ }
+ virtual ~ShillPropertyHandlerTest() {
+ }
+
+ virtual void SetUp() OVERRIDE {
+ // Initialize DBusThreadManager with a stub implementation.
+ DBusThreadManager::InitializeWithStub();
+ // Get the test interface for manager / device / service and clear the
+ // default stub properties.
+ manager_test_ =
+ DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface();
+ ASSERT_TRUE(manager_test_);
+ device_test_ =
+ DBusThreadManager::Get()->GetShillDeviceClient()->GetTestInterface();
+ ASSERT_TRUE(device_test_);
+ service_test_ =
+ DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface();
+ ASSERT_TRUE(service_test_);
+ }
+
+ virtual void TearDown() OVERRIDE {
+ shill_property_handler_.reset();
+ listener_.reset();
+ DBusThreadManager::Shutdown();
+ }
+
+ void AddDevice(const std::string& type, const std::string& id) {
+ ASSERT_TRUE(IsValidType(type));
+ manager_test_->AddDevice(id);
+ device_test_->AddDevice(id, type, std::string("/device/" + id), "/stub");
+ }
+
+ void RemoveDevice(const std::string& id) {
+ manager_test_->RemoveDevice(id);
+ device_test_->RemoveDevice(id);
+ }
+
+ void AddService(const std::string& type,
+ const std::string& id,
+ const std::string& state,
+ bool add_to_watch_list) {
+ ASSERT_TRUE(IsValidType(type));
+ manager_test_->AddService(id, add_to_watch_list);
+ service_test_->AddService(id, id, type, state);
+ }
+
+ void RemoveService(const std::string& id) {
+ manager_test_->RemoveService(id);
+ service_test_->RemoveService(id);
+ }
+
+ // Call this after any initial Shill client setup
+ void SetupShillPropertyHandler() {
+ listener_.reset(new TestListener);
+ shill_property_handler_.reset(
+ new internal::ShillPropertyHandler(listener_.get()));
+ shill_property_handler_->Init();
+ }
+
+ bool IsValidType(const std::string& type) {
+ return (type == flimflam::kTypeEthernet ||
+ type == flimflam::kTypeWifi ||
+ type == flimflam::kTypeWimax ||
+ type == flimflam::kTypeBluetooth ||
+ type == flimflam::kTypeCellular ||
+ type == flimflam::kTypeVPN);
+ }
+
+ protected:
+ MessageLoopForUI message_loop_;
+ scoped_ptr<TestListener> listener_;
+ scoped_ptr<internal::ShillPropertyHandler> shill_property_handler_;
+ ShillManagerClient::TestInterface* manager_test_;
+ ShillDeviceClient::TestInterface* device_test_;
+ ShillServiceClient::TestInterface* service_test_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ShillPropertyHandlerTest);
+};
+
+TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerStub) {
+ SetupShillPropertyHandler();
+ message_loop_.RunUntilIdle();
+ EXPECT_EQ(1, listener_->manager_updates());
+ // ShillManagerClient default stub entries are in shill_manager_client.cc.
+ // TODO(stevenjb): Eliminate default stub entries and add them explicitly.
+ const size_t kNumShillManagerClientStubImplTechnologies = 3;
+ EXPECT_EQ(kNumShillManagerClientStubImplTechnologies,
+ listener_->entries(
+ flimflam::kAvailableTechnologiesProperty).size());
+ EXPECT_EQ(kNumShillManagerClientStubImplTechnologies,
+ listener_->entries(
+ flimflam::kEnabledTechnologiesProperty).size());
+ const size_t kNumShillManagerClientStubImplDevices = 2;
+ EXPECT_EQ(kNumShillManagerClientStubImplDevices,
+ listener_->entries(flimflam::kDevicesProperty).size());
+ const size_t kNumShillManagerClientStubImplServices = 4;
+ EXPECT_EQ(kNumShillManagerClientStubImplServices,
+ listener_->entries(flimflam::kServicesProperty).size());
+
+ EXPECT_EQ(0, listener_->errors());
+}
+
+TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerTechnologyChanged) {
+ // This relies on the stub dbus implementations for ShillManagerClient,
+ SetupShillPropertyHandler();
+ message_loop_.RunUntilIdle();
+ EXPECT_EQ(1, listener_->manager_updates());
+ // Add a disabled technology.
+ manager_test_->AddTechnology(flimflam::kTypeWimax, false);
+ message_loop_.RunUntilIdle();
+ EXPECT_EQ(2, listener_->manager_updates());
+ const size_t kNumShillManagerClientStubImplTechnologies = 3;
+ EXPECT_EQ(kNumShillManagerClientStubImplTechnologies + 1,
+ listener_->entries(
+ flimflam::kAvailableTechnologiesProperty).size());
+ EXPECT_EQ(kNumShillManagerClientStubImplTechnologies,
+ listener_->entries(
+ flimflam::kEnabledTechnologiesProperty).size());
+ // Enable the technology.
+ DBusThreadManager::Get()->GetShillManagerClient()->EnableTechnology(
+ flimflam::kTypeWimax,
+ base::Bind(&base::DoNothing), base::Bind(&ErrorCallbackFunction));
+ message_loop_.RunUntilIdle();
+ EXPECT_EQ(3, listener_->manager_updates());
+ EXPECT_EQ(kNumShillManagerClientStubImplTechnologies + 1,
+ listener_->entries(
+ flimflam::kEnabledTechnologiesProperty).size());
+
+ EXPECT_EQ(0, listener_->errors());
+}
+
+TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerDevicePropertyChanged) {
+ // This relies on the stub dbus implementations for ShillManagerClient,
+ SetupShillPropertyHandler();
+ message_loop_.RunUntilIdle();
+ EXPECT_EQ(1, listener_->manager_updates());
+ EXPECT_EQ(1, listener_->list_updates(flimflam::kDevicesProperty));
+ const size_t kNumShillManagerClientStubImplDevices = 2;
+ EXPECT_EQ(kNumShillManagerClientStubImplDevices,
+ listener_->entries(flimflam::kDevicesProperty).size());
+ // Add a device.
+ const std::string kTestDevicePath("test_wifi_device1");
+ AddDevice(flimflam::kTypeWifi, kTestDevicePath);
+ message_loop_.RunUntilIdle();
+ EXPECT_EQ(1, listener_->manager_updates()); // No new manager updates.
+ EXPECT_EQ(2, listener_->list_updates(flimflam::kDevicesProperty));
+ EXPECT_EQ(kNumShillManagerClientStubImplDevices + 1,
+ listener_->entries(flimflam::kDevicesProperty).size());
+ // Device changes are not observed.
+ // Remove a device
+ RemoveDevice(kTestDevicePath);
+ message_loop_.RunUntilIdle();
+ EXPECT_EQ(3, listener_->list_updates(flimflam::kDevicesProperty));
+ EXPECT_EQ(kNumShillManagerClientStubImplDevices,
+ listener_->entries(flimflam::kDevicesProperty).size());
+
+ EXPECT_EQ(0, listener_->errors());
+}
+
+TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerServicePropertyChanged) {
+ // This relies on the stub dbus implementations for ShillManagerClient,
+ SetupShillPropertyHandler();
+ message_loop_.RunUntilIdle();
+ EXPECT_EQ(1, listener_->manager_updates());
+ EXPECT_EQ(1, listener_->list_updates(flimflam::kServicesProperty));
+ const size_t kNumShillManagerClientStubImplServices = 4;
+ EXPECT_EQ(kNumShillManagerClientStubImplServices,
+ listener_->entries(flimflam::kServicesProperty).size());
+
+ // Add an unwatched service.
+ const std::string kTestServicePath("test_wifi_service1");
+ AddService(flimflam::kTypeWifi, kTestServicePath,
+ flimflam::kStateIdle, false);
+ message_loop_.RunUntilIdle();
+ EXPECT_EQ(1, listener_->manager_updates()); // No new manager updates.
+ EXPECT_EQ(2, listener_->list_updates(flimflam::kServicesProperty));
+ EXPECT_EQ(kNumShillManagerClientStubImplServices + 1,
+ listener_->entries(flimflam::kServicesProperty).size());
+ // Change a property.
+ base::FundamentalValue scan_interval(3);
+ DBusThreadManager::Get()->GetShillServiceClient()->SetProperty(
+ dbus::ObjectPath(kTestServicePath),
+ flimflam::kScanIntervalProperty,
+ scan_interval,
+ base::Bind(&base::DoNothing), base::Bind(&ErrorCallbackFunction));
+ message_loop_.RunUntilIdle();
+ // Property change should NOT trigger an update.
+ EXPECT_EQ(0, listener_->
+ property_updates(flimflam::kServicesProperty)[kTestServicePath]);
+
+ // Add the existing service to the watch list.
+ AddService(flimflam::kTypeWifi, kTestServicePath,
+ flimflam::kStateIdle, true);
+ message_loop_.RunUntilIdle();
+ // No new updates or services:
+ EXPECT_EQ(1, listener_->manager_updates());
+ EXPECT_EQ(2, listener_->list_updates(flimflam::kServicesProperty));
+ EXPECT_EQ(kNumShillManagerClientStubImplServices + 1,
+ listener_->entries(flimflam::kServicesProperty).size());
+ // Change a property.
+ DBusThreadManager::Get()->GetShillServiceClient()->SetProperty(
+ dbus::ObjectPath(kTestServicePath),
+ flimflam::kScanIntervalProperty,
+ scan_interval,
+ base::Bind(&base::DoNothing), base::Bind(&ErrorCallbackFunction));
+ message_loop_.RunUntilIdle();
+ // Property change SHOULD trigger an update.
+ EXPECT_EQ(1, listener_->
+ property_updates(flimflam::kServicesProperty)[kTestServicePath]);
+
+ // Remove a service
+ RemoveService(kTestServicePath);
+ message_loop_.RunUntilIdle();
+ EXPECT_EQ(3, listener_->list_updates(flimflam::kServicesProperty));
+ EXPECT_EQ(kNumShillManagerClientStubImplServices,
+ listener_->entries(flimflam::kServicesProperty).size());
+
+ EXPECT_EQ(0, listener_->errors());
+}
+
+// TODO(stevenjb): Test IP Configs.
+
+} // namespace chromeos
diff --git a/chromeos/network/shill_service_observer.cc b/chromeos/network/shill_service_observer.cc
new file mode 100644
index 0000000..ae42690
--- /dev/null
+++ b/chromeos/network/shill_service_observer.cc
@@ -0,0 +1,33 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/network/shill_service_observer.h"
+
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/shill_service_client.h"
+#include "dbus/object_path.h"
+
+namespace chromeos {
+namespace internal {
+
+ShillServiceObserver::ShillServiceObserver(const std::string& service_path,
+ const Handler& handler)
+ : service_path_(service_path),
+ handler_(handler) {
+ DBusThreadManager::Get()->GetShillServiceClient()->
+ AddPropertyChangedObserver(dbus::ObjectPath(service_path), this);
+}
+
+ShillServiceObserver::~ShillServiceObserver() {
+ DBusThreadManager::Get()->GetShillServiceClient()->
+ RemovePropertyChangedObserver(dbus::ObjectPath(service_path_), this);
+}
+
+void ShillServiceObserver::OnPropertyChanged(const std::string& key,
+ const base::Value& value) {
+ handler_.Run(service_path_, key, value);
+}
+
+} // namespace internal
+} // namespace chromeos
diff --git a/chromeos/network/shill_service_observer.h b/chromeos/network/shill_service_observer.h
new file mode 100644
index 0000000..1365f0e
--- /dev/null
+++ b/chromeos/network/shill_service_observer.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_NETWORK_SHILL_SERVICE_OBSERVER_H_
+#define CHROMEOS_NETWORK_SHILL_SERVICE_OBSERVER_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "chromeos/dbus/shill_property_changed_observer.h"
+
+namespace chromeos {
+namespace internal {
+
+// Class to manage Shill service property changed observers. Observers are
+// added on construction and removed on destruction. Runs the handler when
+// OnPropertyChanged is called.
+class ShillServiceObserver : public ShillPropertyChangedObserver {
+ public:
+ typedef base::Callback<void(const std::string& service,
+ const std::string& name,
+ const base::Value& value)> Handler;
+
+ ShillServiceObserver(const std::string& service_path,
+ const Handler& handler);
+
+ virtual ~ShillServiceObserver();
+
+ // ShillPropertyChangedObserver overrides.
+ virtual void OnPropertyChanged(const std::string& key,
+ const base::Value& value) OVERRIDE;
+
+ private:
+ std::string service_path_;
+ Handler handler_;
+
+ DISALLOW_COPY_AND_ASSIGN(ShillServiceObserver);
+};
+
+} // namespace internal
+} // namespace chromeos
+
+#endif // CHROMEOS_NETWORK_SHILL_SERVICE_OBSERVER_H_