| // 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 <stddef.h> | 
 |  | 
 | #include "base/bind.h" | 
 | #include "base/format_macros.h" | 
 | #include "base/guid.h" | 
 | #include "base/json/json_string_value_serializer.h" | 
 | #include "base/json/json_writer.h" | 
 | #include "base/location.h" | 
 | #include "base/logging.h" | 
 | #include "base/metrics/histogram.h" | 
 | #include "base/stl_util.h" | 
 | #include "base/strings/string_util.h" | 
 | #include "base/strings/stringprintf.h" | 
 | #include "base/values.h" | 
 | #include "chromeos/network/device_state.h" | 
 | #include "chromeos/network/network_event_log.h" | 
 | #include "chromeos/network/network_state.h" | 
 | #include "chromeos/network/network_state_handler_observer.h" | 
 | #include "third_party/cros_system_api/dbus/service_constants.h" | 
 |  | 
 | namespace chromeos { | 
 |  | 
 | namespace { | 
 |  | 
 | bool ConnectionStateChanged(NetworkState* network, | 
 |                             const std::string& prev_connection_state, | 
 |                             bool prev_is_captive_portal) { | 
 |   return ((network->connection_state() != prev_connection_state) && | 
 |           !((network->connection_state() == shill::kStateIdle) && | 
 |             prev_connection_state.empty())) || | 
 |          (network->is_captive_portal() != prev_is_captive_portal); | 
 | } | 
 |  | 
 | std::string GetManagedStateLogType(const ManagedState* state) { | 
 |   switch (state->managed_type()) { | 
 |     case ManagedState::MANAGED_TYPE_NETWORK: | 
 |       return "Network"; | 
 |     case ManagedState::MANAGED_TYPE_DEVICE: | 
 |       return "Device"; | 
 |   } | 
 |   NOTREACHED(); | 
 |   return ""; | 
 | } | 
 |  | 
 | std::string GetLogName(const ManagedState* state) { | 
 |   if (!state) | 
 |     return "None"; | 
 |   return base::StringPrintf("%s (%s)", state->name().c_str(), | 
 |                             state->path().c_str()); | 
 | } | 
 |  | 
 | std::string ValueAsString(const base::Value& value) { | 
 |   std::string vstr; | 
 |   base::JSONWriter::WriteWithOptions( | 
 |       value, base::JSONWriter::OPTIONS_OMIT_BINARY_VALUES, &vstr); | 
 |   return vstr.empty() ? "''" : vstr; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | const char NetworkStateHandler::kDefaultCheckPortalList[] = | 
 |     "ethernet,wifi,cellular"; | 
 |  | 
 | NetworkStateHandler::NetworkStateHandler() {} | 
 |  | 
 | NetworkStateHandler::~NetworkStateHandler() { | 
 |   // Normally Shutdown() will get called in ~NetworkHandler, however unit | 
 |   // tests do not use that class so this needs to call Shutdown when we | 
 |   // destry the class. | 
 |   if (!did_shutdown_) | 
 |     Shutdown(); | 
 |   STLDeleteContainerPointers(network_list_.begin(), network_list_.end()); | 
 |   STLDeleteContainerPointers(device_list_.begin(), device_list_.end()); | 
 | } | 
 |  | 
 | void NetworkStateHandler::Shutdown() { | 
 |   DCHECK(!did_shutdown_); | 
 |   did_shutdown_ = true; | 
 |   FOR_EACH_OBSERVER(NetworkStateHandlerObserver, observers_, OnShuttingDown()); | 
 | } | 
 |  | 
 | void NetworkStateHandler::InitShillPropertyHandler() { | 
 |   shill_property_handler_.reset(new internal::ShillPropertyHandler(this)); | 
 |   shill_property_handler_->Init(); | 
 | } | 
 |  | 
 | // static | 
 | NetworkStateHandler* NetworkStateHandler::InitializeForTest() { | 
 |   NetworkStateHandler* handler = new NetworkStateHandler(); | 
 |   handler->InitShillPropertyHandler(); | 
 |   return handler; | 
 | } | 
 |  | 
 | void NetworkStateHandler::AddObserver( | 
 |     NetworkStateHandlerObserver* observer, | 
 |     const tracked_objects::Location& from_here) { | 
 |   observers_.AddObserver(observer); | 
 |   device_event_log::AddEntry( | 
 |       from_here.file_name(), from_here.line_number(), | 
 |       device_event_log::LOG_TYPE_NETWORK, device_event_log::LOG_LEVEL_DEBUG, | 
 |       base::StringPrintf("NetworkStateHandler::AddObserver: 0x%p", observer)); | 
 | } | 
 |  | 
 | void NetworkStateHandler::RemoveObserver( | 
 |     NetworkStateHandlerObserver* observer, | 
 |     const tracked_objects::Location& from_here) { | 
 |   observers_.RemoveObserver(observer); | 
 |   device_event_log::AddEntry( | 
 |       from_here.file_name(), from_here.line_number(), | 
 |       device_event_log::LOG_TYPE_NETWORK, device_event_log::LOG_LEVEL_DEBUG, | 
 |       base::StringPrintf("NetworkStateHandler::RemoveObserver: 0x%p", | 
 |                          observer)); | 
 | } | 
 |  | 
 | NetworkStateHandler::TechnologyState NetworkStateHandler::GetTechnologyState( | 
 |     const NetworkTypePattern& type) const { | 
 |   std::string technology = GetTechnologyForType(type); | 
 |   TechnologyState state; | 
 |   if (shill_property_handler_->IsTechnologyEnabled(technology)) | 
 |     state = TECHNOLOGY_ENABLED; | 
 |   else if (shill_property_handler_->IsTechnologyEnabling(technology)) | 
 |     state = TECHNOLOGY_ENABLING; | 
 |   else if (shill_property_handler_->IsTechnologyProhibited(technology)) | 
 |     state = TECHNOLOGY_PROHIBITED; | 
 |   else if (shill_property_handler_->IsTechnologyUninitialized(technology)) | 
 |     state = TECHNOLOGY_UNINITIALIZED; | 
 |   else if (shill_property_handler_->IsTechnologyAvailable(technology)) | 
 |     state = TECHNOLOGY_AVAILABLE; | 
 |   else | 
 |     state = TECHNOLOGY_UNAVAILABLE; | 
 |   VLOG(2) << "GetTechnologyState: " << type.ToDebugString() << " = " << state; | 
 |   return state; | 
 | } | 
 |  | 
 | void NetworkStateHandler::SetTechnologyEnabled( | 
 |     const NetworkTypePattern& type, | 
 |     bool enabled, | 
 |     const network_handler::ErrorCallback& error_callback) { | 
 |   ScopedVector<std::string> technologies = GetTechnologiesForType(type); | 
 |   for (ScopedVector<std::string>::iterator it = technologies.begin(); | 
 |        it != technologies.end(); ++it) { | 
 |     std::string* technology = *it; | 
 |     DCHECK(technology); | 
 |     if (!shill_property_handler_->IsTechnologyAvailable(*technology)) | 
 |       continue; | 
 |     NET_LOG_USER("SetTechnologyEnabled", | 
 |                  base::StringPrintf("%s:%d", technology->c_str(), enabled)); | 
 |     shill_property_handler_->SetTechnologyEnabled(*technology, enabled, | 
 |                                                   error_callback); | 
 |   } | 
 |   // Signal Device/Technology state changed. | 
 |   NotifyDeviceListChanged(); | 
 | } | 
 |  | 
 | void NetworkStateHandler::SetProhibitedTechnologies( | 
 |     const std::vector<std::string>& prohibited_technologies, | 
 |     const network_handler::ErrorCallback& error_callback) { | 
 |   shill_property_handler_->SetProhibitedTechnologies(prohibited_technologies, | 
 |                                                      error_callback); | 
 |   // Signal Device/Technology state changed. | 
 |   NotifyDeviceListChanged(); | 
 | } | 
 |  | 
 | const DeviceState* NetworkStateHandler::GetDeviceState( | 
 |     const std::string& device_path) const { | 
 |   const DeviceState* device = GetModifiableDeviceState(device_path); | 
 |   if (device && !device->update_received()) | 
 |     return nullptr; | 
 |   return device; | 
 | } | 
 |  | 
 | const DeviceState* NetworkStateHandler::GetDeviceStateByType( | 
 |     const NetworkTypePattern& type) const { | 
 |   for (ManagedStateList::const_iterator iter = device_list_.begin(); | 
 |        iter != device_list_.end(); ++iter) { | 
 |     ManagedState* device = *iter; | 
 |     if (!device->update_received()) | 
 |       continue; | 
 |     if (device->Matches(type)) | 
 |       return device->AsDeviceState(); | 
 |   } | 
 |   return nullptr; | 
 | } | 
 |  | 
 | bool NetworkStateHandler::GetScanningByType( | 
 |     const NetworkTypePattern& type) const { | 
 |   for (ManagedStateList::const_iterator iter = device_list_.begin(); | 
 |        iter != device_list_.end(); ++iter) { | 
 |     const DeviceState* device = (*iter)->AsDeviceState(); | 
 |     DCHECK(device); | 
 |     if (!device->update_received()) | 
 |       continue; | 
 |     if (device->Matches(type) && device->scanning()) | 
 |       return true; | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | const NetworkState* NetworkStateHandler::GetNetworkState( | 
 |     const std::string& service_path) const { | 
 |   const NetworkState* network = GetModifiableNetworkState(service_path); | 
 |   if (network && !network->update_received()) | 
 |     return nullptr; | 
 |   return network; | 
 | } | 
 |  | 
 | const NetworkState* NetworkStateHandler::DefaultNetwork() const { | 
 |   if (default_network_path_.empty()) | 
 |     return nullptr; | 
 |   return GetNetworkState(default_network_path_); | 
 | } | 
 |  | 
 | const NetworkState* NetworkStateHandler::ConnectedNetworkByType( | 
 |     const NetworkTypePattern& type) const { | 
 |   // Active networks are always listed first by Shill so no need to sort. | 
 |   for (ManagedStateList::const_iterator iter = network_list_.begin(); | 
 |        iter != network_list_.end(); ++iter) { | 
 |     const NetworkState* network = (*iter)->AsNetworkState(); | 
 |     DCHECK(network); | 
 |     if (!network->update_received()) | 
 |       continue; | 
 |     if (!network->IsConnectedState()) | 
 |       break;  // Connected networks are listed first. | 
 |     if (network->Matches(type)) | 
 |       return network; | 
 |   } | 
 |   return nullptr; | 
 | } | 
 |  | 
 | const NetworkState* NetworkStateHandler::ConnectingNetworkByType( | 
 |     const NetworkTypePattern& type) const { | 
 |   // Active networks are always listed first by Shill so no need to sort. | 
 |   for (ManagedStateList::const_iterator iter = network_list_.begin(); | 
 |        iter != network_list_.end(); ++iter) { | 
 |     const NetworkState* network = (*iter)->AsNetworkState(); | 
 |     DCHECK(network); | 
 |     if (!network->update_received() || network->IsConnectedState()) | 
 |       continue; | 
 |     if (!network->IsConnectingState()) | 
 |       break;  // Connected and connecting networks are listed first. | 
 |     if (network->Matches(type)) | 
 |       return network; | 
 |   } | 
 |   return nullptr; | 
 | } | 
 |  | 
 | const NetworkState* NetworkStateHandler::FirstNetworkByType( | 
 |     const NetworkTypePattern& type) { | 
 |   if (!network_list_sorted_) | 
 |     SortNetworkList();  // Sort to ensure visible networks are listed first. | 
 |   for (ManagedStateList::const_iterator iter = network_list_.begin(); | 
 |        iter != network_list_.end(); ++iter) { | 
 |     const NetworkState* network = (*iter)->AsNetworkState(); | 
 |     DCHECK(network); | 
 |     if (!network->update_received()) | 
 |       continue; | 
 |     if (!network->visible()) | 
 |       break; | 
 |     if (network->Matches(type)) | 
 |       return network; | 
 |   } | 
 |   return nullptr; | 
 | } | 
 |  | 
 | std::string NetworkStateHandler::FormattedHardwareAddressForType( | 
 |     const NetworkTypePattern& type) const { | 
 |   const DeviceState* device = nullptr; | 
 |   const NetworkState* network = ConnectedNetworkByType(type); | 
 |   if (network) | 
 |     device = GetDeviceState(network->device_path()); | 
 |   else | 
 |     device = GetDeviceStateByType(type); | 
 |   if (!device) | 
 |     return std::string(); | 
 |   return network_util::FormattedMacAddress(device->mac_address()); | 
 | } | 
 |  | 
 | void NetworkStateHandler::GetVisibleNetworkListByType( | 
 |     const NetworkTypePattern& type, | 
 |     NetworkStateList* list) { | 
 |   GetNetworkListByType(type, false /* configured_only */, | 
 |                        true /* visible_only */, 0 /* no limit */, list); | 
 | } | 
 |  | 
 | void NetworkStateHandler::GetVisibleNetworkList(NetworkStateList* list) { | 
 |   GetVisibleNetworkListByType(NetworkTypePattern::Default(), list); | 
 | } | 
 |  | 
 | void NetworkStateHandler::GetNetworkListByType(const NetworkTypePattern& type, | 
 |                                                bool configured_only, | 
 |                                                bool visible_only, | 
 |                                                int limit, | 
 |                                                NetworkStateList* list) { | 
 |   DCHECK(list); | 
 |   list->clear(); | 
 |   int count = 0; | 
 |   // Sort the network list if necessary. | 
 |   if (!network_list_sorted_) | 
 |     SortNetworkList(); | 
 |   for (ManagedStateList::const_iterator iter = network_list_.begin(); | 
 |        iter != network_list_.end(); ++iter) { | 
 |     const NetworkState* network = (*iter)->AsNetworkState(); | 
 |     DCHECK(network); | 
 |     if (!network->update_received() || !network->Matches(type)) | 
 |       continue; | 
 |     if (configured_only && !network->IsInProfile()) | 
 |       continue; | 
 |     if (visible_only && !network->visible()) | 
 |       continue; | 
 |     list->push_back(network); | 
 |     if (limit > 0 && ++count >= limit) | 
 |       break; | 
 |   } | 
 | } | 
 |  | 
 | const NetworkState* NetworkStateHandler::GetNetworkStateFromServicePath( | 
 |     const std::string& service_path, | 
 |     bool configured_only) const { | 
 |   ManagedState* managed = | 
 |       GetModifiableManagedState(&network_list_, service_path); | 
 |   if (!managed) | 
 |     return nullptr; | 
 |   const NetworkState* network = managed->AsNetworkState(); | 
 |   DCHECK(network); | 
 |   if (!network->update_received() || | 
 |       (configured_only && !network->IsInProfile())) { | 
 |     return nullptr; | 
 |   } | 
 |   return network; | 
 | } | 
 |  | 
 | const NetworkState* NetworkStateHandler::GetNetworkStateFromGuid( | 
 |     const std::string& guid) const { | 
 |   DCHECK(!guid.empty()); | 
 |   for (ManagedStateList::const_iterator iter = network_list_.begin(); | 
 |        iter != network_list_.end(); ++iter) { | 
 |     const NetworkState* network = (*iter)->AsNetworkState(); | 
 |     if (network->guid() == guid) | 
 |       return network; | 
 |   } | 
 |   return nullptr; | 
 | } | 
 |  | 
 | void NetworkStateHandler::GetDeviceList(DeviceStateList* list) const { | 
 |   GetDeviceListByType(NetworkTypePattern::Default(), list); | 
 | } | 
 |  | 
 | void NetworkStateHandler::GetDeviceListByType(const NetworkTypePattern& type, | 
 |                                               DeviceStateList* list) const { | 
 |   DCHECK(list); | 
 |   list->clear(); | 
 |   for (ManagedStateList::const_iterator iter = device_list_.begin(); | 
 |        iter != device_list_.end(); ++iter) { | 
 |     const DeviceState* device = (*iter)->AsDeviceState(); | 
 |     DCHECK(device); | 
 |     if (device->update_received() && device->Matches(type)) | 
 |       list->push_back(device); | 
 |   } | 
 | } | 
 |  | 
 | void NetworkStateHandler::RequestScan() const { | 
 |   NET_LOG_USER("RequestScan", ""); | 
 |   shill_property_handler_->RequestScan(); | 
 | } | 
 |  | 
 | void NetworkStateHandler::RequestUpdateForNetwork( | 
 |     const std::string& service_path) { | 
 |   NetworkState* network = GetModifiableNetworkState(service_path); | 
 |   if (network) | 
 |     network->set_update_requested(true); | 
 |   NET_LOG_EVENT("RequestUpdate", service_path); | 
 |   shill_property_handler_->RequestProperties(ManagedState::MANAGED_TYPE_NETWORK, | 
 |                                              service_path); | 
 | } | 
 |  | 
 | void NetworkStateHandler::ClearLastErrorForNetwork( | 
 |     const std::string& service_path) { | 
 |   NetworkState* network = GetModifiableNetworkState(service_path); | 
 |   if (network) | 
 |     network->clear_last_error(); | 
 | } | 
 |  | 
 | void NetworkStateHandler::SetCheckPortalList( | 
 |     const std::string& check_portal_list) { | 
 |   NET_LOG_EVENT("SetCheckPortalList", check_portal_list); | 
 |   shill_property_handler_->SetCheckPortalList(check_portal_list); | 
 | } | 
 |  | 
 | void NetworkStateHandler::SetWakeOnLanEnabled(bool enabled) { | 
 |   NET_LOG_EVENT("SetWakeOnLanEnabled", enabled ? "true" : "false"); | 
 |   shill_property_handler_->SetWakeOnLanEnabled(enabled); | 
 | } | 
 |  | 
 | const NetworkState* NetworkStateHandler::GetEAPForEthernet( | 
 |     const std::string& service_path) { | 
 |   const NetworkState* network = GetNetworkState(service_path); | 
 |   if (!network) { | 
 |     NET_LOG_ERROR("GetEAPForEthernet", "Unknown service path " + service_path); | 
 |     return nullptr; | 
 |   } | 
 |   if (network->type() != shill::kTypeEthernet) { | 
 |     NET_LOG_ERROR("GetEAPForEthernet", "Not of type Ethernet: " + service_path); | 
 |     return nullptr; | 
 |   } | 
 |   if (!network->IsConnectedState()) | 
 |     return nullptr; | 
 |  | 
 |   // The same EAP service is shared for all ethernet services/devices. | 
 |   // However EAP is used/enabled per device and only if the connection was | 
 |   // successfully established. | 
 |   const DeviceState* device = GetDeviceState(network->device_path()); | 
 |   if (!device) { | 
 |     NET_LOG(ERROR) << "GetEAPForEthernet: Unknown device " | 
 |                    << network->device_path() | 
 |                    << " for connected ethernet service: " << service_path; | 
 |     return nullptr; | 
 |   } | 
 |   if (!device->eap_authentication_completed()) | 
 |     return nullptr; | 
 |  | 
 |   NetworkStateList list; | 
 |   GetNetworkListByType(NetworkTypePattern::Primitive(shill::kTypeEthernetEap), | 
 |                        true /* configured_only */, false /* visible_only */, | 
 |                        1 /* limit */, &list); | 
 |   if (list.empty()) { | 
 |     NET_LOG_ERROR("GetEAPForEthernet", | 
 |                   base::StringPrintf( | 
 |                       "Ethernet service %s connected using EAP, but no " | 
 |                       "EAP service found.", | 
 |                       service_path.c_str())); | 
 |     return nullptr; | 
 |   } | 
 |   return list.front(); | 
 | } | 
 |  | 
 | void NetworkStateHandler::SetLastErrorForTest(const std::string& service_path, | 
 |                                               const std::string& error) { | 
 |   NetworkState* network_state = GetModifiableNetworkState(service_path); | 
 |   if (!network_state) { | 
 |     LOG(ERROR) << "No matching NetworkState for: " << service_path; | 
 |     return; | 
 |   } | 
 |   network_state->last_error_ = error; | 
 | } | 
 |  | 
 | //------------------------------------------------------------------------------ | 
 | // ShillPropertyHandler::Delegate overrides | 
 |  | 
 | void NetworkStateHandler::UpdateManagedList(ManagedState::ManagedType type, | 
 |                                             const base::ListValue& entries) { | 
 |   ManagedStateList* managed_list = GetManagedList(type); | 
 |   NET_LOG_DEBUG("UpdateManagedList: " + ManagedState::TypeToString(type), | 
 |                 base::StringPrintf("%" PRIuS, entries.GetSize())); | 
 |   // Create a map of existing entries. Assumes all entries in |managed_list| | 
 |   // are unique. | 
 |   typedef std::map<std::string, ManagedState*> ManagedMap; | 
 |   ManagedMap managed_map; | 
 |   for (ManagedStateList::iterator iter = managed_list->begin(); | 
 |        iter != managed_list->end(); ++iter) { | 
 |     ManagedState* managed = *iter; | 
 |     DCHECK(!ContainsKey(managed_map, managed->path())); | 
 |     managed_map[managed->path()] = managed; | 
 |   } | 
 |   // Clear the list (pointers are temporarily owned by managed_map). | 
 |   managed_list->clear(); | 
 |   // Updates managed_list and request updates for new entries. | 
 |   std::set<std::string> list_entries; | 
 |   for (base::ListValue::const_iterator iter = entries.begin(); | 
 |        iter != entries.end(); ++iter) { | 
 |     std::string path; | 
 |     (*iter)->GetAsString(&path); | 
 |     if (path.empty() || path == shill::kFlimflamServicePath) { | 
 |       NET_LOG_ERROR(base::StringPrintf("Bad path in list:%d", type), path); | 
 |       continue; | 
 |     } | 
 |     ManagedMap::iterator found = managed_map.find(path); | 
 |     if (found == managed_map.end()) { | 
 |       if (list_entries.count(path) != 0) { | 
 |         NET_LOG_ERROR("Duplicate entry in list", path); | 
 |         continue; | 
 |       } | 
 |       ManagedState* managed = ManagedState::Create(type, path); | 
 |       managed_list->push_back(managed); | 
 |     } else { | 
 |       managed_list->push_back(found->second); | 
 |       managed_map.erase(found); | 
 |     } | 
 |     list_entries.insert(path); | 
 |   } | 
 |   // Delete any remaining entries in managed_map. | 
 |   STLDeleteContainerPairSecondPointers(managed_map.begin(), managed_map.end()); | 
 | } | 
 |  | 
 | void NetworkStateHandler::ProfileListChanged() { | 
 |   NET_LOG_EVENT("ProfileListChanged", "Re-Requesting Network Properties"); | 
 |   for (ManagedStateList::iterator iter = network_list_.begin(); | 
 |        iter != network_list_.end(); ++iter) { | 
 |     NetworkState* network = (*iter)->AsNetworkState(); | 
 |     DCHECK(network); | 
 |     shill_property_handler_->RequestProperties( | 
 |         ManagedState::MANAGED_TYPE_NETWORK, network->path()); | 
 |   } | 
 | } | 
 |  | 
 | void NetworkStateHandler::UpdateManagedStateProperties( | 
 |     ManagedState::ManagedType type, | 
 |     const std::string& path, | 
 |     const base::DictionaryValue& properties) { | 
 |   ManagedStateList* managed_list = GetManagedList(type); | 
 |   ManagedState* managed = GetModifiableManagedState(managed_list, path); | 
 |   if (!managed) { | 
 |     // The network has been removed from the list of networks. | 
 |     NET_LOG_DEBUG("UpdateManagedStateProperties: Not found", path); | 
 |     return; | 
 |   } | 
 |   managed->set_update_received(); | 
 |  | 
 |   std::string desc = GetManagedStateLogType(managed) + " Properties Received"; | 
 |   NET_LOG_DEBUG(desc, GetLogName(managed)); | 
 |  | 
 |   if (type == ManagedState::MANAGED_TYPE_NETWORK) { | 
 |     UpdateNetworkStateProperties(managed->AsNetworkState(), properties); | 
 |   } else { | 
 |     // Device | 
 |     for (base::DictionaryValue::Iterator iter(properties); !iter.IsAtEnd(); | 
 |          iter.Advance()) { | 
 |       managed->PropertyChanged(iter.key(), iter.value()); | 
 |     } | 
 |     managed->InitialPropertiesReceived(properties); | 
 |   } | 
 |   managed->set_update_requested(false); | 
 | } | 
 |  | 
 | void NetworkStateHandler::UpdateNetworkStateProperties( | 
 |     NetworkState* network, | 
 |     const base::DictionaryValue& properties) { | 
 |   DCHECK(network); | 
 |   bool network_property_updated = false; | 
 |   std::string prev_connection_state = network->connection_state(); | 
 |   bool prev_is_captive_portal = network->is_captive_portal(); | 
 |   for (base::DictionaryValue::Iterator iter(properties); !iter.IsAtEnd(); | 
 |        iter.Advance()) { | 
 |     if (network->PropertyChanged(iter.key(), iter.value())) | 
 |       network_property_updated = true; | 
 |   } | 
 |   network_property_updated |= network->InitialPropertiesReceived(properties); | 
 |   UpdateGuid(network); | 
 |   network_list_sorted_ = false; | 
 |  | 
 |   // Notify observers of NetworkState changes. | 
 |   if (network_property_updated || network->update_requested()) { | 
 |     // Signal connection state changed after all properties have been updated. | 
 |     if (ConnectionStateChanged(network, prev_connection_state, | 
 |                                prev_is_captive_portal)) { | 
 |       OnNetworkConnectionStateChanged(network); | 
 |     } | 
 |     NET_LOG_EVENT("NetworkPropertiesUpdated", GetLogName(network)); | 
 |     NotifyNetworkPropertiesUpdated(network); | 
 |   } | 
 | } | 
 |  | 
 | void NetworkStateHandler::UpdateNetworkServiceProperty( | 
 |     const std::string& service_path, | 
 |     const std::string& key, | 
 |     const base::Value& value) { | 
 |   SCOPED_NET_LOG_IF_SLOW(); | 
 |   bool changed = false; | 
 |   NetworkState* network = GetModifiableNetworkState(service_path); | 
 |   if (!network) | 
 |     return; | 
 |   std::string prev_connection_state = network->connection_state(); | 
 |   bool prev_is_captive_portal = network->is_captive_portal(); | 
 |   std::string prev_profile_path = network->profile_path(); | 
 |   changed |= network->PropertyChanged(key, value); | 
 |   if (!changed) | 
 |     return; | 
 |  | 
 |   if (key == shill::kStateProperty || key == shill::kVisibleProperty) { | 
 |     network_list_sorted_ = false; | 
 |     if (ConnectionStateChanged(network, prev_connection_state, | 
 |                                prev_is_captive_portal)) { | 
 |       OnNetworkConnectionStateChanged(network); | 
 |       // If the connection state changes, other properties such as IPConfig | 
 |       // may have changed, so request a full update. | 
 |       RequestUpdateForNetwork(service_path); | 
 |     } | 
 |   } else { | 
 |     std::string value_str; | 
 |     value.GetAsString(&value_str); | 
 |     // Some property changes are noisy and not interesting: | 
 |     // * Wifi SignalStrength | 
 |     // * WifiFrequencyList updates | 
 |     // * Device property changes to "/" (occurs before a service is removed) | 
 |     if (key != shill::kSignalStrengthProperty && | 
 |         key != shill::kWifiFrequencyListProperty && | 
 |         (key != shill::kDeviceProperty || value_str != "/")) { | 
 |       std::string log_event = "NetworkPropertyUpdated"; | 
 |       // Trigger a default network update for interesting changes only. | 
 |       if (network->path() == default_network_path_) { | 
 |         NotifyDefaultNetworkChanged(network); | 
 |         log_event = "Default" + log_event; | 
 |       } | 
 |       // Log event. | 
 |       std::string detail = network->name() + "." + key; | 
 |       detail += " = " + ValueAsString(value); | 
 |       device_event_log::LogLevel log_level; | 
 |       if (key == shill::kErrorProperty || key == shill::kErrorDetailsProperty) { | 
 |         log_level = device_event_log::LOG_LEVEL_ERROR; | 
 |       } else { | 
 |         log_level = device_event_log::LOG_LEVEL_EVENT; | 
 |       } | 
 |       NET_LOG_LEVEL(log_level, log_event, detail); | 
 |     } | 
 |   } | 
 |  | 
 |   // All property updates signal 'NetworkPropertiesUpdated'. | 
 |   NotifyNetworkPropertiesUpdated(network); | 
 |  | 
 |   // If added to a Profile, request a full update so that a NetworkState | 
 |   // gets created. | 
 |   if (prev_profile_path.empty() && !network->profile_path().empty()) | 
 |     RequestUpdateForNetwork(service_path); | 
 | } | 
 |  | 
 | void NetworkStateHandler::UpdateDeviceProperty(const std::string& device_path, | 
 |                                                const std::string& key, | 
 |                                                const base::Value& value) { | 
 |   SCOPED_NET_LOG_IF_SLOW(); | 
 |   DeviceState* device = GetModifiableDeviceState(device_path); | 
 |   if (!device) | 
 |     return; | 
 |   if (!device->PropertyChanged(key, value)) | 
 |     return; | 
 |  | 
 |   std::string detail = device->name() + "." + key; | 
 |   detail += " = " + ValueAsString(value); | 
 |   NET_LOG_EVENT("DevicePropertyUpdated", detail); | 
 |  | 
 |   NotifyDeviceListChanged(); | 
 |   NotifyDevicePropertiesUpdated(device); | 
 |  | 
 |   if (key == shill::kScanningProperty && device->scanning() == false) { | 
 |     NotifyScanCompleted(device); | 
 |   } | 
 |   if (key == shill::kEapAuthenticationCompletedProperty) { | 
 |     // Notify a change for each Ethernet service using this device. | 
 |     NetworkStateList ethernet_services; | 
 |     GetNetworkListByType(NetworkTypePattern::Ethernet(), | 
 |                          false /* configured_only */, false /* visible_only */, | 
 |                          0 /* no limit */, ðernet_services); | 
 |     for (NetworkStateList::const_iterator it = ethernet_services.begin(); | 
 |          it != ethernet_services.end(); ++it) { | 
 |       const NetworkState* ethernet_service = *it; | 
 |       if (ethernet_service->update_received() || | 
 |           ethernet_service->device_path() != device->path()) { | 
 |         continue; | 
 |       } | 
 |       RequestUpdateForNetwork(ethernet_service->path()); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void NetworkStateHandler::UpdateIPConfigProperties( | 
 |     ManagedState::ManagedType type, | 
 |     const std::string& path, | 
 |     const std::string& ip_config_path, | 
 |     const base::DictionaryValue& properties) { | 
 |   if (type == ManagedState::MANAGED_TYPE_NETWORK) { | 
 |     NetworkState* network = GetModifiableNetworkState(path); | 
 |     if (!network) | 
 |       return; | 
 |     network->IPConfigPropertiesChanged(properties); | 
 |     NotifyNetworkPropertiesUpdated(network); | 
 |     if (network->path() == default_network_path_) | 
 |       NotifyDefaultNetworkChanged(network); | 
 |   } else if (type == ManagedState::MANAGED_TYPE_DEVICE) { | 
 |     DeviceState* device = GetModifiableDeviceState(path); | 
 |     if (!device) | 
 |       return; | 
 |     device->IPConfigPropertiesChanged(ip_config_path, properties); | 
 |     NotifyDevicePropertiesUpdated(device); | 
 |     if (!default_network_path_.empty()) { | 
 |       const NetworkState* default_network = | 
 |           GetNetworkState(default_network_path_); | 
 |       if (default_network && default_network->device_path() == path) | 
 |         NotifyDefaultNetworkChanged(default_network); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void NetworkStateHandler::CheckPortalListChanged( | 
 |     const std::string& check_portal_list) { | 
 |   check_portal_list_ = check_portal_list; | 
 | } | 
 |  | 
 | void NetworkStateHandler::TechnologyListChanged() { | 
 |   // Eventually we would like to replace Technology state with Device state. | 
 |   // For now, treat technology state changes as device list changes. | 
 |   NotifyDeviceListChanged(); | 
 | } | 
 |  | 
 | void NetworkStateHandler::ManagedStateListChanged( | 
 |     ManagedState::ManagedType type) { | 
 |   SCOPED_NET_LOG_IF_SLOW(); | 
 |   if (type == ManagedState::MANAGED_TYPE_NETWORK) { | 
 |     SortNetworkList(); | 
 |     UpdateNetworkStats(); | 
 |     // Notify observers that the list of networks has changed. | 
 |     NET_LOG_EVENT("NOTIFY:NetworkListChanged", | 
 |                   base::StringPrintf("Size:%" PRIuS, network_list_.size())); | 
 |     FOR_EACH_OBSERVER(NetworkStateHandlerObserver, observers_, | 
 |                       NetworkListChanged()); | 
 |   } else if (type == ManagedState::MANAGED_TYPE_DEVICE) { | 
 |     std::string devices; | 
 |     for (ManagedStateList::const_iterator iter = device_list_.begin(); | 
 |          iter != device_list_.end(); ++iter) { | 
 |       if (iter != device_list_.begin()) | 
 |         devices += ", "; | 
 |       devices += (*iter)->name(); | 
 |     } | 
 |     NET_LOG_EVENT("DeviceList", devices); | 
 |     NotifyDeviceListChanged(); | 
 |   } else { | 
 |     NOTREACHED(); | 
 |   } | 
 | } | 
 |  | 
 | void NetworkStateHandler::SortNetworkList() { | 
 |   // Note: usually active networks will precede inactive networks, however | 
 |   // this may briefly be untrue during state transitions (e.g. a network may | 
 |   // transition to idle before the list is updated). | 
 |   ManagedStateList active, non_wifi_visible, wifi_visible, hidden, new_networks; | 
 |   for (ManagedStateList::iterator iter = network_list_.begin(); | 
 |        iter != network_list_.end(); ++iter) { | 
 |     NetworkState* network = (*iter)->AsNetworkState(); | 
 |     if (!network->update_received()) { | 
 |       new_networks.push_back(network); | 
 |       continue; | 
 |     } | 
 |     if (network->IsConnectedState() || network->IsConnectingState()) { | 
 |       active.push_back(network); | 
 |       continue; | 
 |     } | 
 |     if (network->visible()) { | 
 |       if (NetworkTypePattern::WiFi().MatchesType(network->type())) | 
 |         wifi_visible.push_back(network); | 
 |       else | 
 |         non_wifi_visible.push_back(network); | 
 |     } else { | 
 |       hidden.push_back(network); | 
 |     } | 
 |   } | 
 |   network_list_.clear(); | 
 |   network_list_.insert(network_list_.end(), active.begin(), active.end()); | 
 |   network_list_.insert(network_list_.end(), non_wifi_visible.begin(), | 
 |                        non_wifi_visible.end()); | 
 |   network_list_.insert(network_list_.end(), wifi_visible.begin(), | 
 |                        wifi_visible.end()); | 
 |   network_list_.insert(network_list_.end(), hidden.begin(), hidden.end()); | 
 |   network_list_.insert(network_list_.end(), new_networks.begin(), | 
 |                        new_networks.end()); | 
 |   network_list_sorted_ = true; | 
 | } | 
 |  | 
 | void NetworkStateHandler::UpdateNetworkStats() { | 
 |   size_t shared = 0, unshared = 0, visible = 0; | 
 |   for (ManagedStateList::iterator iter = network_list_.begin(); | 
 |        iter != network_list_.end(); ++iter) { | 
 |     NetworkState* network = (*iter)->AsNetworkState(); | 
 |     if (network->visible()) | 
 |       ++visible; | 
 |     if (network->IsInProfile()) { | 
 |       if (network->IsPrivate()) | 
 |         ++unshared; | 
 |       else | 
 |         ++shared; | 
 |     } | 
 |   } | 
 |   UMA_HISTOGRAM_COUNTS_100("Networks.Visible", visible); | 
 |   UMA_HISTOGRAM_COUNTS_100("Networks.RememberedShared", shared); | 
 |   UMA_HISTOGRAM_COUNTS_100("Networks.RememberedUnshared", unshared); | 
 | } | 
 |  | 
 | void NetworkStateHandler::DefaultNetworkServiceChanged( | 
 |     const std::string& service_path) { | 
 |   // Shill uses '/' for empty service path values; check explicitly for that. | 
 |   const char* kEmptyServicePath = "/"; | 
 |   std::string new_service_path = | 
 |       (service_path != kEmptyServicePath) ? service_path : ""; | 
 |   if (new_service_path == default_network_path_) | 
 |     return; | 
 |  | 
 |   default_network_path_ = service_path; | 
 |   NET_LOG_EVENT("DefaultNetworkServiceChanged:", default_network_path_); | 
 |   const NetworkState* network = nullptr; | 
 |   if (!default_network_path_.empty()) { | 
 |     network = GetNetworkState(default_network_path_); | 
 |     if (!network) { | 
 |       // If NetworkState is not available yet, do not notify observers here, | 
 |       // they will be notified when the state is received. | 
 |       NET_LOG(EVENT) << "Default NetworkState not available: " | 
 |                      << default_network_path_; | 
 |       return; | 
 |     } | 
 |   } | 
 |   if (network && !network->IsConnectedState()) { | 
 |     if (network->IsConnectingState()) { | 
 |       NET_LOG(EVENT) << "DefaultNetwork is connecting: " << GetLogName(network) | 
 |                      << ": " << network->connection_state(); | 
 |     } else { | 
 |       NET_LOG(ERROR) << "DefaultNetwork in unexpected state: " | 
 |                      << GetLogName(network) << ": " | 
 |                      << network->connection_state(); | 
 |     } | 
 |     // Do not notify observers here, the notification will occur when the | 
 |     // connection state changes. | 
 |     return; | 
 |   } | 
 |   NotifyDefaultNetworkChanged(network); | 
 | } | 
 |  | 
 | //------------------------------------------------------------------------------ | 
 | // Private methods | 
 |  | 
 | void NetworkStateHandler::UpdateGuid(NetworkState* network) { | 
 |   std::string specifier = network->GetSpecifier(); | 
 |   DCHECK(!specifier.empty()); | 
 |   if (!network->guid().empty()) { | 
 |     // If the network is saved in a profile, remove the entry from the map. | 
 |     // Otherwise ensure that the entry matches the specified GUID. (e.g. in | 
 |     // case a visible network with a specified guid gets configured with a | 
 |     // new guid). | 
 |     if (network->IsInProfile()) | 
 |       specifier_guid_map_.erase(specifier); | 
 |     else | 
 |       specifier_guid_map_[specifier] = network->guid(); | 
 |     return; | 
 |   } | 
 |   // Ensure that the NetworkState has a valid GUID. | 
 |   std::string guid; | 
 |   SpecifierGuidMap::iterator iter = specifier_guid_map_.find(specifier); | 
 |   if (iter != specifier_guid_map_.end()) { | 
 |     guid = iter->second; | 
 |   } else { | 
 |     guid = base::GenerateGUID(); | 
 |     specifier_guid_map_[specifier] = guid; | 
 |   } | 
 |   network->SetGuid(guid); | 
 | } | 
 |  | 
 | void NetworkStateHandler::NotifyDeviceListChanged() { | 
 |   SCOPED_NET_LOG_IF_SLOW(); | 
 |   NET_LOG_DEBUG("NOTIFY:DeviceListChanged", | 
 |                 base::StringPrintf("Size:%" PRIuS, device_list_.size())); | 
 |   FOR_EACH_OBSERVER(NetworkStateHandlerObserver, observers_, | 
 |                     DeviceListChanged()); | 
 | } | 
 |  | 
 | DeviceState* NetworkStateHandler::GetModifiableDeviceState( | 
 |     const std::string& device_path) const { | 
 |   ManagedState* managed = GetModifiableManagedState(&device_list_, device_path); | 
 |   if (!managed) | 
 |     return nullptr; | 
 |   return managed->AsDeviceState(); | 
 | } | 
 |  | 
 | NetworkState* NetworkStateHandler::GetModifiableNetworkState( | 
 |     const std::string& service_path) const { | 
 |   ManagedState* managed = | 
 |       GetModifiableManagedState(&network_list_, service_path); | 
 |   if (!managed) | 
 |     return nullptr; | 
 |   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 nullptr; | 
 | } | 
 |  | 
 | 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 nullptr; | 
 | } | 
 |  | 
 | void NetworkStateHandler::OnNetworkConnectionStateChanged( | 
 |     NetworkState* network) { | 
 |   SCOPED_NET_LOG_IF_SLOW(); | 
 |   DCHECK(network); | 
 |   bool notify_default = false; | 
 |   if (network->path() == default_network_path_) { | 
 |     if (network->IsConnectedState()) { | 
 |       notify_default = true; | 
 |     } else if (network->IsConnectingState()) { | 
 |       // Wait until the network is actually connected to notify that the default | 
 |       // network changed. | 
 |       NET_LOG(EVENT) << "Default network is not connected: " | 
 |                      << GetLogName(network) | 
 |                      << "State: " << network->connection_state(); | 
 |     } else { | 
 |       NET_LOG(ERROR) << "Default network in unexpected state: " | 
 |                      << GetLogName(network) | 
 |                      << "State: " << network->connection_state(); | 
 |       default_network_path_.clear(); | 
 |       SortNetworkList(); | 
 |       NotifyDefaultNetworkChanged(nullptr); | 
 |     } | 
 |   } | 
 |   std::string desc = "NetworkConnectionStateChanged"; | 
 |   if (notify_default) | 
 |     desc = "Default" + desc; | 
 |   NET_LOG(EVENT) << "NOTIFY: " << desc << ": " << GetLogName(network) << ": " | 
 |                  << network->connection_state(); | 
 |   FOR_EACH_OBSERVER(NetworkStateHandlerObserver, observers_, | 
 |                     NetworkConnectionStateChanged(network)); | 
 |   if (notify_default) | 
 |     NotifyDefaultNetworkChanged(network); | 
 | } | 
 |  | 
 | void NetworkStateHandler::NotifyDefaultNetworkChanged( | 
 |     const NetworkState* default_network) { | 
 |   SCOPED_NET_LOG_IF_SLOW(); | 
 |   NET_LOG_EVENT("NOTIFY:DefaultNetworkChanged", GetLogName(default_network)); | 
 |   FOR_EACH_OBSERVER(NetworkStateHandlerObserver, observers_, | 
 |                     DefaultNetworkChanged(default_network)); | 
 | } | 
 |  | 
 | void NetworkStateHandler::NotifyNetworkPropertiesUpdated( | 
 |     const NetworkState* network) { | 
 |   SCOPED_NET_LOG_IF_SLOW(); | 
 |   NET_LOG_DEBUG("NOTIFY:NetworkPropertiesUpdated", GetLogName(network)); | 
 |   FOR_EACH_OBSERVER(NetworkStateHandlerObserver, observers_, | 
 |                     NetworkPropertiesUpdated(network)); | 
 | } | 
 |  | 
 | void NetworkStateHandler::NotifyDevicePropertiesUpdated( | 
 |     const DeviceState* device) { | 
 |   SCOPED_NET_LOG_IF_SLOW(); | 
 |   NET_LOG_DEBUG("NOTIFY:DevicePropertiesUpdated", GetLogName(device)); | 
 |   FOR_EACH_OBSERVER(NetworkStateHandlerObserver, observers_, | 
 |                     DevicePropertiesUpdated(device)); | 
 | } | 
 |  | 
 | void NetworkStateHandler::NotifyScanCompleted(const DeviceState* device) { | 
 |   SCOPED_NET_LOG_IF_SLOW(); | 
 |   NET_LOG_DEBUG("NOTIFY:ScanCompleted", GetLogName(device)); | 
 |   FOR_EACH_OBSERVER(NetworkStateHandlerObserver, observers_, | 
 |                     ScanCompleted(device)); | 
 | } | 
 |  | 
 | std::string NetworkStateHandler::GetTechnologyForType( | 
 |     const NetworkTypePattern& type) const { | 
 |   if (type.MatchesType(shill::kTypeEthernet)) | 
 |     return shill::kTypeEthernet; | 
 |  | 
 |   if (type.MatchesType(shill::kTypeWifi)) | 
 |     return shill::kTypeWifi; | 
 |  | 
 |   if (type.Equals(NetworkTypePattern::Wimax())) | 
 |     return shill::kTypeWimax; | 
 |  | 
 |   // Prefer Wimax over Cellular only if it's available. | 
 |   if (type.MatchesType(shill::kTypeWimax) && | 
 |       shill_property_handler_->IsTechnologyAvailable(shill::kTypeWimax)) { | 
 |     return shill::kTypeWimax; | 
 |   } | 
 |  | 
 |   if (type.MatchesType(shill::kTypeCellular)) | 
 |     return shill::kTypeCellular; | 
 |  | 
 |   NOTREACHED(); | 
 |   return std::string(); | 
 | } | 
 |  | 
 | ScopedVector<std::string> NetworkStateHandler::GetTechnologiesForType( | 
 |     const NetworkTypePattern& type) const { | 
 |   ScopedVector<std::string> technologies; | 
 |   if (type.MatchesType(shill::kTypeEthernet)) | 
 |     technologies.push_back(new std::string(shill::kTypeEthernet)); | 
 |   if (type.MatchesType(shill::kTypeWifi)) | 
 |     technologies.push_back(new std::string(shill::kTypeWifi)); | 
 |   if (type.MatchesType(shill::kTypeWimax)) | 
 |     technologies.push_back(new std::string(shill::kTypeWimax)); | 
 |   if (type.MatchesType(shill::kTypeCellular)) | 
 |     technologies.push_back(new std::string(shill::kTypeCellular)); | 
 |   if (type.MatchesType(shill::kTypeBluetooth)) | 
 |     technologies.push_back(new std::string(shill::kTypeBluetooth)); | 
 |   if (type.MatchesType(shill::kTypeVPN)) | 
 |     technologies.push_back(new std::string(shill::kTypeVPN)); | 
 |  | 
 |   CHECK_GT(technologies.size(), 0ul); | 
 |   return technologies; | 
 | } | 
 |  | 
 | }  // namespace chromeos |