Chromium Code Reviews| Index: chromeos/network/network_state_manager.cc |
| diff --git a/chromeos/network/network_state_manager.cc b/chromeos/network/network_state_manager.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..4fea4f0276780093e17eb25424ba0ea9a9934f5f |
| --- /dev/null |
| +++ b/chromeos/network/network_state_manager.cc |
| @@ -0,0 +1,533 @@ |
| +// 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_manager.h" |
| + |
| +#include <algorithm> |
| + |
| +#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/device_state.h" |
| +#include "chromeos/network/managed_state.h" |
| +#include "chromeos/network/network_state.h" |
| +#include "chromeos/network/network_service_observer.h" |
| +#include "dbus/object_path.h" |
| + |
| +namespace { |
| + |
| +void ErrorCallbackFunction(const std::string& error_name, |
| + const std::string& error_message) { |
|
stevenjb
2012/10/23 23:12:09
Add TODO: Add error logging, including the API (e.
|
| + 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 chromeos { |
| + |
| +NetworkStateManager::NetworkStateManager() |
| + : shill_manager_(DBusThreadManager::Get()->GetShillManagerClient()), |
| + weak_ptr_factory_(this) { |
| +} |
| + |
| +NetworkStateManager::~NetworkStateManager() { |
| + STLDeleteContainerPointers(network_list_.begin(), network_list_.end()); |
| + // Delete network service observers. |
| + STLDeleteContainerPairSecondPointers( |
| + observed_networks_.begin(), observed_networks_.end()); |
| + CHECK(shill_manager_ == DBusThreadManager::Get()->GetShillManagerClient()); |
| + shill_manager_->RemovePropertyChangedObserver(this); |
| +} |
| + |
| +void NetworkStateManager::Init() { |
| + shill_manager_->GetProperties( |
| + base::Bind(&NetworkStateManager::ManagerPropertiesCallback, |
| + weak_ptr_factory_.GetWeakPtr())); |
| + shill_manager_->AddPropertyChangedObserver(this); |
| +} |
| + |
| +void NetworkStateManager::AddObserver(Observer* observer) { |
| + observers_.AddObserver(observer); |
| +} |
| + |
| +void NetworkStateManager::RemoveObserver(Observer* observer) { |
| + observers_.RemoveObserver(observer); |
| +} |
| + |
| +void NetworkStateManager::RequestScan() const { |
| + if (TechnologyEnabled(flimflam::kTypeWifi)) { |
| + shill_manager_->RequestScan(flimflam::kTypeWifi, |
| + base::Bind(&base::DoNothing), |
| + base::Bind(&ErrorCallbackFunction)); |
| + } |
| + if (TechnologyEnabled(flimflam::kTypeWimax)) { |
| + shill_manager_->RequestScan(flimflam::kTypeWimax, |
| + base::Bind(&base::DoNothing), |
| + base::Bind(&ErrorCallbackFunction)); |
| + } |
| +} |
| + |
| +bool NetworkStateManager::TechnologyAvailable( |
| + const std::string& technology) const { |
| + return available_technologies_.find(technology) != |
| + available_technologies_.end(); |
| +} |
| + |
| +bool NetworkStateManager::TechnologyEnabled( |
| + const std::string& technology) const { |
| + return enabled_technologies_.find(technology) != |
| + enabled_technologies_.end(); |
| +} |
| + |
| +void NetworkStateManager::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)); |
| + } |
| +} |
| + |
| +const DeviceState* NetworkStateManager::GetDeviceState( |
| + const std::string& path) const { |
| + return const_cast<NetworkStateManager*>(this)->GetModifiableDeviceState(path); |
| +} |
| + |
| +const DeviceState* NetworkStateManager::GetDeviceStateByType( |
| + const std::string& type) const { |
| + for (DeviceStateList::const_iterator iter = device_list_.begin(); |
| + iter != device_list_.end(); ++iter) { |
| + const DeviceState* device = *iter; |
| + if (device->type() == type) |
| + return device; |
| + } |
| + return NULL; |
| +} |
| + |
| +const NetworkState* NetworkStateManager::GetNetworkState( |
| + const std::string& path) const { |
| + return const_cast<NetworkStateManager*>(this)->GetModifiableNetworkState( |
| + path); |
| +} |
| + |
| +const NetworkState* NetworkStateManager::ActiveNetwork() const { |
| + if (network_list_.empty()) |
| + return NULL; |
| + const NetworkState* network = network_list_.front(); |
| + if (!network->IsConnectedState()) |
| + return NULL; |
| + return network; |
| +} |
| + |
| +const NetworkState* NetworkStateManager::ConnectedNetworkByType( |
| + const std::string& type) const { |
| + for (NetworkStateList::const_iterator iter = network_list_.begin(); |
| + iter != network_list_.end(); ++iter) { |
| + const NetworkState* network = *iter; |
| + if (!network->IsConnectedState()) |
| + break; // Connected networks are listed first. |
| + if (network->type() == type) |
| + return network; |
| + } |
| + return NULL; |
| +} |
| + |
| +const NetworkState* NetworkStateManager::ConnectingNetworkByType( |
| + const std::string& type) const { |
| + for (NetworkStateList::const_iterator iter = network_list_.begin(); |
| + iter != network_list_.end(); ++iter) { |
| + const NetworkState* network = *iter; |
| + 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 NetworkStateManager::HardwareAddress( |
| + 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 NetworkStateManager::HardwareAddressFormatted( |
| + const std::string& type) const { |
| + std::string address = HardwareAddress(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 NetworkStateManager::OnPropertyChanged(const std::string& key, |
| + const base::Value& value) { |
| + if (ManagerPropertyChanged(key, value)) |
| + FOR_EACH_OBSERVER(Observer, observers_, NetworkManagerChanged(key)); |
| +} |
| + |
| +//------------------------------------------------------------------------------ |
| +// Private methods |
| + |
| +DeviceState* NetworkStateManager::GetModifiableDeviceState( |
| + const std::string& path) { |
| + for (DeviceStateList::iterator iter = device_list_.begin(); |
| + iter != device_list_.end(); ++iter) { |
| + DeviceState* device = *iter; |
| + if (device->path() == path) |
| + return device; |
| + } |
| + return NULL; |
| +} |
| + |
| +NetworkState* NetworkStateManager::GetModifiableNetworkState( |
| + const std::string& path) { |
| + for (NetworkStateList::iterator iter = network_list_.begin(); |
| + iter != network_list_.end(); ++iter) { |
| + NetworkState* network = *iter; |
| + if (network->path() == path) |
| + return network; |
| + } |
| + return NULL; |
| +} |
| + |
| +void NetworkStateManager::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; |
| + for (base::DictionaryValue::key_iterator iter = properties.begin_keys(); |
| + iter != properties.end_keys(); ++iter) { |
| + const std::string& key = *iter; |
| + const base::Value* value; |
| + bool res = properties.GetWithoutPathExpansion(key, &value); |
| + if (res) |
| + notify |= ManagerPropertyChanged(key, *value); |
| + else |
| + LOG(ERROR) << "Error getting value for key: " << key; |
| + } |
| + if (notify) { |
| + FOR_EACH_OBSERVER( |
| + Observer, observers_, NetworkManagerChanged(std::string())); |
| + } |
| +} |
| + |
| +bool NetworkStateManager::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); |
| + // Do not send an UpdateManagerChanged notification to observers. |
| + // An UpdateNetworkList notification will be sent when the service list |
| + // updates have completed. |
| + } |
| + } else if (key == flimflam::kDevicesProperty) { |
| + const ListValue* vlist = GetListValue(key, value); |
| + if (vlist) { |
| + UpdateManagedList(ManagedState::MANAGED_TYPE_DEVICE, *vlist); |
| + notify_manager_changed = true; |
| + } |
| + } else if (key == flimflam::kAvailableTechnologiesProperty) { |
| + const base::ListValue* vlist = GetListValue(key, value); |
| + if (vlist ) { |
| + UpdateAvailableTechnologies(*vlist); |
| + notify_manager_changed = true; |
| + } |
| + } else if (key == flimflam::kEnabledTechnologiesProperty) { |
| + const base::ListValue* vlist = GetListValue(key, value); |
| + if (vlist) { |
| + UpdateEnabledTechnologies(*vlist); |
| + notify_manager_changed = true; |
| + } |
| + } |
| + return notify_manager_changed; |
| +} |
| + |
| +NetworkStateManager::ManagedStateList* NetworkStateManager::GetManagedList( |
| + ManagedState::ManagedType type) { |
| + // Use reinterpret_cast here to avoid copying the list to a list of identical |
|
pneubeck (no reviews)
2012/10/24 14:41:42
We shouldn't be concerned about the copying. Here
stevenjb
2012/10/25 00:41:58
I am concerned about copying, we do this fairly of
|
| + // base class pointers. |
| + switch(type) { |
| + case ManagedState::MANAGED_TYPE_NETWORK: |
| + return reinterpret_cast<ManagedStateList*>(&network_list_); |
|
pneubeck (no reviews)
2012/10/24 14:41:42
Is it worth to include such an unsafe cast for thi
stevenjb
2012/10/25 00:41:58
We are downcasting the pointers here, so it's not
|
| + case ManagedState::MANAGED_TYPE_DEVICE: |
| + return reinterpret_cast<ManagedStateList*>(&device_list_); |
| + } |
| + return NULL; |
| +} |
| + |
| +void NetworkStateManager::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); |
| + if (path.empty()) |
| + continue; |
| + std::map<std::string, ManagedState*>::iterator found = |
| + managed_map.find(path); |
| + bool request_properties = false; |
| + if (found == managed_map.end()) { |
| + request_properties = true; |
| + managed_list->push_back(ManagedState::Create(type, path)); |
| + } else { |
| + ManagedState* managed = found->second; |
| + managed_list->push_back(managed); |
| + managed_map.erase(found); |
| + } |
| + if (request_properties) { |
| + ++pending_updates_[type]; |
| + RequestProperties(type, path); |
| + } |
| + } |
| + // Delete any remaning entries in managed_map. |
| + STLDeleteContainerPairSecondPointers(managed_map.begin(), managed_map.end()); |
| +} |
| + |
| +void NetworkStateManager::UpdateObservedNetworkServices() { |
| + // Watch any connected or connecting networks. |
| + NetworkServiceObservers new_observed; |
| + for (NetworkStateList::const_iterator iter = network_list_.begin(); |
| + iter != network_list_.end(); ++iter) { |
| + const NetworkState* network = *iter; |
| + if (!network->IsConnectedState() && !network->IsConnectingState()) |
| + break; // Connected and connecting networks are listed first. |
|
pneubeck (no reviews)
2012/10/24 14:41:42
Hm. Are you sure that the NetworkState of disconne
stevenjb
2012/10/25 00:41:58
Will comment this better when updated in response
|
| + const std::string& path = network->path(); |
| + NetworkServiceObservers::iterator iter = observed_networks_.find(path); |
| + if (iter != observed_networks_.end()) { |
| + new_observed[path] = observed_networks_[path]; |
| + } else { |
| + new_observed[path] = new NetworkServiceObserver( |
| + path, base::Bind(&NetworkStateManager::NetworkServicePropertyChanged, |
| + weak_ptr_factory_.GetWeakPtr())); |
| + } |
| + observed_networks_.erase(path); |
| + } |
| + 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); |
| + // Notify observers that the list of networks has changed. |
| + FOR_EACH_OBSERVER(Observer, observers_, NetworkListChanged(network_list_)); |
| + // Notify observers if the active network has changed. |
| + const NetworkState* new_active_network = |
| + network_list_.empty() ? NULL : network_list_.front(); |
| + if (new_active_network->path() != active_network_path_) { |
| + active_network_path_ = new_active_network->path(); |
| + FOR_EACH_OBSERVER(Observer, observers_, |
| + ActiveNetworkChanged(new_active_network)); |
| + } |
| +} |
| + |
| +void NetworkStateManager::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); |
| + if (technology.empty()) |
| + continue; |
| + available_technologies_.insert(technology); |
| + } |
| +} |
| + |
| +void NetworkStateManager::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); |
| + if (technology.empty()) |
| + continue; |
| + enabled_technologies_.insert(technology); |
| + } |
| +} |
| + |
| +void NetworkStateManager::RequestProperties(ManagedState::ManagedType type, |
| + const std::string& path) { |
| + switch(type) { |
| + case ManagedState::MANAGED_TYPE_NETWORK: |
| + DBusThreadManager::Get()->GetShillServiceClient()->GetProperties( |
| + dbus::ObjectPath(path), |
| + base::Bind(&NetworkStateManager::GetPropertiesCallback, |
| + weak_ptr_factory_.GetWeakPtr(), |
| + type, path)); |
| + break; |
| + case ManagedState::MANAGED_TYPE_DEVICE: |
| + DBusThreadManager::Get()->GetShillDeviceClient()->GetProperties( |
| + dbus::ObjectPath(path), |
| + base::Bind(&NetworkStateManager::GetPropertiesCallback, |
| + weak_ptr_factory_.GetWeakPtr(), |
| + type, path)); |
| + break; |
| + } |
| +} |
| + |
| +void NetworkStateManager::GetPropertiesCallback( |
| + ManagedState::ManagedType type, |
| + const std::string& path, |
| + DBusMethodCallStatus call_status, |
| + const base::DictionaryValue& properties) { |
| + VLOG(2) << "GetPropertiesCallback: " << type << " : " << path; |
|
Greg Spencer (Chromium)
2012/10/23 23:19:10
I wonder if there should be a VLOG(2) section here
stevenjb
2012/10/25 00:41:58
My pattern is to add VLOG(2) as I need it and not
|
| + --pending_updates_[type]; |
| + if (call_status != DBUS_METHOD_CALL_SUCCESS) { |
| + LOG(ERROR) << "Failed to get properties for: " << path |
| + << ": " << call_status; |
| + return; |
| + } |
| + bool found = false; |
| + ManagedStateList* list = GetManagedList(type); |
| + for (ManagedStateList::iterator iter = list->begin(); |
| + iter != list->end(); ++iter) { |
| + ManagedState* managed = *iter; |
| + if (managed->path() == path) { |
| + ParseProperties(managed, properties); |
| + found = true; |
| + break; |
| + } |
| + } |
| + if (!found) |
| + LOG(ERROR) << "GetPropertiesCallback: " << path << " Not found!"; |
| + // Notify observers only when all updates for that type have completed. |
| + if (type == ManagedState::MANAGED_TYPE_NETWORK && |
| + pending_updates_[type] == 0) { |
| + UpdateObservedNetworkServices(); |
| + } |
| +} |
| + |
| +void NetworkStateManager::ParseProperties( |
| + ManagedState* managed, |
| + const base::DictionaryValue& properties) { |
| + for (base::DictionaryValue::key_iterator iter = properties.begin_keys(); |
| + iter != properties.end_keys(); ++iter) { |
| + bool success = false; |
| + const std::string& key = *iter; |
| + // Handle IPConfig here since it requires additional complexity that is |
| + // better handled here than in NetworkService::PropertyChanged (e.g. |
| + // observer notification). |
| + if (key == shill::kIPConfigProperty) { |
| + DCHECK(managed->managed_type() == ManagedState::MANAGED_TYPE_NETWORK); |
| + std::string ip_config_path; |
| + if (properties.GetStringWithoutPathExpansion(key, &ip_config_path)) { |
| + DBusThreadManager::Get()->GetShillIPConfigClient()->GetProperties( |
| + dbus::ObjectPath(ip_config_path), |
| + base::Bind(&NetworkStateManager::GetIPConfigCallback, |
| + weak_ptr_factory_.GetWeakPtr(), |
| + managed->path())); |
| + success = true; |
| + } |
| + } else { |
| + const base::Value* value; |
| + if (properties.GetWithoutPathExpansion(key, &value)) |
| + success = managed->PropertyChanged(key, *value); |
| + } |
| + if (!success) |
| + LOG(ERROR) << "Error getting value for key: " << key; |
| + } |
| +} |
| + |
| +void NetworkStateManager::NetworkServicePropertyChanged( |
| + const std::string& path, |
| + const std::string& key, |
| + const base::Value& value) { |
| + NetworkState* network = GetModifiableNetworkState(path); |
| + if (!network) |
| + return; |
| + if (network->PropertyChanged(key, value)) { |
| + FOR_EACH_OBSERVER( |
| + Observer, observers_, NetworkServicePropertyChanged(network, key)); |
| + if (network == network_list_.front() && key == flimflam::kStateProperty) { |
| + FOR_EACH_OBSERVER( |
| + Observer, observers_, ActiveNetworkStateChanged(network)); |
| + } |
| + } |
| +} |
| + |
| +void NetworkStateManager::GetIPConfigCallback( |
| + const std::string& path, |
| + DBusMethodCallStatus call_status, |
| + const base::DictionaryValue& properties) { |
| + if (call_status != DBUS_METHOD_CALL_SUCCESS) { |
| + LOG(ERROR) << "Failed to get IP properties for: " << path; |
| + return; |
| + } |
| + bool success = false; |
| + for (base::DictionaryValue::key_iterator iter = properties.begin_keys(); |
| + iter != properties.end_keys(); ++iter) { |
| + const std::string& key = *iter; |
| + if (key == flimflam::kAddressProperty) { |
| + std::string ip_config; |
| + properties.GetStringWithoutPathExpansion(key, &ip_config); |
| + NetworkState* network = GetModifiableNetworkState(path); |
| + if (network) { |
| + network->set_ip_config(ip_config); |
| + success = true; |
| + } |
| + break; |
| + } |
| + } |
| + if (success) { |
| + FOR_EACH_OBSERVER( |
| + Observer, observers_, NetworkManagerChanged(shill::kIPConfigProperty)); |
| + } |
| +} |
| + |
| +} // namespace chromeos |