普通文本  |  4515行  |  163.48 KB

// Copyright (c) 2011 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 "chrome/browser/chromeos/cros/network_library.h"

#include <algorithm>
#include <map>

#include "base/i18n/icu_encoding_detection.h"
#include "base/i18n/icu_string_conversions.h"
#include "base/i18n/time_formatting.h"
#include "base/metrics/histogram.h"
#include "base/stl_util-inl.h"
#include "base/string_number_conversions.h"
#include "base/string_util.h"
#include "base/stringprintf.h"
#include "base/utf_string_conversions.h"
#include "base/utf_string_conversion_utils.h"
#include "base/values.h"
#include "chrome/browser/chromeos/cros/cros_library.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/chromeos/network_login_observer.h"
#include "chrome/browser/chromeos/user_cros_settings_provider.h"
#include "chrome/common/time_format.h"
#include "content/browser/browser_thread.h"
#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"

////////////////////////////////////////////////////////////////////////////////
// Implementation notes.
// NetworkLibraryImpl manages a series of classes that describe network devices
// and services:
//
// NetworkDevice: e.g. ethernet, wifi modem, cellular modem
//  device_map_: canonical map<path,NetworkDevice*> for devices
//
// Network: a network service ("network").
//  network_map_: canonical map<path,Network*> for all visible networks.
//  EthernetNetwork
//   ethernet_: EthernetNetwork* to the active ethernet network in network_map_.
//  WirelessNetwork: a Wifi or Cellular Network.
//  WifiNetwork
//   active_wifi_: WifiNetwork* to the active wifi network in network_map_.
//   wifi_networks_: ordered vector of WifiNetwork* entries in network_map_,
//       in descending order of importance.
//  CellularNetwork
//   active_cellular_: Cellular version of wifi_.
//   cellular_networks_: Cellular version of wifi_.
// network_unique_id_map_: map<unique_id,Network*> for visible networks.
// remembered_network_map_: a canonical map<path,Network*> for all networks
//     remembered in the active Profile ("favorites").
// remembered_wifi_networks_: ordered vector of WifiNetwork* entries in
//     remembered_network_map_, in descending order of preference.
//
// network_manager_monitor_: a handle to the libcros network Manager handler.
// NetworkManagerStatusChanged: This handles all messages from the Manager.
//   Messages are parsed here and the appropriate updates are then requested.
//
// UpdateNetworkServiceList: This is the primary Manager handler. It handles
//  the "Services" message which list all visible networks. The handler
//  rebuilds the network lists without destroying existing Network structures,
//  then requests neccessary updates to be fetched asynchronously from
//  libcros (RequestNetworkServiceInfo).
//
// TODO(stevenjb): Document cellular data plan handlers.
//
// AddNetworkObserver: Adds an observer for a specific network.
// NetworkObserverList: A monitor and list of observers of a network.
// network_monitor_: a handle to the libcros network Service handler.
// UpdateNetworkStatus: This handles changes to a monitored service, typically
//     changes to transient states like Strength. (Note: also updates State).
//
// AddNetworkDeviceObserver: Adds an observer for a specific device.
//                           Will be called on any device property change.
// NetworkDeviceObserverList: A monitor and list of observers of a device.
// UpdateNetworkDeviceStatus: Handles changes to a monitored device, like
//     SIM lock state and updates device state.
//
// All *Pin(...) methods use internal callback that would update cellular
//    device state once async call is completed and notify all device observers.
//
////////////////////////////////////////////////////////////////////////////////

namespace chromeos {

// Local constants.
namespace {

// Only send network change notifications to observers once every 50ms.
const int kNetworkNotifyDelayMs = 50;

// How long we should remember that cellular plan payment was received.
const int kRecentPlanPaymentHours = 6;

// Default value of the SIM unlock retries count. It is updated to the real
// retries count once cellular device with SIM card is initialized.
// If cellular device doesn't have SIM card, then retries are never used.
const int kDefaultSimUnlockRetriesCount = 999;

// Format of the Carrier ID: <carrier name> (<carrier country>).
const char* kCarrierIdFormat = "%s (%s)";

// Type of a pending SIM operation.
enum SimOperationType {
  SIM_OPERATION_NONE               = 0,
  SIM_OPERATION_CHANGE_PIN         = 1,
  SIM_OPERATION_CHANGE_REQUIRE_PIN = 2,
  SIM_OPERATION_ENTER_PIN          = 3,
  SIM_OPERATION_UNBLOCK_PIN        = 4,
};

// D-Bus interface string constants.

// Flimflam property names.
const char* kSecurityProperty = "Security";
const char* kPassphraseProperty = "Passphrase";
const char* kIdentityProperty = "Identity";
const char* kCertPathProperty = "CertPath";
const char* kPassphraseRequiredProperty = "PassphraseRequired";
const char* kSaveCredentialsProperty = "SaveCredentials";
const char* kProfilesProperty = "Profiles";
const char* kServicesProperty = "Services";
const char* kServiceWatchListProperty = "ServiceWatchList";
const char* kAvailableTechnologiesProperty = "AvailableTechnologies";
const char* kEnabledTechnologiesProperty = "EnabledTechnologies";
const char* kConnectedTechnologiesProperty = "ConnectedTechnologies";
const char* kDefaultTechnologyProperty = "DefaultTechnology";
const char* kOfflineModeProperty = "OfflineMode";
const char* kSignalStrengthProperty = "Strength";
const char* kNameProperty = "Name";
const char* kStateProperty = "State";
const char* kConnectivityStateProperty = "ConnectivityState";
const char* kTypeProperty = "Type";
const char* kDeviceProperty = "Device";
const char* kActivationStateProperty = "Cellular.ActivationState";
const char* kNetworkTechnologyProperty = "Cellular.NetworkTechnology";
const char* kRoamingStateProperty = "Cellular.RoamingState";
const char* kOperatorNameProperty = "Cellular.OperatorName";
const char* kOperatorCodeProperty = "Cellular.OperatorCode";
const char* kServingOperatorProperty = "Cellular.ServingOperator";
const char* kPaymentURLProperty = "Cellular.OlpUrl";
const char* kUsageURLProperty = "Cellular.UsageUrl";
const char* kCellularApnProperty = "Cellular.APN";
const char* kCellularLastGoodApnProperty = "Cellular.LastGoodAPN";
const char* kWifiHexSsid = "WiFi.HexSSID";
const char* kWifiFrequency = "WiFi.Frequency";
const char* kWifiHiddenSsid = "WiFi.HiddenSSID";
const char* kWifiPhyMode = "WiFi.PhyMode";
const char* kFavoriteProperty = "Favorite";
const char* kConnectableProperty = "Connectable";
const char* kAutoConnectProperty = "AutoConnect";
const char* kIsActiveProperty = "IsActive";
const char* kModeProperty = "Mode";
const char* kErrorProperty = "Error";
const char* kActiveProfileProperty = "ActiveProfile";
const char* kEntriesProperty = "Entries";
const char* kDevicesProperty = "Devices";
const char* kProviderProperty = "Provider";
const char* kHostProperty = "Host";

// Flimflam property names for SIMLock status.
const char* kSIMLockStatusProperty = "Cellular.SIMLockStatus";
const char* kSIMLockTypeProperty = "LockType";
const char* kSIMLockRetriesLeftProperty = "RetriesLeft";

// Flimflam property names for Cellular.FoundNetworks.
const char* kLongNameProperty = "long_name";
const char* kStatusProperty = "status";
const char* kShortNameProperty = "short_name";
const char* kTechnologyProperty = "technology";

// Flimflam SIMLock status types.
const char* kSIMLockPin = "sim-pin";
const char* kSIMLockPuk = "sim-puk";

// APN info property names.
const char* kApnProperty = "apn";
const char* kNetworkIdProperty = "network_id";
const char* kUsernameProperty = "username";
const char* kPasswordProperty = "password";

// Operator info property names.
const char* kOperatorNameKey = "name";
const char* kOperatorCodeKey = "code";
const char* kOperatorCountryKey = "country";

// Flimflam device info property names.
const char* kScanningProperty = "Scanning";
const char* kCarrierProperty = "Cellular.Carrier";
const char* kCellularAllowRoamingProperty = "Cellular.AllowRoaming";
const char* kHomeProviderProperty = "Cellular.HomeProvider";
const char* kMeidProperty = "Cellular.MEID";
const char* kImeiProperty = "Cellular.IMEI";
const char* kImsiProperty = "Cellular.IMSI";
const char* kEsnProperty = "Cellular.ESN";
const char* kMdnProperty = "Cellular.MDN";
const char* kMinProperty = "Cellular.MIN";
const char* kModelIDProperty = "Cellular.ModelID";
const char* kManufacturerProperty = "Cellular.Manufacturer";
const char* kFirmwareRevisionProperty = "Cellular.FirmwareRevision";
const char* kHardwareRevisionProperty = "Cellular.HardwareRevision";
const char* kPoweredProperty = "Powered";
const char* kPRLVersionProperty = "Cellular.PRLVersion"; // (INT16)
const char* kSelectedNetworkProperty = "Cellular.SelectedNetwork";
const char* kSupportNetworkScanProperty = "Cellular.SupportNetworkScan";
const char* kFoundNetworksProperty = "Cellular.FoundNetworks";

// Flimflam type options.
const char* kTypeEthernet = "ethernet";
const char* kTypeWifi = "wifi";
const char* kTypeWimax = "wimax";
const char* kTypeBluetooth = "bluetooth";
const char* kTypeCellular = "cellular";
const char* kTypeVPN = "vpn";

// Flimflam mode options.
const char* kModeManaged = "managed";
const char* kModeAdhoc = "adhoc";

// Flimflam security options.
const char* kSecurityWpa = "wpa";
const char* kSecurityWep = "wep";
const char* kSecurityRsn = "rsn";
const char* kSecurity8021x = "802_1x";
const char* kSecurityPsk = "psk";
const char* kSecurityNone = "none";

// Flimflam L2TPIPsec property names.
const char* kL2TPIPSecCACertProperty = "L2TPIPsec.CACert";
const char* kL2TPIPSecCertProperty = "L2TPIPsec.Cert";
const char* kL2TPIPSecKeyProperty = "L2TPIPsec.Key";
const char* kL2TPIPSecPSKProperty = "L2TPIPsec.PSK";
const char* kL2TPIPSecUserProperty = "L2TPIPsec.User";
const char* kL2TPIPSecPasswordProperty = "L2TPIPsec.Password";

// Flimflam EAP property names.
// See src/third_party/flimflam/doc/service-api.txt.
const char* kEapIdentityProperty = "EAP.Identity";
const char* kEapMethodProperty = "EAP.EAP";
const char* kEapPhase2AuthProperty = "EAP.InnerEAP";
const char* kEapAnonymousIdentityProperty = "EAP.AnonymousIdentity";
const char* kEapClientCertProperty = "EAP.ClientCert";  // path
const char* kEapCertIDProperty = "EAP.CertID";  // PKCS#11 ID
const char* kEapClientCertNssProperty = "EAP.ClientCertNSS";  // NSS nickname
const char* kEapPrivateKeyProperty = "EAP.PrivateKey";
const char* kEapPrivateKeyPasswordProperty = "EAP.PrivateKeyPassword";
const char* kEapKeyIDProperty = "EAP.KeyID";
const char* kEapCaCertProperty = "EAP.CACert";  // server CA cert path
const char* kEapCaCertIDProperty = "EAP.CACertID";  // server CA PKCS#11 ID
const char* kEapCaCertNssProperty = "EAP.CACertNSS";  // server CA NSS nickname
const char* kEapUseSystemCAsProperty = "EAP.UseSystemCAs";
const char* kEapPinProperty = "EAP.PIN";
const char* kEapPasswordProperty = "EAP.Password";
const char* kEapKeyMgmtProperty = "EAP.KeyMgmt";

// Flimflam EAP method options.
const std::string& kEapMethodPEAP = "PEAP";
const std::string& kEapMethodTLS = "TLS";
const std::string& kEapMethodTTLS = "TTLS";
const std::string& kEapMethodLEAP = "LEAP";

// Flimflam EAP phase 2 auth options.
const std::string& kEapPhase2AuthPEAPMD5 = "auth=MD5";
const std::string& kEapPhase2AuthPEAPMSCHAPV2 = "auth=MSCHAPV2";
const std::string& kEapPhase2AuthTTLSMD5 = "autheap=MD5";
const std::string& kEapPhase2AuthTTLSMSCHAPV2 = "autheap=MSCHAPV2";
const std::string& kEapPhase2AuthTTLSMSCHAP = "autheap=MSCHAP";
const std::string& kEapPhase2AuthTTLSPAP = "autheap=PAP";
const std::string& kEapPhase2AuthTTLSCHAP = "autheap=CHAP";

// Flimflam VPN provider types.
const char* kProviderL2tpIpsec = "l2tpipsec";
const char* kProviderOpenVpn = "openvpn";


// Flimflam state options.
const char* kStateIdle = "idle";
const char* kStateCarrier = "carrier";
const char* kStateAssociation = "association";
const char* kStateConfiguration = "configuration";
const char* kStateReady = "ready";
const char* kStateDisconnect = "disconnect";
const char* kStateFailure = "failure";
const char* kStateActivationFailure = "activation-failure";

// Flimflam connectivity state options.
const char* kConnStateUnrestricted = "unrestricted";
const char* kConnStateRestricted = "restricted";
const char* kConnStateNone = "none";

// Flimflam network technology options.
const char* kNetworkTechnology1Xrtt = "1xRTT";
const char* kNetworkTechnologyEvdo = "EVDO";
const char* kNetworkTechnologyGprs = "GPRS";
const char* kNetworkTechnologyEdge = "EDGE";
const char* kNetworkTechnologyUmts = "UMTS";
const char* kNetworkTechnologyHspa = "HSPA";
const char* kNetworkTechnologyHspaPlus = "HSPA+";
const char* kNetworkTechnologyLte = "LTE";
const char* kNetworkTechnologyLteAdvanced = "LTE Advanced";

// Flimflam roaming state options
const char* kRoamingStateHome = "home";
const char* kRoamingStateRoaming = "roaming";
const char* kRoamingStateUnknown = "unknown";

// Flimflam activation state options
const char* kActivationStateActivated = "activated";
const char* kActivationStateActivating = "activating";
const char* kActivationStateNotActivated = "not-activated";
const char* kActivationStatePartiallyActivated = "partially-activated";
const char* kActivationStateUnknown = "unknown";

// Flimflam error options.
const char* kErrorOutOfRange = "out-of-range";
const char* kErrorPinMissing = "pin-missing";
const char* kErrorDhcpFailed = "dhcp-failed";
const char* kErrorConnectFailed = "connect-failed";
const char* kErrorBadPassphrase = "bad-passphrase";
const char* kErrorBadWEPKey = "bad-wepkey";
const char* kErrorActivationFailed = "activation-failed";
const char* kErrorNeedEvdo = "need-evdo";
const char* kErrorNeedHomeNetwork = "need-home-network";
const char* kErrorOtaspFailed = "otasp-failed";
const char* kErrorAaaFailed = "aaa-failed";

// Flimflam error messages.
const char* kErrorPassphraseRequiredMsg = "Passphrase required";
const char* kErrorIncorrectPinMsg = "org.chromium.flimflam.Error.IncorrectPin";
const char* kErrorPinBlockedMsg = "org.chromium.flimflam.Error.PinBlocked";
const char* kErrorPinRequiredMsg = "org.chromium.flimflam.Error.PinRequired";

const char* kUnknownString = "UNKNOWN";

////////////////////////////////////////////////////////////////////////////

static const char* ConnectionTypeToString(ConnectionType type) {
  switch (type) {
    case TYPE_UNKNOWN:
      break;
    case TYPE_ETHERNET:
      return kTypeEthernet;
    case TYPE_WIFI:
      return kTypeWifi;
    case TYPE_WIMAX:
      return kTypeWimax;
    case TYPE_BLUETOOTH:
      return kTypeBluetooth;
    case TYPE_CELLULAR:
      return kTypeCellular;
    case TYPE_VPN:
      return kTypeVPN;
  }
  LOG(ERROR) << "ConnectionTypeToString called with unknown type: " << type;
  return kUnknownString;
}

// TODO(stevenjb/njw): Deprecate in favor of setting EAP properties.
static const char* SecurityToString(ConnectionSecurity security) {
  switch (security) {
    case SECURITY_NONE:
      return kSecurityNone;
    case SECURITY_WEP:
      return kSecurityWep;
    case SECURITY_WPA:
      return kSecurityWpa;
    case SECURITY_RSN:
      return kSecurityRsn;
    case SECURITY_8021X:
      return kSecurity8021x;
    case SECURITY_PSK:
      return kSecurityPsk;
    case SECURITY_UNKNOWN:
      break;
  }
  LOG(ERROR) << "SecurityToString called with unknown type: " << security;
  return kUnknownString;
}

static const char* ProviderTypeToString(VirtualNetwork::ProviderType type) {
  switch (type) {
    case VirtualNetwork::PROVIDER_TYPE_L2TP_IPSEC_PSK:
    case VirtualNetwork::PROVIDER_TYPE_L2TP_IPSEC_USER_CERT:
      return kProviderL2tpIpsec;
    case VirtualNetwork::PROVIDER_TYPE_OPEN_VPN:
      return kProviderOpenVpn;
    case VirtualNetwork::PROVIDER_TYPE_MAX:
      break;
  }
  LOG(ERROR) << "ProviderTypeToString called with unknown type: " << type;
  return kUnknownString;
}

////////////////////////////////////////////////////////////////////////////

// Helper class to cache maps of strings to enums.
template <typename Type>
class StringToEnum {
 public:
  struct Pair {
    const char* key;
    const Type value;
  };

  explicit StringToEnum(const Pair* list, size_t num_entries, Type unknown)
      : unknown_value_(unknown) {
    for (size_t i = 0; i < num_entries; ++i, ++list)
      enum_map_[list->key] = list->value;
  }

  Type Get(const std::string& type) const {
    EnumMapConstIter iter = enum_map_.find(type);
    if (iter != enum_map_.end())
      return iter->second;
    return unknown_value_;
  }

 private:
  typedef typename std::map<std::string, Type> EnumMap;
  typedef typename std::map<std::string, Type>::const_iterator EnumMapConstIter;
  EnumMap enum_map_;
  Type unknown_value_;
  DISALLOW_COPY_AND_ASSIGN(StringToEnum);
};

////////////////////////////////////////////////////////////////////////////

enum PropertyIndex {
  PROPERTY_INDEX_ACTIVATION_STATE,
  PROPERTY_INDEX_ACTIVE_PROFILE,
  PROPERTY_INDEX_AUTO_CONNECT,
  PROPERTY_INDEX_AVAILABLE_TECHNOLOGIES,
  PROPERTY_INDEX_CARRIER,
  PROPERTY_INDEX_CELLULAR_ALLOW_ROAMING,
  PROPERTY_INDEX_CELLULAR_APN,
  PROPERTY_INDEX_CELLULAR_LAST_GOOD_APN,
  PROPERTY_INDEX_CERT_PATH,
  PROPERTY_INDEX_CONNECTABLE,
  PROPERTY_INDEX_CONNECTED_TECHNOLOGIES,
  PROPERTY_INDEX_CONNECTIVITY_STATE,
  PROPERTY_INDEX_DEFAULT_TECHNOLOGY,
  PROPERTY_INDEX_DEVICE,
  PROPERTY_INDEX_DEVICES,
  PROPERTY_INDEX_EAP_IDENTITY,
  PROPERTY_INDEX_EAP_METHOD,
  PROPERTY_INDEX_EAP_PHASE_2_AUTH,
  PROPERTY_INDEX_EAP_ANONYMOUS_IDENTITY,
  PROPERTY_INDEX_EAP_CLIENT_CERT,
  PROPERTY_INDEX_EAP_CERT_ID,
  PROPERTY_INDEX_EAP_CLIENT_CERT_NSS,
  PROPERTY_INDEX_EAP_PRIVATE_KEY,
  PROPERTY_INDEX_EAP_PRIVATE_KEY_PASSWORD,
  PROPERTY_INDEX_EAP_KEY_ID,
  PROPERTY_INDEX_EAP_CA_CERT,
  PROPERTY_INDEX_EAP_CA_CERT_ID,
  PROPERTY_INDEX_EAP_CA_CERT_NSS,
  PROPERTY_INDEX_EAP_USE_SYSTEM_CAS,
  PROPERTY_INDEX_EAP_PIN,
  PROPERTY_INDEX_EAP_PASSWORD,
  PROPERTY_INDEX_EAP_KEY_MGMT,
  PROPERTY_INDEX_ENABLED_TECHNOLOGIES,
  PROPERTY_INDEX_ERROR,
  PROPERTY_INDEX_ESN,
  PROPERTY_INDEX_FAVORITE,
  PROPERTY_INDEX_FIRMWARE_REVISION,
  PROPERTY_INDEX_FOUND_NETWORKS,
  PROPERTY_INDEX_HARDWARE_REVISION,
  PROPERTY_INDEX_HOME_PROVIDER,
  PROPERTY_INDEX_HOST,
  PROPERTY_INDEX_IDENTITY,
  PROPERTY_INDEX_IMEI,
  PROPERTY_INDEX_IMSI,
  PROPERTY_INDEX_IS_ACTIVE,
  PROPERTY_INDEX_L2TPIPSEC_CA_CERT,
  PROPERTY_INDEX_L2TPIPSEC_CERT,
  PROPERTY_INDEX_L2TPIPSEC_KEY,
  PROPERTY_INDEX_L2TPIPSEC_PASSWORD,
  PROPERTY_INDEX_L2TPIPSEC_PSK,
  PROPERTY_INDEX_L2TPIPSEC_USER,
  PROPERTY_INDEX_MANUFACTURER,
  PROPERTY_INDEX_MDN,
  PROPERTY_INDEX_MEID,
  PROPERTY_INDEX_MIN,
  PROPERTY_INDEX_MODE,
  PROPERTY_INDEX_MODEL_ID,
  PROPERTY_INDEX_NAME,
  PROPERTY_INDEX_NETWORK_TECHNOLOGY,
  PROPERTY_INDEX_OFFLINE_MODE,
  PROPERTY_INDEX_OPERATOR_CODE,
  PROPERTY_INDEX_OPERATOR_NAME,
  PROPERTY_INDEX_PASSPHRASE,
  PROPERTY_INDEX_PASSPHRASE_REQUIRED,
  PROPERTY_INDEX_PAYMENT_URL,
  PROPERTY_INDEX_POWERED,
  PROPERTY_INDEX_PRL_VERSION,
  PROPERTY_INDEX_PROFILES,
  PROPERTY_INDEX_PROVIDER,
  PROPERTY_INDEX_ROAMING_STATE,
  PROPERTY_INDEX_SAVE_CREDENTIALS,
  PROPERTY_INDEX_SCANNING,
  PROPERTY_INDEX_SECURITY,
  PROPERTY_INDEX_SELECTED_NETWORK,
  PROPERTY_INDEX_SERVICES,
  PROPERTY_INDEX_SERVICE_WATCH_LIST,
  PROPERTY_INDEX_SERVING_OPERATOR,
  PROPERTY_INDEX_SIGNAL_STRENGTH,
  PROPERTY_INDEX_SIM_LOCK,
  PROPERTY_INDEX_STATE,
  PROPERTY_INDEX_SUPPORT_NETWORK_SCAN,
  PROPERTY_INDEX_TYPE,
  PROPERTY_INDEX_UNKNOWN,
  PROPERTY_INDEX_USAGE_URL,
  PROPERTY_INDEX_WIFI_FREQUENCY,
  PROPERTY_INDEX_WIFI_HEX_SSID,
  PROPERTY_INDEX_WIFI_HIDDEN_SSID,
  PROPERTY_INDEX_WIFI_PHY_MODE,
};

StringToEnum<PropertyIndex>::Pair property_index_table[] = {
  { kActivationStateProperty, PROPERTY_INDEX_ACTIVATION_STATE },
  { kActiveProfileProperty, PROPERTY_INDEX_ACTIVE_PROFILE },
  { kAutoConnectProperty, PROPERTY_INDEX_AUTO_CONNECT },
  { kAvailableTechnologiesProperty, PROPERTY_INDEX_AVAILABLE_TECHNOLOGIES },
  { kCellularAllowRoamingProperty, PROPERTY_INDEX_CELLULAR_ALLOW_ROAMING },
  { kCellularApnProperty, PROPERTY_INDEX_CELLULAR_APN },
  { kCellularLastGoodApnProperty, PROPERTY_INDEX_CELLULAR_LAST_GOOD_APN },
  { kCarrierProperty, PROPERTY_INDEX_CARRIER },
  { kCertPathProperty, PROPERTY_INDEX_CERT_PATH },
  { kConnectableProperty, PROPERTY_INDEX_CONNECTABLE },
  { kConnectedTechnologiesProperty, PROPERTY_INDEX_CONNECTED_TECHNOLOGIES },
  { kConnectivityStateProperty, PROPERTY_INDEX_CONNECTIVITY_STATE },
  { kDefaultTechnologyProperty, PROPERTY_INDEX_DEFAULT_TECHNOLOGY },
  { kDeviceProperty, PROPERTY_INDEX_DEVICE },
  { kDevicesProperty, PROPERTY_INDEX_DEVICES },
  { kEapIdentityProperty, PROPERTY_INDEX_EAP_IDENTITY },
  { kEapMethodProperty, PROPERTY_INDEX_EAP_METHOD },
  { kEapPhase2AuthProperty, PROPERTY_INDEX_EAP_PHASE_2_AUTH },
  { kEapAnonymousIdentityProperty, PROPERTY_INDEX_EAP_ANONYMOUS_IDENTITY },
  { kEapClientCertProperty, PROPERTY_INDEX_EAP_CLIENT_CERT },
  { kEapCertIDProperty, PROPERTY_INDEX_EAP_CERT_ID },
  { kEapClientCertNssProperty, PROPERTY_INDEX_EAP_CLIENT_CERT_NSS },
  { kEapPrivateKeyProperty, PROPERTY_INDEX_EAP_PRIVATE_KEY },
  { kEapPrivateKeyPasswordProperty, PROPERTY_INDEX_EAP_PRIVATE_KEY_PASSWORD },
  { kEapKeyIDProperty, PROPERTY_INDEX_EAP_KEY_ID },
  { kEapCaCertProperty, PROPERTY_INDEX_EAP_CA_CERT },
  { kEapCaCertIDProperty, PROPERTY_INDEX_EAP_CA_CERT_ID },
  { kEapCaCertNssProperty, PROPERTY_INDEX_EAP_CA_CERT_NSS },
  { kEapUseSystemCAsProperty, PROPERTY_INDEX_EAP_USE_SYSTEM_CAS },
  { kEapPinProperty, PROPERTY_INDEX_EAP_PIN },
  { kEapPasswordProperty, PROPERTY_INDEX_EAP_PASSWORD },
  { kEapKeyMgmtProperty, PROPERTY_INDEX_EAP_KEY_MGMT },
  { kEnabledTechnologiesProperty, PROPERTY_INDEX_ENABLED_TECHNOLOGIES },
  { kErrorProperty, PROPERTY_INDEX_ERROR },
  { kEsnProperty, PROPERTY_INDEX_ESN },
  { kFavoriteProperty, PROPERTY_INDEX_FAVORITE },
  { kFirmwareRevisionProperty, PROPERTY_INDEX_FIRMWARE_REVISION },
  { kFoundNetworksProperty, PROPERTY_INDEX_FOUND_NETWORKS },
  { kHardwareRevisionProperty, PROPERTY_INDEX_HARDWARE_REVISION },
  { kHomeProviderProperty, PROPERTY_INDEX_HOME_PROVIDER },
  { kHostProperty, PROPERTY_INDEX_HOST },
  { kIdentityProperty, PROPERTY_INDEX_IDENTITY },
  { kImeiProperty, PROPERTY_INDEX_IMEI },
  { kImsiProperty, PROPERTY_INDEX_IMSI },
  { kIsActiveProperty, PROPERTY_INDEX_IS_ACTIVE },
  { kL2TPIPSecCACertProperty, PROPERTY_INDEX_L2TPIPSEC_CA_CERT },
  { kL2TPIPSecCertProperty, PROPERTY_INDEX_L2TPIPSEC_CERT },
  { kL2TPIPSecKeyProperty, PROPERTY_INDEX_L2TPIPSEC_KEY },
  { kL2TPIPSecPasswordProperty, PROPERTY_INDEX_L2TPIPSEC_PASSWORD },
  { kL2TPIPSecPSKProperty, PROPERTY_INDEX_L2TPIPSEC_PSK },
  { kL2TPIPSecUserProperty, PROPERTY_INDEX_L2TPIPSEC_USER },
  { kManufacturerProperty, PROPERTY_INDEX_MANUFACTURER },
  { kMdnProperty, PROPERTY_INDEX_MDN },
  { kMeidProperty, PROPERTY_INDEX_MEID },
  { kMinProperty, PROPERTY_INDEX_MIN },
  { kModeProperty, PROPERTY_INDEX_MODE },
  { kModelIDProperty, PROPERTY_INDEX_MODEL_ID },
  { kNameProperty, PROPERTY_INDEX_NAME },
  { kNetworkTechnologyProperty, PROPERTY_INDEX_NETWORK_TECHNOLOGY },
  { kOfflineModeProperty, PROPERTY_INDEX_OFFLINE_MODE },
  { kOperatorCodeProperty, PROPERTY_INDEX_OPERATOR_CODE },
  { kOperatorNameProperty, PROPERTY_INDEX_OPERATOR_NAME },
  { kPRLVersionProperty, PROPERTY_INDEX_PRL_VERSION },
  { kPassphraseProperty, PROPERTY_INDEX_PASSPHRASE },
  { kPassphraseRequiredProperty, PROPERTY_INDEX_PASSPHRASE_REQUIRED },
  { kPaymentURLProperty, PROPERTY_INDEX_PAYMENT_URL },
  { kPoweredProperty, PROPERTY_INDEX_POWERED },
  { kProfilesProperty, PROPERTY_INDEX_PROFILES },
  { kProviderProperty, PROPERTY_INDEX_PROVIDER },
  { kRoamingStateProperty, PROPERTY_INDEX_ROAMING_STATE },
  { kSaveCredentialsProperty, PROPERTY_INDEX_SAVE_CREDENTIALS },
  { kScanningProperty, PROPERTY_INDEX_SCANNING },
  { kSecurityProperty, PROPERTY_INDEX_SECURITY },
  { kSelectedNetworkProperty, PROPERTY_INDEX_SELECTED_NETWORK },
  { kServiceWatchListProperty, PROPERTY_INDEX_SERVICE_WATCH_LIST },
  { kServicesProperty, PROPERTY_INDEX_SERVICES },
  { kServingOperatorProperty, PROPERTY_INDEX_SERVING_OPERATOR },
  { kSignalStrengthProperty, PROPERTY_INDEX_SIGNAL_STRENGTH },
  { kSIMLockStatusProperty, PROPERTY_INDEX_SIM_LOCK },
  { kStateProperty, PROPERTY_INDEX_STATE },
  { kSupportNetworkScanProperty, PROPERTY_INDEX_SUPPORT_NETWORK_SCAN },
  { kTypeProperty, PROPERTY_INDEX_TYPE },
  { kUsageURLProperty, PROPERTY_INDEX_USAGE_URL },
  { kWifiFrequency, PROPERTY_INDEX_WIFI_FREQUENCY },
  { kWifiHexSsid, PROPERTY_INDEX_WIFI_HEX_SSID },
  { kWifiHiddenSsid, PROPERTY_INDEX_WIFI_HIDDEN_SSID },
  { kWifiPhyMode, PROPERTY_INDEX_WIFI_PHY_MODE },
};

StringToEnum<PropertyIndex>& property_index_parser() {
  static StringToEnum<PropertyIndex> parser(property_index_table,
                                            arraysize(property_index_table),
                                            PROPERTY_INDEX_UNKNOWN);
  return parser;
}

////////////////////////////////////////////////////////////////////////////
// Parse strings from libcros.

// Network.
static ConnectionType ParseType(const std::string& type) {
  static StringToEnum<ConnectionType>::Pair table[] = {
    { kTypeEthernet, TYPE_ETHERNET },
    { kTypeWifi, TYPE_WIFI },
    { kTypeWimax, TYPE_WIMAX },
    { kTypeBluetooth, TYPE_BLUETOOTH },
    { kTypeCellular, TYPE_CELLULAR },
    { kTypeVPN, TYPE_VPN },
  };
  static StringToEnum<ConnectionType> parser(
      table, arraysize(table), TYPE_UNKNOWN);
  return parser.Get(type);
}

ConnectionType ParseTypeFromDictionary(const DictionaryValue* info) {
  std::string type_string;
  info->GetString(kTypeProperty, &type_string);
  return ParseType(type_string);
}

static ConnectionMode ParseMode(const std::string& mode) {
  static StringToEnum<ConnectionMode>::Pair table[] = {
    { kModeManaged, MODE_MANAGED },
    { kModeAdhoc, MODE_ADHOC },
  };
  static StringToEnum<ConnectionMode> parser(
      table, arraysize(table), MODE_UNKNOWN);
  return parser.Get(mode);
}

static ConnectionState ParseState(const std::string& state) {
  static StringToEnum<ConnectionState>::Pair table[] = {
    { kStateIdle, STATE_IDLE },
    { kStateCarrier, STATE_CARRIER },
    { kStateAssociation, STATE_ASSOCIATION },
    { kStateConfiguration, STATE_CONFIGURATION },
    { kStateReady, STATE_READY },
    { kStateDisconnect, STATE_DISCONNECT },
    { kStateFailure, STATE_FAILURE },
    { kStateActivationFailure, STATE_ACTIVATION_FAILURE },
  };
  static StringToEnum<ConnectionState> parser(
      table, arraysize(table), STATE_UNKNOWN);
  return parser.Get(state);
}

static ConnectionError ParseError(const std::string& error) {
  static StringToEnum<ConnectionError>::Pair table[] = {
    { kErrorOutOfRange, ERROR_OUT_OF_RANGE },
    { kErrorPinMissing, ERROR_PIN_MISSING },
    { kErrorDhcpFailed, ERROR_DHCP_FAILED },
    { kErrorConnectFailed, ERROR_CONNECT_FAILED },
    { kErrorBadPassphrase, ERROR_BAD_PASSPHRASE },
    { kErrorBadWEPKey, ERROR_BAD_WEPKEY },
    { kErrorActivationFailed, ERROR_ACTIVATION_FAILED },
    { kErrorNeedEvdo, ERROR_NEED_EVDO },
    { kErrorNeedHomeNetwork, ERROR_NEED_HOME_NETWORK },
    { kErrorOtaspFailed, ERROR_OTASP_FAILED },
    { kErrorAaaFailed, ERROR_AAA_FAILED },
  };
  static StringToEnum<ConnectionError> parser(
      table, arraysize(table), ERROR_NO_ERROR);
  return parser.Get(error);
}

// VirtualNetwork
static VirtualNetwork::ProviderType ParseProviderType(const std::string& mode) {
  static StringToEnum<VirtualNetwork::ProviderType>::Pair table[] = {
    { kProviderL2tpIpsec, VirtualNetwork::PROVIDER_TYPE_L2TP_IPSEC_PSK },
    { kProviderOpenVpn, VirtualNetwork::PROVIDER_TYPE_OPEN_VPN },
  };
  static StringToEnum<VirtualNetwork::ProviderType> parser(
      table, arraysize(table), VirtualNetwork::PROVIDER_TYPE_MAX);
  return parser.Get(mode);
}

// CellularNetwork.
static ActivationState ParseActivationState(const std::string& state) {
  static StringToEnum<ActivationState>::Pair table[] = {
    { kActivationStateActivated, ACTIVATION_STATE_ACTIVATED },
    { kActivationStateActivating, ACTIVATION_STATE_ACTIVATING },
    { kActivationStateNotActivated, ACTIVATION_STATE_NOT_ACTIVATED },
    { kActivationStatePartiallyActivated, ACTIVATION_STATE_PARTIALLY_ACTIVATED},
    { kActivationStateUnknown, ACTIVATION_STATE_UNKNOWN},
  };
  static StringToEnum<ActivationState> parser(
      table, arraysize(table), ACTIVATION_STATE_UNKNOWN);
  return parser.Get(state);
}

static ConnectivityState ParseConnectivityState(const std::string& state) {
  static StringToEnum<ConnectivityState>::Pair table[] = {
    { kConnStateUnrestricted, CONN_STATE_UNRESTRICTED },
    { kConnStateRestricted, CONN_STATE_RESTRICTED },
    { kConnStateNone, CONN_STATE_NONE },
  };
  static StringToEnum<ConnectivityState> parser(
      table, arraysize(table), CONN_STATE_UNKNOWN);
  return parser.Get(state);
}

static NetworkTechnology ParseNetworkTechnology(const std::string& technology) {
  static StringToEnum<NetworkTechnology>::Pair table[] = {
    { kNetworkTechnology1Xrtt, NETWORK_TECHNOLOGY_1XRTT },
    { kNetworkTechnologyEvdo, NETWORK_TECHNOLOGY_EVDO },
    { kNetworkTechnologyGprs, NETWORK_TECHNOLOGY_GPRS },
    { kNetworkTechnologyEdge, NETWORK_TECHNOLOGY_EDGE },
    { kNetworkTechnologyUmts, NETWORK_TECHNOLOGY_UMTS },
    { kNetworkTechnologyHspa, NETWORK_TECHNOLOGY_HSPA },
    { kNetworkTechnologyHspaPlus, NETWORK_TECHNOLOGY_HSPA_PLUS },
    { kNetworkTechnologyLte, NETWORK_TECHNOLOGY_LTE },
    { kNetworkTechnologyLteAdvanced, NETWORK_TECHNOLOGY_LTE_ADVANCED },
  };
  static StringToEnum<NetworkTechnology> parser(
      table, arraysize(table), NETWORK_TECHNOLOGY_UNKNOWN);
  return parser.Get(technology);
}

static SIMLockState ParseSimLockState(const std::string& state) {
  static StringToEnum<SIMLockState>::Pair table[] = {
    { "", SIM_UNLOCKED },
    { kSIMLockPin, SIM_LOCKED_PIN },
    { kSIMLockPuk, SIM_LOCKED_PUK },
  };
  static StringToEnum<SIMLockState> parser(
      table, arraysize(table), SIM_UNKNOWN);
  SIMLockState parsed_state = parser.Get(state);
  DCHECK(parsed_state != SIM_UNKNOWN) << "Unknown SIMLock state encountered";
  return parsed_state;
}

static bool ParseSimLockStateFromDictionary(const DictionaryValue* info,
                                            SIMLockState* out_state,
                                            int* out_retries) {
  std::string state_string;
  if (!info->GetString(kSIMLockTypeProperty, &state_string) ||
      !info->GetInteger(kSIMLockRetriesLeftProperty, out_retries)) {
    LOG(ERROR) << "Error parsing SIMLock state";
    return false;
  }
  *out_state = ParseSimLockState(state_string);
  return true;
}

static bool ParseFoundNetworksFromList(const ListValue* list,
                                       CellularNetworkList* found_networks_) {
  found_networks_->clear();
  found_networks_->reserve(list->GetSize());
  for (ListValue::const_iterator it = list->begin(); it != list->end(); ++it) {
    if ((*it)->IsType(Value::TYPE_DICTIONARY)) {
      found_networks_->resize(found_networks_->size() + 1);
      DictionaryValue* dict = static_cast<const DictionaryValue*>(*it);
      dict->GetStringWithoutPathExpansion(
          kStatusProperty, &found_networks_->back().status);
      dict->GetStringWithoutPathExpansion(
          kNetworkIdProperty, &found_networks_->back().network_id);
      dict->GetStringWithoutPathExpansion(
          kShortNameProperty, &found_networks_->back().short_name);
      dict->GetStringWithoutPathExpansion(
          kLongNameProperty, &found_networks_->back().long_name);
      dict->GetStringWithoutPathExpansion(
          kTechnologyProperty, &found_networks_->back().technology);
    } else {
      return false;
    }
  }
  return true;
}

static NetworkRoamingState ParseRoamingState(const std::string& roaming_state) {
  static StringToEnum<NetworkRoamingState>::Pair table[] = {
    { kRoamingStateHome, ROAMING_STATE_HOME },
    { kRoamingStateRoaming, ROAMING_STATE_ROAMING },
    { kRoamingStateUnknown, ROAMING_STATE_UNKNOWN },
  };
  static StringToEnum<NetworkRoamingState> parser(
      table, arraysize(table), ROAMING_STATE_UNKNOWN);
  return parser.Get(roaming_state);
}

// WifiNetwork
static ConnectionSecurity ParseSecurity(const std::string& security) {
  static StringToEnum<ConnectionSecurity>::Pair table[] = {
    { kSecurityNone, SECURITY_NONE },
    { kSecurityWep, SECURITY_WEP },
    { kSecurityWpa, SECURITY_WPA },
    { kSecurityRsn, SECURITY_RSN },
    { kSecurityPsk, SECURITY_PSK },
    { kSecurity8021x, SECURITY_8021X },
  };
  static StringToEnum<ConnectionSecurity> parser(
      table, arraysize(table), SECURITY_UNKNOWN);
  return parser.Get(security);
}

static EAPMethod ParseEAPMethod(const std::string& method) {
  static StringToEnum<EAPMethod>::Pair table[] = {
    { kEapMethodPEAP.c_str(), EAP_METHOD_PEAP },
    { kEapMethodTLS.c_str(), EAP_METHOD_TLS },
    { kEapMethodTTLS.c_str(), EAP_METHOD_TTLS },
    { kEapMethodLEAP.c_str(), EAP_METHOD_LEAP },
  };
  static StringToEnum<EAPMethod> parser(
      table, arraysize(table), EAP_METHOD_UNKNOWN);
  return parser.Get(method);
}

static EAPPhase2Auth ParseEAPPhase2Auth(const std::string& auth) {
  static StringToEnum<EAPPhase2Auth>::Pair table[] = {
    { kEapPhase2AuthPEAPMD5.c_str(), EAP_PHASE_2_AUTH_MD5 },
    { kEapPhase2AuthPEAPMSCHAPV2.c_str(), EAP_PHASE_2_AUTH_MSCHAPV2 },
    { kEapPhase2AuthTTLSMD5.c_str(), EAP_PHASE_2_AUTH_MD5 },
    { kEapPhase2AuthTTLSMSCHAPV2.c_str(), EAP_PHASE_2_AUTH_MSCHAPV2 },
    { kEapPhase2AuthTTLSMSCHAP.c_str(), EAP_PHASE_2_AUTH_MSCHAP },
    { kEapPhase2AuthTTLSPAP.c_str(), EAP_PHASE_2_AUTH_PAP },
    { kEapPhase2AuthTTLSCHAP.c_str(), EAP_PHASE_2_AUTH_CHAP },
  };
  static StringToEnum<EAPPhase2Auth> parser(
      table, arraysize(table), EAP_PHASE_2_AUTH_AUTO);
  return parser.Get(auth);
}

////////////////////////////////////////////////////////////////////////////////
// Misc.

// Safe string constructor since we can't rely on non NULL pointers
// for string values from libcros.
static std::string SafeString(const char* s) {
  return s ? std::string(s) : std::string();
}

// Erase the memory used by a string, then clear it.
static void WipeString(std::string* str) {
  str->assign(str->size(), '\0');
  str->clear();
}

static bool EnsureCrosLoaded() {
  if (!CrosLibrary::Get()->EnsureLoaded()) {
    return false;
  } else {
    if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
      LOG(ERROR) << "chromeos_library calls made from non UI thread!";
      NOTREACHED();
    }
    return true;
  }
}

static void ValidateUTF8(const std::string& str, std::string* output) {
  output->clear();

  for (int32 index = 0; index < static_cast<int32>(str.size()); ++index) {
    uint32 code_point_out;
    bool is_unicode_char = base::ReadUnicodeCharacter(str.c_str(), str.size(),
                                                      &index, &code_point_out);
    if (is_unicode_char && (code_point_out >= 0x20))
      base::WriteUnicodeCharacter(code_point_out, output);
    else
      // Puts REPLACEMENT CHARACTER (U+FFFD) if character is not readable UTF-8
      base::WriteUnicodeCharacter(0xFFFD, output);
  }
}

}  // namespace

////////////////////////////////////////////////////////////////////////////////
// NetworkDevice

NetworkDevice::NetworkDevice(const std::string& device_path)
    : device_path_(device_path),
      type_(TYPE_UNKNOWN),
      scanning_(false),
      sim_lock_state_(SIM_UNKNOWN),
      sim_retries_left_(kDefaultSimUnlockRetriesCount),
      sim_pin_required_(SIM_PIN_REQUIRE_UNKNOWN),
      PRL_version_(0),
      data_roaming_allowed_(false),
      support_network_scan_(false) {
}

bool NetworkDevice::ParseValue(int index, const Value* value) {
  switch (index) {
    case PROPERTY_INDEX_TYPE: {
      std::string type_string;
      if (value->GetAsString(&type_string)) {
        type_ = ParseType(type_string);
        return true;
      }
      break;
    }
    case PROPERTY_INDEX_NAME:
      return value->GetAsString(&name_);
    case PROPERTY_INDEX_CARRIER:
      return value->GetAsString(&carrier_);
    case PROPERTY_INDEX_SCANNING:
      return value->GetAsBoolean(&scanning_);
    case PROPERTY_INDEX_CELLULAR_ALLOW_ROAMING:
      return value->GetAsBoolean(&data_roaming_allowed_);
    case PROPERTY_INDEX_FOUND_NETWORKS:
      if (value->IsType(Value::TYPE_LIST)) {
        return ParseFoundNetworksFromList(
            static_cast<const ListValue*>(value),
            &found_cellular_networks_);
      }
      break;
    case PROPERTY_INDEX_HOME_PROVIDER: {
      if (value->IsType(Value::TYPE_DICTIONARY)) {
        const DictionaryValue *dict =
            static_cast<const DictionaryValue*>(value);
        home_provider_code_.clear();
        home_provider_country_.clear();
        home_provider_name_.clear();
        dict->GetStringWithoutPathExpansion(kOperatorCodeKey,
                                            &home_provider_code_);
        dict->GetStringWithoutPathExpansion(kOperatorCountryKey,
                                            &home_provider_country_);
        dict->GetStringWithoutPathExpansion(kOperatorNameKey,
                                            &home_provider_name_);
        if (!home_provider_name_.empty() && !home_provider_country_.empty()) {
          home_provider_id_ = base::StringPrintf(
              kCarrierIdFormat,
              home_provider_name_.c_str(),
              home_provider_country_.c_str());
        } else {
          home_provider_id_ = home_provider_code_;
          LOG(WARNING) << "Carrier ID not defined, using code instead: "
                       << home_provider_id_;
        }
        return true;
      }
      break;
    }
    case PROPERTY_INDEX_MEID:
      return value->GetAsString(&MEID_);
    case PROPERTY_INDEX_IMEI:
      return value->GetAsString(&IMEI_);
    case PROPERTY_INDEX_IMSI:
      return value->GetAsString(&IMSI_);
    case PROPERTY_INDEX_ESN:
      return value->GetAsString(&ESN_);
    case PROPERTY_INDEX_MDN:
      return value->GetAsString(&MDN_);
    case PROPERTY_INDEX_MIN:
      return value->GetAsString(&MIN_);
    case PROPERTY_INDEX_MODEL_ID:
      return value->GetAsString(&model_id_);
    case PROPERTY_INDEX_MANUFACTURER:
      return value->GetAsString(&manufacturer_);
    case PROPERTY_INDEX_SIM_LOCK:
      if (value->IsType(Value::TYPE_DICTIONARY)) {
        bool result = ParseSimLockStateFromDictionary(
            static_cast<const DictionaryValue*>(value),
            &sim_lock_state_,
            &sim_retries_left_);
        // Initialize PinRequired value only once.
        // See SIMPinRequire enum comments.
        if (sim_pin_required_ == SIM_PIN_REQUIRE_UNKNOWN) {
          if (sim_lock_state_ == SIM_UNLOCKED) {
            sim_pin_required_ = SIM_PIN_NOT_REQUIRED;
          } else if (sim_lock_state_ == SIM_LOCKED_PIN ||
                     sim_lock_state_ == SIM_LOCKED_PUK) {
            sim_pin_required_ = SIM_PIN_REQUIRED;
          }
        }
        return result;
      }
      break;
    case PROPERTY_INDEX_FIRMWARE_REVISION:
      return value->GetAsString(&firmware_revision_);
    case PROPERTY_INDEX_HARDWARE_REVISION:
      return value->GetAsString(&hardware_revision_);
    case PROPERTY_INDEX_POWERED:
      // we don't care about the value, just the fact that it changed
      return true;
    case PROPERTY_INDEX_PRL_VERSION:
      return value->GetAsInteger(&PRL_version_);
    case PROPERTY_INDEX_SELECTED_NETWORK:
      return value->GetAsString(&selected_cellular_network_);
    case PROPERTY_INDEX_SUPPORT_NETWORK_SCAN:
      return value->GetAsBoolean(&support_network_scan_);
    default:
      break;
  }
  return false;
}

void NetworkDevice::ParseInfo(const DictionaryValue* info) {
  for (DictionaryValue::key_iterator iter = info->begin_keys();
       iter != info->end_keys(); ++iter) {
    const std::string& key = *iter;
    Value* value;
    bool res = info->GetWithoutPathExpansion(key, &value);
    DCHECK(res);
    if (res) {
      int index = property_index_parser().Get(key);
      if (!ParseValue(index, value))
        VLOG(1) << "NetworkDevice: Unhandled key: " << key;
    }
  }
}

////////////////////////////////////////////////////////////////////////////////
// Network

void Network::SetName(const std::string& name) {
  std::string name_utf8;
  ValidateUTF8(name, &name_utf8);
  set_name(name_utf8);
}

bool Network::ParseValue(int index, const Value* value) {
  switch (index) {
    case PROPERTY_INDEX_TYPE: {
      std::string type_string;
      if (value->GetAsString(&type_string)) {
        ConnectionType type = ParseType(type_string);
        LOG_IF(ERROR, type != type_)
            << "Network with mismatched type: " << service_path_
            << " " << type << " != " << type_;
        return true;
      }
      break;
    }
    case PROPERTY_INDEX_DEVICE:
      return value->GetAsString(&device_path_);
    case PROPERTY_INDEX_NAME: {
      std::string name;
      if (value->GetAsString(&name)) {
        SetName(name);
        return true;
      }
      break;
    }
    case PROPERTY_INDEX_STATE: {
      std::string state_string;
      if (value->GetAsString(&state_string)) {
        ConnectionState prev_state = state_;
        state_ = ParseState(state_string);
        if (state_ != prev_state) {
          // State changed, so refresh IP address.
          // Note: blocking DBus call. TODO(stevenjb): refactor this.
          InitIPAddress();
        }
        return true;
      }
      break;
    }
    case PROPERTY_INDEX_MODE: {
      std::string mode_string;
      if (value->GetAsString(&mode_string)) {
        mode_ = ParseMode(mode_string);
        return true;
      }
      break;
    }
    case PROPERTY_INDEX_ERROR: {
      std::string error_string;
      if (value->GetAsString(&error_string)) {
        error_ = ParseError(error_string);
        return true;
      }
      break;
    }
    case PROPERTY_INDEX_CONNECTABLE:
      return value->GetAsBoolean(&connectable_);
    case PROPERTY_INDEX_IS_ACTIVE:
      return value->GetAsBoolean(&is_active_);
    case PROPERTY_INDEX_FAVORITE:
      return value->GetAsBoolean(&favorite_);
    case PROPERTY_INDEX_AUTO_CONNECT:
      return value->GetAsBoolean(&auto_connect_);
    case PROPERTY_INDEX_CONNECTIVITY_STATE: {
      std::string connectivity_state_string;
      if (value->GetAsString(&connectivity_state_string)) {
        connectivity_state_ = ParseConnectivityState(connectivity_state_string);
        return true;
      }
      break;
    }
    default:
      break;
  }
  return false;
}

void Network::ParseInfo(const DictionaryValue* info) {
  for (DictionaryValue::key_iterator iter = info->begin_keys();
       iter != info->end_keys(); ++iter) {
    const std::string& key = *iter;
    Value* value;
    bool res = info->GetWithoutPathExpansion(key, &value);
    DCHECK(res);
    if (res) {
      int index = property_index_parser().Get(key);
      if (!ParseValue(index, value))  // virtual.
        VLOG(1) << "Network: " << name()
                << " Type: " << ConnectionTypeToString(type())
                << " Unhandled key: " << key;
    }
  }
}

void Network::SetValueProperty(const char* prop, Value* val) {
  DCHECK(prop);
  DCHECK(val);
  if (!EnsureCrosLoaded())
    return;
  SetNetworkServiceProperty(service_path_.c_str(), prop, val);
}

void Network::ClearProperty(const char* prop) {
  DCHECK(prop);
  if (!EnsureCrosLoaded())
    return;
  ClearNetworkServiceProperty(service_path_.c_str(), prop);
}

void Network::SetStringProperty(
    const char* prop, const std::string& str, std::string* dest) {
  if (dest)
    *dest = str;
  scoped_ptr<Value> value(Value::CreateStringValue(str));
  SetValueProperty(prop, value.get());
}

void Network::SetOrClearStringProperty(const char* prop,
                                       const std::string& str,
                                       std::string* dest) {
  if (str.empty()) {
    ClearProperty(prop);
    if (dest)
      dest->clear();
  } else {
    SetStringProperty(prop, str, dest);
  }
}

void Network::SetBooleanProperty(const char* prop, bool b, bool* dest) {
  if (dest)
    *dest = b;
  scoped_ptr<Value> value(Value::CreateBooleanValue(b));
  SetValueProperty(prop, value.get());
}

void Network::SetIntegerProperty(const char* prop, int i, int* dest) {
  if (dest)
    *dest = i;
  scoped_ptr<Value> value(Value::CreateIntegerValue(i));
  SetValueProperty(prop, value.get());
}

void Network::SetAutoConnect(bool auto_connect) {
  SetBooleanProperty(kAutoConnectProperty, auto_connect, &auto_connect_);
}

std::string Network::GetStateString() const {
  switch (state_) {
    case STATE_UNKNOWN:
      return l10n_util::GetStringUTF8(IDS_CHROMEOS_NETWORK_STATE_UNKNOWN);
    case STATE_IDLE:
      return l10n_util::GetStringUTF8(IDS_CHROMEOS_NETWORK_STATE_IDLE);
    case STATE_CARRIER:
      return l10n_util::GetStringUTF8(IDS_CHROMEOS_NETWORK_STATE_CARRIER);
    case STATE_ASSOCIATION:
      return l10n_util::GetStringUTF8(IDS_CHROMEOS_NETWORK_STATE_ASSOCIATION);
    case STATE_CONFIGURATION:
      return l10n_util::GetStringUTF8(IDS_CHROMEOS_NETWORK_STATE_CONFIGURATION);
    case STATE_READY:
      return l10n_util::GetStringUTF8(IDS_CHROMEOS_NETWORK_STATE_READY);
    case STATE_DISCONNECT:
      return l10n_util::GetStringUTF8(IDS_CHROMEOS_NETWORK_STATE_DISCONNECT);
    case STATE_FAILURE:
      return l10n_util::GetStringUTF8(IDS_CHROMEOS_NETWORK_STATE_FAILURE);
    case STATE_ACTIVATION_FAILURE:
      return l10n_util::GetStringUTF8(
          IDS_CHROMEOS_NETWORK_STATE_ACTIVATION_FAILURE);
  }
  return l10n_util::GetStringUTF8(IDS_CHROMEOS_NETWORK_STATE_UNRECOGNIZED);
}

std::string Network::GetErrorString() const {
  switch (error_) {
    case ERROR_NO_ERROR:
      // TODO(nkostylev): Introduce new error message "None" instead.
      return std::string();
    case ERROR_OUT_OF_RANGE:
      return l10n_util::GetStringUTF8(IDS_CHROMEOS_NETWORK_ERROR_OUT_OF_RANGE);
    case ERROR_PIN_MISSING:
      return l10n_util::GetStringUTF8(IDS_CHROMEOS_NETWORK_ERROR_PIN_MISSING);
    case ERROR_DHCP_FAILED:
      return l10n_util::GetStringUTF8(IDS_CHROMEOS_NETWORK_ERROR_DHCP_FAILED);
    case ERROR_CONNECT_FAILED:
      return l10n_util::GetStringUTF8(
          IDS_CHROMEOS_NETWORK_ERROR_CONNECT_FAILED);
    case ERROR_BAD_PASSPHRASE:
      return l10n_util::GetStringUTF8(
          IDS_CHROMEOS_NETWORK_ERROR_BAD_PASSPHRASE);
    case ERROR_BAD_WEPKEY:
      return l10n_util::GetStringUTF8(IDS_CHROMEOS_NETWORK_ERROR_BAD_WEPKEY);
    case ERROR_ACTIVATION_FAILED:
      return l10n_util::GetStringUTF8(
          IDS_CHROMEOS_NETWORK_ERROR_ACTIVATION_FAILED);
    case ERROR_NEED_EVDO:
      return l10n_util::GetStringUTF8(IDS_CHROMEOS_NETWORK_ERROR_NEED_EVDO);
    case ERROR_NEED_HOME_NETWORK:
      return l10n_util::GetStringUTF8(
          IDS_CHROMEOS_NETWORK_ERROR_NEED_HOME_NETWORK);
    case ERROR_OTASP_FAILED:
      return l10n_util::GetStringUTF8(IDS_CHROMEOS_NETWORK_ERROR_OTASP_FAILED);
    case ERROR_AAA_FAILED:
      return l10n_util::GetStringUTF8(IDS_CHROMEOS_NETWORK_ERROR_AAA_FAILED);
  }
  return l10n_util::GetStringUTF8(IDS_CHROMEOS_NETWORK_STATE_UNRECOGNIZED);
}

void Network::InitIPAddress() {
  ip_address_.clear();
  // If connected, get ip config.
  if (EnsureCrosLoaded() && connected() && !device_path_.empty()) {
    IPConfigStatus* ipconfig_status = ListIPConfigs(device_path_.c_str());
    if (ipconfig_status) {
      for (int i = 0; i < ipconfig_status->size; i++) {
        IPConfig ipconfig = ipconfig_status->ips[i];
        if (strlen(ipconfig.address) > 0) {
          ip_address_ = ipconfig.address;
          break;
        }
      }
      FreeIPConfigStatus(ipconfig_status);
    }
  }
}

////////////////////////////////////////////////////////////////////////////////
// VirtualNetwork

bool VirtualNetwork::ParseProviderValue(int index, const Value* value) {
  switch (index) {
    case PROPERTY_INDEX_HOST:
      return value->GetAsString(&server_hostname_);
    case PROPERTY_INDEX_NAME:
      // Note: shadows Network::name_ property.
      return value->GetAsString(&name_);
    case PROPERTY_INDEX_TYPE: {
      std::string provider_type_string;
      if (value->GetAsString(&provider_type_string)) {
        provider_type_ = ParseProviderType(provider_type_string);
        return true;
      }
      break;
    }
    default:
      break;
  }
  return false;
}

bool VirtualNetwork::ParseValue(int index, const Value* value) {
  switch (index) {
    case PROPERTY_INDEX_PROVIDER: {
      DCHECK_EQ(value->GetType(), Value::TYPE_DICTIONARY);
      const DictionaryValue* dict = static_cast<const DictionaryValue*>(value);
      for (DictionaryValue::key_iterator iter = dict->begin_keys();
           iter != dict->end_keys(); ++iter) {
        const std::string& key = *iter;
        Value* v;
        bool res = dict->GetWithoutPathExpansion(key, &v);
        DCHECK(res);
        if (res) {
          int index = property_index_parser().Get(key);
          if (!ParseProviderValue(index, v))
            VLOG(1) << name() << ": Provider unhandled key: " << key
                    << " Type: " << v->GetType();
        }
      }
      return true;
    }
    case PROPERTY_INDEX_L2TPIPSEC_CA_CERT:
      return value->GetAsString(&ca_cert_);
    case PROPERTY_INDEX_L2TPIPSEC_PSK:
      return value->GetAsString(&psk_passphrase_);
    case PROPERTY_INDEX_L2TPIPSEC_CERT:
      return value->GetAsString(&user_cert_);
    case PROPERTY_INDEX_L2TPIPSEC_KEY:
      return value->GetAsString(&user_cert_key_);
    case PROPERTY_INDEX_L2TPIPSEC_USER:
      return value->GetAsString(&username_);
    case PROPERTY_INDEX_L2TPIPSEC_PASSWORD:
      return value->GetAsString(&user_passphrase_);
    default:
      return Network::ParseValue(index, value);
      break;
  }
  return false;
}

void VirtualNetwork::ParseInfo(const DictionaryValue* info) {
  Network::ParseInfo(info);
  VLOG(1) << "VPN: " << name()
          << " Type: " << ProviderTypeToString(provider_type());
  if (provider_type_ == PROVIDER_TYPE_L2TP_IPSEC_PSK) {
    if (!user_cert_.empty())
      provider_type_ = PROVIDER_TYPE_L2TP_IPSEC_USER_CERT;
  }
}

bool VirtualNetwork::NeedMoreInfoToConnect() const {
  if (server_hostname_.empty() || username_.empty() || user_passphrase_.empty())
    return true;
  switch (provider_type_) {
    case PROVIDER_TYPE_L2TP_IPSEC_PSK:
      if (psk_passphrase_.empty())
        return true;
      break;
    case PROVIDER_TYPE_L2TP_IPSEC_USER_CERT:
    case PROVIDER_TYPE_OPEN_VPN:
      if (user_cert_.empty())
        return true;
      break;
    case PROVIDER_TYPE_MAX:
      break;
  }
  return false;
}

void VirtualNetwork::SetCACert(const std::string& ca_cert) {
  SetStringProperty(kL2TPIPSecCACertProperty, ca_cert, &ca_cert_);
}

void VirtualNetwork::SetPSKPassphrase(const std::string& psk_passphrase) {
  SetStringProperty(kL2TPIPSecPSKProperty, psk_passphrase,
                           &psk_passphrase_);
}

void VirtualNetwork::SetUserCert(const std::string& user_cert) {
  SetStringProperty(kL2TPIPSecCertProperty, user_cert, &user_cert_);
}

void VirtualNetwork::SetUserCertKey(const std::string& key) {
  SetStringProperty(kL2TPIPSecKeyProperty, key, &user_cert_key_);
}

void VirtualNetwork::SetUsername(const std::string& username) {
  SetStringProperty(kL2TPIPSecUserProperty, username, &username_);
}

void VirtualNetwork::SetUserPassphrase(const std::string& user_passphrase) {
  SetStringProperty(kL2TPIPSecPasswordProperty, user_passphrase,
                    &user_passphrase_);
}

std::string VirtualNetwork::GetProviderTypeString() const {
  switch (this->provider_type_) {
    case PROVIDER_TYPE_L2TP_IPSEC_PSK:
      return l10n_util::GetStringUTF8(
          IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_L2TP_IPSEC_PSK);
      break;
    case PROVIDER_TYPE_L2TP_IPSEC_USER_CERT:
      return l10n_util::GetStringUTF8(
          IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_L2TP_IPSEC_USER_CERT);
      break;
    case PROVIDER_TYPE_OPEN_VPN:
      return l10n_util::GetStringUTF8(
          IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_OPEN_VPN);
      break;
    default:
      return l10n_util::GetStringUTF8(
          IDS_CHROMEOS_NETWORK_ERROR_UNKNOWN);
      break;
  }
}

////////////////////////////////////////////////////////////////////////////////
// WirelessNetwork

bool WirelessNetwork::ParseValue(int index, const Value* value) {
  switch (index) {
    case PROPERTY_INDEX_SIGNAL_STRENGTH:
      return value->GetAsInteger(&strength_);
    default:
      return Network::ParseValue(index, value);
      break;
  }
  return false;
}

////////////////////////////////////////////////////////////////////////////////
// CellularDataPlan

string16 CellularDataPlan::GetPlanDesciption() const {
  switch (plan_type) {
    case chromeos::CELLULAR_DATA_PLAN_UNLIMITED: {
      return l10n_util::GetStringFUTF16(
          IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_PURCHASE_UNLIMITED_DATA,
          base::TimeFormatFriendlyDate(plan_start_time));
      break;
    }
    case chromeos::CELLULAR_DATA_PLAN_METERED_PAID: {
      return l10n_util::GetStringFUTF16(
                IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_PURCHASE_DATA,
                FormatBytes(plan_data_bytes,
                            GetByteDisplayUnits(plan_data_bytes),
                            true),
                base::TimeFormatFriendlyDate(plan_start_time));
    }
    case chromeos::CELLULAR_DATA_PLAN_METERED_BASE: {
      return l10n_util::GetStringFUTF16(
                IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_RECEIVED_FREE_DATA,
                FormatBytes(plan_data_bytes,
                            GetByteDisplayUnits(plan_data_bytes),
                            true),
                base::TimeFormatFriendlyDate(plan_start_time));
    default:
      break;
    }
  }
  return string16();
}

string16 CellularDataPlan::GetRemainingWarning() const {
  if (plan_type == chromeos::CELLULAR_DATA_PLAN_UNLIMITED) {
    // Time based plan. Show nearing expiration and data expiration.
    if (remaining_time().InSeconds() <= chromeos::kCellularDataVeryLowSecs) {
      return GetPlanExpiration();
    }
  } else if (plan_type == chromeos::CELLULAR_DATA_PLAN_METERED_PAID ||
             plan_type == chromeos::CELLULAR_DATA_PLAN_METERED_BASE) {
    // Metered plan. Show low data and out of data.
    if (remaining_data() <= chromeos::kCellularDataVeryLowBytes) {
      int64 remaining_mbytes = remaining_data() / (1024 * 1024);
      return l10n_util::GetStringFUTF16(
          IDS_NETWORK_DATA_REMAINING_MESSAGE,
          UTF8ToUTF16(base::Int64ToString(remaining_mbytes)));
    }
  }
  return string16();
}

string16 CellularDataPlan::GetDataRemainingDesciption() const {
  int64 remaining_bytes = remaining_data();
  switch (plan_type) {
    case chromeos::CELLULAR_DATA_PLAN_UNLIMITED: {
      return l10n_util::GetStringUTF16(
          IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_UNLIMITED);
    }
    case chromeos::CELLULAR_DATA_PLAN_METERED_PAID: {
      return FormatBytes(remaining_bytes,
          GetByteDisplayUnits(remaining_bytes),
          true);
    }
    case chromeos::CELLULAR_DATA_PLAN_METERED_BASE: {
      return FormatBytes(remaining_bytes,
          GetByteDisplayUnits(remaining_bytes),
          true);
    }
    default:
      break;
  }
  return string16();
}

string16 CellularDataPlan::GetUsageInfo() const {
  if (plan_type == chromeos::CELLULAR_DATA_PLAN_UNLIMITED) {
    // Time based plan. Show nearing expiration and data expiration.
    return GetPlanExpiration();
  } else if (plan_type == chromeos::CELLULAR_DATA_PLAN_METERED_PAID ||
             plan_type == chromeos::CELLULAR_DATA_PLAN_METERED_BASE) {
    // Metered plan. Show low data and out of data.
    int64 remaining_bytes = remaining_data();
    if (remaining_bytes == 0) {
      return l10n_util::GetStringUTF16(
          IDS_NETWORK_DATA_NONE_AVAILABLE_MESSAGE);
    } else if (remaining_bytes < 1024 * 1024) {
      return l10n_util::GetStringUTF16(
          IDS_NETWORK_DATA_LESS_THAN_ONE_MB_AVAILABLE_MESSAGE);
    } else {
      int64 remaining_mb = remaining_bytes / (1024 * 1024);
      return l10n_util::GetStringFUTF16(
          IDS_NETWORK_DATA_MB_AVAILABLE_MESSAGE,
          UTF8ToUTF16(base::Int64ToString(remaining_mb)));
    }
  }
  return string16();
}

std::string CellularDataPlan::GetUniqueIdentifier() const {
  // A cellular plan is uniquely described by the union of name, type,
  // start time, end time, and max bytes.
  // So we just return a union of all these variables.
  return plan_name + "|" +
      base::Int64ToString(plan_type) + "|" +
      base::Int64ToString(plan_start_time.ToInternalValue()) + "|" +
      base::Int64ToString(plan_end_time.ToInternalValue()) + "|" +
      base::Int64ToString(plan_data_bytes);
}

base::TimeDelta CellularDataPlan::remaining_time() const {
  base::TimeDelta time = plan_end_time - base::Time::Now();
  return time.InMicroseconds() < 0 ? base::TimeDelta() : time;
}

int64 CellularDataPlan::remaining_minutes() const {
  return remaining_time().InMinutes();
}

int64 CellularDataPlan::remaining_data() const {
  int64 data = plan_data_bytes - data_bytes_used;
  return data < 0 ? 0 : data;
}

string16 CellularDataPlan::GetPlanExpiration() const {
  return TimeFormat::TimeRemaining(remaining_time());
}

////////////////////////////////////////////////////////////////////////////////
// CellularNetwork::Apn

void CellularNetwork::Apn::Set(const DictionaryValue& dict) {
  if (!dict.GetStringWithoutPathExpansion(kApnProperty, &apn))
    apn.clear();
  if (!dict.GetStringWithoutPathExpansion(kNetworkIdProperty, &network_id))
    network_id.clear();
  if (!dict.GetStringWithoutPathExpansion(kUsernameProperty, &username))
    username.clear();
  if (!dict.GetStringWithoutPathExpansion(kPasswordProperty, &password))
    password.clear();
}

////////////////////////////////////////////////////////////////////////////////
// CellularNetwork

CellularNetwork::~CellularNetwork() {
}

bool CellularNetwork::ParseValue(int index, const Value* value) {
  switch (index) {
    case PROPERTY_INDEX_ACTIVATION_STATE: {
      std::string activation_state_string;
      if (value->GetAsString(&activation_state_string)) {
        ActivationState prev_state = activation_state_;
        activation_state_ = ParseActivationState(activation_state_string);
        if (activation_state_ != prev_state)
          RefreshDataPlansIfNeeded();
        return true;
      }
      break;
    }
    case PROPERTY_INDEX_CELLULAR_APN: {
      if (value->IsType(Value::TYPE_DICTIONARY)) {
        apn_.Set(*static_cast<const DictionaryValue*>(value));
        return true;
      }
      break;
    }
    case PROPERTY_INDEX_CELLULAR_LAST_GOOD_APN: {
      if (value->IsType(Value::TYPE_DICTIONARY)) {
        last_good_apn_.Set(*static_cast<const DictionaryValue*>(value));
        return true;
      }
      break;
    }
    case PROPERTY_INDEX_NETWORK_TECHNOLOGY: {
      std::string network_technology_string;
      if (value->GetAsString(&network_technology_string)) {
        network_technology_ = ParseNetworkTechnology(network_technology_string);
        return true;
      }
      break;
    }
    case PROPERTY_INDEX_ROAMING_STATE: {
      std::string roaming_state_string;
      if (value->GetAsString(&roaming_state_string)) {
        roaming_state_ = ParseRoamingState(roaming_state_string);
        return true;
      }
      break;
    }
    case PROPERTY_INDEX_OPERATOR_NAME:
      return value->GetAsString(&operator_name_);
    case PROPERTY_INDEX_OPERATOR_CODE:
      return value->GetAsString(&operator_code_);
    case PROPERTY_INDEX_SERVING_OPERATOR: {
      if (value->IsType(Value::TYPE_DICTIONARY)) {
        const DictionaryValue *dict =
            static_cast<const DictionaryValue*>(value);
        operator_code_.clear();
        operator_country_.clear();
        operator_name_.clear();
        dict->GetStringWithoutPathExpansion(kOperatorNameKey,
                                            &operator_name_);
        dict->GetStringWithoutPathExpansion(kOperatorCodeKey,
                                            &operator_code_);
        dict->GetStringWithoutPathExpansion(kOperatorCountryKey,
                                            &operator_country_);
        return true;
      }
      break;
    }
    case PROPERTY_INDEX_PAYMENT_URL:
      return value->GetAsString(&payment_url_);
    case PROPERTY_INDEX_USAGE_URL:
      return value->GetAsString(&usage_url_);
    case PROPERTY_INDEX_STATE: {
      // Save previous state before calling WirelessNetwork::ParseValue.
      ConnectionState prev_state = state_;
      if (WirelessNetwork::ParseValue(index, value)) {
        if (state_ != prev_state)
          RefreshDataPlansIfNeeded();
        return true;
      }
      break;
    }
    case PROPERTY_INDEX_CONNECTIVITY_STATE: {
      // Save previous state before calling WirelessNetwork::ParseValue.
      ConnectivityState prev_state = connectivity_state_;
      if (WirelessNetwork::ParseValue(index, value)) {
        if (connectivity_state_ != prev_state)
          RefreshDataPlansIfNeeded();
        return true;
      }
      break;
    }
    default:
      return WirelessNetwork::ParseValue(index, value);
  }
  return false;
}

bool CellularNetwork::StartActivation() const {
  if (!EnsureCrosLoaded())
    return false;
  return ActivateCellularModem(service_path().c_str(), NULL);
}

void CellularNetwork::RefreshDataPlansIfNeeded() const {
  if (!EnsureCrosLoaded())
    return;
  if (connected() && activated())
    RequestCellularDataPlanUpdate(service_path().c_str());
}

void CellularNetwork::SetApn(const Apn& apn) {
  if (!EnsureCrosLoaded())
    return;

  if (!apn.apn.empty()) {
    DictionaryValue value;
    value.SetString(kApnProperty, apn.apn);
    value.SetString(kNetworkIdProperty, apn.network_id);
    value.SetString(kUsernameProperty, apn.username);
    value.SetString(kPasswordProperty, apn.password);
    SetValueProperty(kCellularApnProperty, &value);
  } else {
    ClearProperty(kCellularApnProperty);
  }
}

bool CellularNetwork::SupportsDataPlan() const {
  // TODO(nkostylev): Are there cases when only one of this is defined?
  return !usage_url().empty() || !payment_url().empty();
}

std::string CellularNetwork::GetNetworkTechnologyString() const {
  // No need to localize these cellular technology abbreviations.
  switch (network_technology_) {
    case NETWORK_TECHNOLOGY_1XRTT:
      return "1xRTT";
      break;
    case NETWORK_TECHNOLOGY_EVDO:
      return "EVDO";
      break;
    case NETWORK_TECHNOLOGY_GPRS:
      return "GPRS";
      break;
    case NETWORK_TECHNOLOGY_EDGE:
      return "EDGE";
      break;
    case NETWORK_TECHNOLOGY_UMTS:
      return "UMTS";
      break;
    case NETWORK_TECHNOLOGY_HSPA:
      return "HSPA";
      break;
    case NETWORK_TECHNOLOGY_HSPA_PLUS:
      return "HSPA Plus";
      break;
    case NETWORK_TECHNOLOGY_LTE:
      return "LTE";
      break;
    case NETWORK_TECHNOLOGY_LTE_ADVANCED:
      return "LTE Advanced";
      break;
    default:
      return l10n_util::GetStringUTF8(
          IDS_CHROMEOS_NETWORK_CELLULAR_TECHNOLOGY_UNKNOWN);
      break;
  }
}

std::string CellularNetwork::GetConnectivityStateString() const {
  // These strings do not appear in the UI, so no need to localize them
  switch (connectivity_state_) {
    case CONN_STATE_UNRESTRICTED:
      return "unrestricted";
      break;
    case CONN_STATE_RESTRICTED:
      return "restricted";
      break;
    case CONN_STATE_NONE:
      return "none";
      break;
    case CONN_STATE_UNKNOWN:
    default:
      return "unknown";
  }
}

std::string CellularNetwork::ActivationStateToString(
    ActivationState activation_state) {
  switch (activation_state) {
    case ACTIVATION_STATE_ACTIVATED:
      return l10n_util::GetStringUTF8(
          IDS_CHROMEOS_NETWORK_ACTIVATION_STATE_ACTIVATED);
      break;
    case ACTIVATION_STATE_ACTIVATING:
      return l10n_util::GetStringUTF8(
          IDS_CHROMEOS_NETWORK_ACTIVATION_STATE_ACTIVATING);
      break;
    case ACTIVATION_STATE_NOT_ACTIVATED:
      return l10n_util::GetStringUTF8(
          IDS_CHROMEOS_NETWORK_ACTIVATION_STATE_NOT_ACTIVATED);
      break;
    case ACTIVATION_STATE_PARTIALLY_ACTIVATED:
      return l10n_util::GetStringUTF8(
          IDS_CHROMEOS_NETWORK_ACTIVATION_STATE_PARTIALLY_ACTIVATED);
      break;
    default:
      return l10n_util::GetStringUTF8(
          IDS_CHROMEOS_NETWORK_ACTIVATION_STATE_UNKNOWN);
      break;
  }
}

std::string CellularNetwork::GetActivationStateString() const {
  return ActivationStateToString(this->activation_state_);
}

std::string CellularNetwork::GetRoamingStateString() const {
  switch (this->roaming_state_) {
    case ROAMING_STATE_HOME:
      return l10n_util::GetStringUTF8(
          IDS_CHROMEOS_NETWORK_ROAMING_STATE_HOME);
      break;
    case ROAMING_STATE_ROAMING:
      return l10n_util::GetStringUTF8(
          IDS_CHROMEOS_NETWORK_ROAMING_STATE_ROAMING);
      break;
    default:
      return l10n_util::GetStringUTF8(
          IDS_CHROMEOS_NETWORK_ROAMING_STATE_UNKNOWN);
      break;
  }
}

////////////////////////////////////////////////////////////////////////////////
// WifiNetwork

// Called from ParseNetwork after calling ParseInfo.
void WifiNetwork::CalculateUniqueId() {
  ConnectionSecurity encryption = encryption_;
  // Flimflam treats wpa and rsn as psk internally, so convert those types
  // to psk for unique naming.
  if (encryption == SECURITY_WPA || encryption == SECURITY_RSN)
    encryption = SECURITY_PSK;
  std::string security = std::string(SecurityToString(encryption));
  unique_id_ = security + "|" + name_;
}

bool WifiNetwork::SetSsid(const std::string& ssid) {
  // Detects encoding and convert to UTF-8.
  std::string ssid_utf8;
  if (!IsStringUTF8(ssid)) {
    std::string encoding;
    if (base::DetectEncoding(ssid, &encoding)) {
      if (!base::ConvertToUtf8AndNormalize(ssid, encoding, &ssid_utf8)) {
        ssid_utf8.clear();
      }
    }
  }

  if (ssid_utf8.empty())
    SetName(ssid);
  else
    SetName(ssid_utf8);

  return true;
}

bool WifiNetwork::SetHexSsid(const std::string& ssid_hex) {
  // Converts ascii hex dump (eg. "49656c6c6f") to string (eg. "Hello").
  std::vector<uint8> ssid_raw;
  if (!base::HexStringToBytes(ssid_hex, &ssid_raw)) {
    LOG(ERROR) << "Iligal hex char is found in WiFi.HexSSID.";
    ssid_raw.clear();
    return false;
  }

  return SetSsid(std::string(ssid_raw.begin(), ssid_raw.end()));
}

bool WifiNetwork::ParseValue(int index, const Value* value) {
  switch (index) {
    case PROPERTY_INDEX_WIFI_HEX_SSID: {
      std::string ssid_hex;
      if (!value->GetAsString(&ssid_hex))
        return false;

      SetHexSsid(ssid_hex);
      return true;
    }
    case PROPERTY_INDEX_NAME: {
      // Does not change network name when it was already set by WiFi.HexSSID.
      if (!name().empty())
        return true;
      else
        return WirelessNetwork::ParseValue(index, value);
    }
    case PROPERTY_INDEX_SECURITY: {
      std::string security_string;
      if (value->GetAsString(&security_string)) {
        encryption_ = ParseSecurity(security_string);
        return true;
      }
      break;
    }
    case PROPERTY_INDEX_PASSPHRASE: {
      std::string passphrase;
      if (value->GetAsString(&passphrase)) {
        // Only store the passphrase if we are the owner.
        // TODO(stevenjb): Remove this when chromium-os:12948 is resolved.
        if (chromeos::UserManager::Get()->current_user_is_owner())
          passphrase_ = passphrase;
        return true;
      }
      break;
    }
    case PROPERTY_INDEX_PASSPHRASE_REQUIRED:
      return value->GetAsBoolean(&passphrase_required_);
    case PROPERTY_INDEX_SAVE_CREDENTIALS:
      return value->GetAsBoolean(&save_credentials_);
    case PROPERTY_INDEX_IDENTITY:
      return value->GetAsString(&identity_);
    case PROPERTY_INDEX_CERT_PATH:
      return value->GetAsString(&cert_path_);
    case PROPERTY_INDEX_EAP_IDENTITY:
      return value->GetAsString(&eap_identity_);
    case PROPERTY_INDEX_EAP_METHOD: {
      std::string method;
      if (value->GetAsString(&method)) {
        eap_method_ = ParseEAPMethod(method);
        return true;
      }
      break;
    }
    case PROPERTY_INDEX_EAP_PHASE_2_AUTH: {
      std::string auth;
      if (value->GetAsString(&auth)) {
        eap_phase_2_auth_ = ParseEAPPhase2Auth(auth);
        return true;
      }
      break;
    }
    case PROPERTY_INDEX_EAP_ANONYMOUS_IDENTITY:
      return value->GetAsString(&eap_anonymous_identity_);
    case PROPERTY_INDEX_EAP_CERT_ID:
      return value->GetAsString(&eap_client_cert_pkcs11_id_);
    case PROPERTY_INDEX_EAP_CA_CERT_NSS:
      return value->GetAsString(&eap_server_ca_cert_nss_nickname_);
    case PROPERTY_INDEX_EAP_USE_SYSTEM_CAS:
      return value->GetAsBoolean(&eap_use_system_cas_);
    case PROPERTY_INDEX_EAP_PASSWORD:
      return value->GetAsString(&eap_passphrase_);
    case PROPERTY_INDEX_EAP_CLIENT_CERT:
    case PROPERTY_INDEX_EAP_CLIENT_CERT_NSS:
    case PROPERTY_INDEX_EAP_PRIVATE_KEY:
    case PROPERTY_INDEX_EAP_PRIVATE_KEY_PASSWORD:
    case PROPERTY_INDEX_EAP_KEY_ID:
    case PROPERTY_INDEX_EAP_CA_CERT:
    case PROPERTY_INDEX_EAP_CA_CERT_ID:
    case PROPERTY_INDEX_EAP_PIN:
    case PROPERTY_INDEX_EAP_KEY_MGMT:
      // These properties are currently not used in the UI.
      return true;
    default:
      return WirelessNetwork::ParseValue(index, value);
  }
  return false;
}

void WifiNetwork::ParseInfo(const DictionaryValue* info) {
  Network::ParseInfo(info);
  CalculateUniqueId();
}

const std::string& WifiNetwork::GetPassphrase() const {
  if (!user_passphrase_.empty())
    return user_passphrase_;
  return passphrase_;
}

void WifiNetwork::SetPassphrase(const std::string& passphrase) {
  // Set the user_passphrase_ only; passphrase_ stores the flimflam value.
  // If the user sets an empty passphrase, restore it to the passphrase
  // remembered by flimflam.
  if (!passphrase.empty()) {
    user_passphrase_ = passphrase;
    passphrase_ = passphrase;
  } else {
    user_passphrase_ = passphrase_;
  }
  // Send the change to flimflam. If the format is valid, it will propagate to
  // passphrase_ with a service update.
  SetOrClearStringProperty(kPassphraseProperty, passphrase, NULL);
}

void WifiNetwork::SetSaveCredentials(bool save_credentials) {
  SetBooleanProperty(kSaveCredentialsProperty, save_credentials,
                     &save_credentials_);
}

// See src/third_party/flimflam/doc/service-api.txt for properties that
// flimflam will forget when SaveCredentials is false.
void WifiNetwork::EraseCredentials() {
  WipeString(&passphrase_);
  WipeString(&user_passphrase_);
  WipeString(&eap_client_cert_pkcs11_id_);
  WipeString(&eap_identity_);
  WipeString(&eap_anonymous_identity_);
  WipeString(&eap_passphrase_);
}

void WifiNetwork::SetIdentity(const std::string& identity) {
  SetStringProperty(kIdentityProperty, identity, &identity_);
}

void WifiNetwork::SetCertPath(const std::string& cert_path) {
  SetStringProperty(kCertPathProperty, cert_path, &cert_path_);
}

void WifiNetwork::SetEAPMethod(EAPMethod method) {
  eap_method_ = method;
  switch (method) {
    case EAP_METHOD_PEAP:
      SetStringProperty(kEapMethodProperty, kEapMethodPEAP, NULL);
      break;
    case EAP_METHOD_TLS:
      SetStringProperty(kEapMethodProperty, kEapMethodTLS, NULL);
      break;
    case EAP_METHOD_TTLS:
      SetStringProperty(kEapMethodProperty, kEapMethodTTLS, NULL);
      break;
    case EAP_METHOD_LEAP:
      SetStringProperty(kEapMethodProperty, kEapMethodLEAP, NULL);
      break;
    default:
      ClearProperty(kEapMethodProperty);
      break;
  }
}

void WifiNetwork::SetEAPPhase2Auth(EAPPhase2Auth auth) {
  eap_phase_2_auth_ = auth;
  bool is_peap = (eap_method_ == EAP_METHOD_PEAP);
  switch (auth) {
    case EAP_PHASE_2_AUTH_AUTO:
      ClearProperty(kEapPhase2AuthProperty);
      break;
    case EAP_PHASE_2_AUTH_MD5:
      SetStringProperty(kEapPhase2AuthProperty,
                        is_peap ? kEapPhase2AuthPEAPMD5
                                : kEapPhase2AuthTTLSMD5,
                        NULL);
      break;
    case EAP_PHASE_2_AUTH_MSCHAPV2:
      SetStringProperty(kEapPhase2AuthProperty,
                        is_peap ? kEapPhase2AuthPEAPMSCHAPV2
                                : kEapPhase2AuthTTLSMSCHAPV2,
                        NULL);
      break;
    case EAP_PHASE_2_AUTH_MSCHAP:
      SetStringProperty(kEapPhase2AuthProperty, kEapPhase2AuthTTLSMSCHAP, NULL);
      break;
    case EAP_PHASE_2_AUTH_PAP:
      SetStringProperty(kEapPhase2AuthProperty, kEapPhase2AuthTTLSPAP, NULL);
      break;
    case EAP_PHASE_2_AUTH_CHAP:
      SetStringProperty(kEapPhase2AuthProperty, kEapPhase2AuthTTLSCHAP, NULL);
      break;
  }
}

void WifiNetwork::SetEAPServerCaCertNssNickname(
    const std::string& nss_nickname) {
  VLOG(1) << "SetEAPServerCaCertNssNickname " << nss_nickname;
  SetOrClearStringProperty(kEapCaCertNssProperty, nss_nickname,
                           &eap_server_ca_cert_nss_nickname_);
}

void WifiNetwork::SetEAPClientCertPkcs11Id(const std::string& pkcs11_id) {
  VLOG(1) << "SetEAPClientCertPkcs11Id " << pkcs11_id;
  SetOrClearStringProperty(kEapCertIDProperty, pkcs11_id,
                           &eap_client_cert_pkcs11_id_);
}

void WifiNetwork::SetEAPUseSystemCAs(bool use_system_cas) {
  SetBooleanProperty(kEapUseSystemCAsProperty, use_system_cas,
                     &eap_use_system_cas_);
}

void WifiNetwork::SetEAPIdentity(const std::string& identity) {
  SetOrClearStringProperty(kEapIdentityProperty, identity, &eap_identity_);
}

void WifiNetwork::SetEAPAnonymousIdentity(const std::string& identity) {
  SetOrClearStringProperty(kEapAnonymousIdentityProperty, identity,
                           &eap_anonymous_identity_);
}

void WifiNetwork::SetEAPPassphrase(const std::string& passphrase) {
  SetOrClearStringProperty(kEapPasswordProperty, passphrase, &eap_passphrase_);
}

std::string WifiNetwork::GetEncryptionString() const {
  switch (encryption_) {
    case SECURITY_UNKNOWN:
      break;
    case SECURITY_NONE:
      return "";
    case SECURITY_WEP:
      return "WEP";
    case SECURITY_WPA:
      return "WPA";
    case SECURITY_RSN:
      return "RSN";
    case SECURITY_8021X:
      return "8021X";
    case SECURITY_PSK:
      return "PSK";
  }
  return "Unknown";
}

bool WifiNetwork::IsPassphraseRequired() const {
  // TODO(stevenjb): Remove error_ tests when fixed in flimflam
  // (http://crosbug.com/10135).
  if (error_ == ERROR_BAD_PASSPHRASE || error_ == ERROR_BAD_WEPKEY)
    return true;
  // For 802.1x networks, configuration is required if connectable is false.
  if (encryption_ == SECURITY_8021X)
    return !connectable_;
  return passphrase_required_;
}

// Parse 'path' to determine if the certificate is stored in a pkcs#11 device.
// flimflam recognizes the string "SETTINGS:" to specify authentication
// parameters. 'key_id=' indicates that the certificate is stored in a pkcs#11
// device. See src/third_party/flimflam/files/doc/service-api.txt.
bool WifiNetwork::IsCertificateLoaded() const {
  static const std::string settings_string("SETTINGS:");
  static const std::string pkcs11_key("key_id");
  if (cert_path_.find(settings_string) == 0) {
    std::string::size_type idx = cert_path_.find(pkcs11_key);
    if (idx != std::string::npos)
      idx = cert_path_.find_first_not_of(kWhitespaceASCII,
                                         idx + pkcs11_key.length());
    if (idx != std::string::npos && cert_path_[idx] == '=')
      return true;
  }
  return false;
}

////////////////////////////////////////////////////////////////////////////////
// NetworkLibrary

class NetworkLibraryImpl : public NetworkLibrary  {
 public:
  NetworkLibraryImpl()
      : network_manager_monitor_(NULL),
        data_plan_monitor_(NULL),
        ethernet_(NULL),
        active_wifi_(NULL),
        active_cellular_(NULL),
        active_virtual_(NULL),
        available_devices_(0),
        enabled_devices_(0),
        connected_devices_(0),
        wifi_scanning_(false),
        offline_mode_(false),
        is_locked_(false),
        sim_operation_(SIM_OPERATION_NONE),
        notify_task_(NULL) {
    if (EnsureCrosLoaded()) {
      Init();
      network_manager_monitor_ =
          MonitorNetworkManager(&NetworkManagerStatusChangedHandler,
                                this);
      data_plan_monitor_ = MonitorCellularDataPlan(&DataPlanUpdateHandler,
                                                   this);
      network_login_observer_.reset(new NetworkLoginObserver(this));
    } else {
      InitTestData();
    }
  }

  virtual ~NetworkLibraryImpl() {
    network_manager_observers_.Clear();
    if (network_manager_monitor_)
      DisconnectPropertyChangeMonitor(network_manager_monitor_);
    data_plan_observers_.Clear();
    pin_operation_observers_.Clear();
    user_action_observers_.Clear();
    if (data_plan_monitor_)
      DisconnectDataPlanUpdateMonitor(data_plan_monitor_);
    STLDeleteValues(&network_observers_);
    STLDeleteValues(&network_device_observers_);
    ClearNetworks(true /*delete networks*/);
    ClearRememberedNetworks(true /*delete networks*/);
    STLDeleteValues(&data_plan_map_);
  }

  virtual void AddNetworkManagerObserver(NetworkManagerObserver* observer) {
    if (!network_manager_observers_.HasObserver(observer))
      network_manager_observers_.AddObserver(observer);
  }

  virtual void RemoveNetworkManagerObserver(NetworkManagerObserver* observer) {
    network_manager_observers_.RemoveObserver(observer);
  }

  virtual void AddNetworkObserver(const std::string& service_path,
                                  NetworkObserver* observer) {
    DCHECK(observer);
    if (!EnsureCrosLoaded())
      return;
    // First, add the observer to the callback map.
    NetworkObserverMap::iterator iter = network_observers_.find(service_path);
    NetworkObserverList* oblist;
    if (iter != network_observers_.end()) {
      oblist = iter->second;
    } else {
      oblist = new NetworkObserverList(this, service_path);
      network_observers_[service_path] = oblist;
    }
    if (!oblist->HasObserver(observer))
      oblist->AddObserver(observer);
  }

  virtual void RemoveNetworkObserver(const std::string& service_path,
                                     NetworkObserver* observer) {
    DCHECK(observer);
    DCHECK(service_path.size());
    NetworkObserverMap::iterator map_iter =
        network_observers_.find(service_path);
    if (map_iter != network_observers_.end()) {
      map_iter->second->RemoveObserver(observer);
      if (!map_iter->second->size()) {
        delete map_iter->second;
        network_observers_.erase(map_iter);
      }
    }
  }

  virtual void AddNetworkDeviceObserver(const std::string& device_path,
                                        NetworkDeviceObserver* observer) {
    DCHECK(observer);
    if (!EnsureCrosLoaded())
      return;
    // First, add the observer to the callback map.
    NetworkDeviceObserverMap::iterator iter =
        network_device_observers_.find(device_path);
    NetworkDeviceObserverList* oblist;
    if (iter != network_device_observers_.end()) {
      oblist = iter->second;
      if (!oblist->HasObserver(observer))
        oblist->AddObserver(observer);
    } else {
      LOG(WARNING) << "No NetworkDeviceObserverList found for "
                   << device_path;
    }
  }

  virtual void RemoveNetworkDeviceObserver(const std::string& device_path,
                                           NetworkDeviceObserver* observer) {
    DCHECK(observer);
    DCHECK(device_path.size());
    NetworkDeviceObserverMap::iterator map_iter =
        network_device_observers_.find(device_path);
    if (map_iter != network_device_observers_.end()) {
      map_iter->second->RemoveObserver(observer);
    }
  }

  virtual void RemoveObserverForAllNetworks(NetworkObserver* observer) {
    DCHECK(observer);
    NetworkObserverMap::iterator map_iter = network_observers_.begin();
    while (map_iter != network_observers_.end()) {
      map_iter->second->RemoveObserver(observer);
      if (!map_iter->second->size()) {
        delete map_iter->second;
        network_observers_.erase(map_iter++);
      } else {
        ++map_iter;
      }
    }
  }

  virtual void Lock() {
    if (is_locked_)
      return;
    is_locked_ = true;
    NotifyNetworkManagerChanged(true);  // Forced update.
  }

  virtual void Unlock() {
    DCHECK(is_locked_);
    if (!is_locked_)
      return;
    is_locked_ = false;
    NotifyNetworkManagerChanged(true);  // Forced update.
  }

  virtual bool IsLocked() {
    return is_locked_;
  }

  virtual void AddCellularDataPlanObserver(CellularDataPlanObserver* observer) {
    if (!data_plan_observers_.HasObserver(observer))
      data_plan_observers_.AddObserver(observer);
  }

  virtual void RemoveCellularDataPlanObserver(
      CellularDataPlanObserver* observer) {
    data_plan_observers_.RemoveObserver(observer);
  }

  virtual void AddPinOperationObserver(PinOperationObserver* observer) {
    if (!pin_operation_observers_.HasObserver(observer))
      pin_operation_observers_.AddObserver(observer);
  }

  virtual void RemovePinOperationObserver(PinOperationObserver* observer) {
    pin_operation_observers_.RemoveObserver(observer);
  }

  virtual void AddUserActionObserver(UserActionObserver* observer) {
    if (!user_action_observers_.HasObserver(observer))
      user_action_observers_.AddObserver(observer);
  }

  virtual void RemoveUserActionObserver(UserActionObserver* observer) {
    user_action_observers_.RemoveObserver(observer);
  }

  virtual const EthernetNetwork* ethernet_network() const { return ethernet_; }
  virtual bool ethernet_connecting() const {
    return ethernet_ ? ethernet_->connecting() : false;
  }
  virtual bool ethernet_connected() const {
    return ethernet_ ? ethernet_->connected() : false;
  }

  virtual const WifiNetwork* wifi_network() const { return active_wifi_; }
  virtual bool wifi_connecting() const {
    return active_wifi_ ? active_wifi_->connecting() : false;
  }
  virtual bool wifi_connected() const {
    return active_wifi_ ? active_wifi_->connected() : false;
  }

  virtual const CellularNetwork* cellular_network() const {
    return active_cellular_;
  }
  virtual bool cellular_connecting() const {
    return active_cellular_ ? active_cellular_->connecting() : false;
  }
  virtual bool cellular_connected() const {
    return active_cellular_ ? active_cellular_->connected() : false;
  }
  virtual const VirtualNetwork* virtual_network() const {
    return active_virtual_;
  }
  virtual bool virtual_network_connecting() const {
    return active_virtual_ ? active_virtual_->connecting() : false;
  }
  virtual bool virtual_network_connected() const {
    return active_virtual_ ? active_virtual_->connected() : false;
  }

  bool Connected() const {
    return ethernet_connected() || wifi_connected() || cellular_connected();
  }

  bool Connecting() const {
    return ethernet_connecting() || wifi_connecting() || cellular_connecting();
  }

  const std::string& IPAddress() const {
    // Returns IP address for the active network.
    // TODO(stevenjb): Fix this for VPNs. See chromium-os:13972.
    const Network* result = active_network();
    if (!result)
      result = connected_network();  // happens if we are connected to a VPN.
    if (!result)
      result = ethernet_;  // Use non active ethernet addr if no active network.
    if (result)
      return result->ip_address();
    static std::string null_address("0.0.0.0");
    return null_address;
  }

  virtual const WifiNetworkVector& wifi_networks() const {
    return wifi_networks_;
  }

  virtual const WifiNetworkVector& remembered_wifi_networks() const {
    return remembered_wifi_networks_;
  }

  virtual const CellularNetworkVector& cellular_networks() const {
    return cellular_networks_;
  }

  virtual const VirtualNetworkVector& virtual_networks() const {
    return virtual_networks_;
  }

  /////////////////////////////////////////////////////////////////////////////

  virtual const NetworkDevice* FindNetworkDeviceByPath(
      const std::string& path) const {
    NetworkDeviceMap::const_iterator iter = device_map_.find(path);
    if (iter != device_map_.end())
      return iter->second;
    LOG(WARNING) << "Device path not found: " << path;
    return NULL;
  }

  virtual const NetworkDevice* FindCellularDevice() const {
    for (NetworkDeviceMap::const_iterator iter = device_map_.begin();
         iter != device_map_.end(); ++iter) {
      if (iter->second->type() == TYPE_CELLULAR)
        return iter->second;
    }
    return NULL;
  }

  virtual const NetworkDevice* FindEthernetDevice() const {
    for (NetworkDeviceMap::const_iterator iter = device_map_.begin();
         iter != device_map_.end(); ++iter) {
      if (iter->second->type() == TYPE_ETHERNET)
        return iter->second;
    }
    return NULL;
  }

  virtual const NetworkDevice* FindWifiDevice() const {
    for (NetworkDeviceMap::const_iterator iter = device_map_.begin();
         iter != device_map_.end(); ++iter) {
      if (iter->second->type() == TYPE_WIFI)
        return iter->second;
    }
    return NULL;
  }

  virtual Network* FindNetworkByPath(const std::string& path) const {
    NetworkMap::const_iterator iter = network_map_.find(path);
    if (iter != network_map_.end())
      return iter->second;
    return NULL;
  }

  WirelessNetwork* FindWirelessNetworkByPath(const std::string& path) const {
    Network* network = FindNetworkByPath(path);
    if (network &&
        (network->type() == TYPE_WIFI || network->type() == TYPE_CELLULAR))
      return static_cast<WirelessNetwork*>(network);
    return NULL;
  }

  virtual WifiNetwork* FindWifiNetworkByPath(const std::string& path) const {
    Network* network = FindNetworkByPath(path);
    if (network && network->type() == TYPE_WIFI)
      return static_cast<WifiNetwork*>(network);
    return NULL;
  }

  virtual CellularNetwork* FindCellularNetworkByPath(
      const std::string& path) const {
    Network* network = FindNetworkByPath(path);
    if (network && network->type() == TYPE_CELLULAR)
      return static_cast<CellularNetwork*>(network);
    return NULL;
  }

  virtual VirtualNetwork* FindVirtualNetworkByPath(
      const std::string& path) const {
    Network* network = FindNetworkByPath(path);
    if (network && network->type() == TYPE_VPN)
      return static_cast<VirtualNetwork*>(network);
    return NULL;
  }

  virtual Network* FindNetworkFromRemembered(
      const Network* remembered) const {
    NetworkMap::const_iterator found =
        network_unique_id_map_.find(remembered->unique_id());
    if (found != network_unique_id_map_.end())
      return found->second;
    return NULL;
  }

  virtual const CellularDataPlanVector* GetDataPlans(
      const std::string& path) const {
    CellularDataPlanMap::const_iterator iter = data_plan_map_.find(path);
    if (iter == data_plan_map_.end())
      return NULL;
    // If we need a new plan, then ignore any data plans we have.
    CellularNetwork* cellular = FindCellularNetworkByPath(path);
    if (cellular && cellular->needs_new_plan())
      return NULL;
    return iter->second;
  }

  virtual const CellularDataPlan* GetSignificantDataPlan(
      const std::string& path) const {
    const CellularDataPlanVector* plans = GetDataPlans(path);
    if (plans)
      return GetSignificantDataPlanFromVector(plans);
    return NULL;
  }

  /////////////////////////////////////////////////////////////////////////////

  virtual void ChangePin(const std::string& old_pin,
                         const std::string& new_pin) {
    const NetworkDevice* cellular = FindCellularDevice();
    if (!cellular) {
      NOTREACHED() << "Calling ChangePin method w/o cellular device.";
      return;
    }
    sim_operation_ = SIM_OPERATION_CHANGE_PIN;
    chromeos::RequestChangePin(cellular->device_path().c_str(),
                               old_pin.c_str(), new_pin.c_str(),
                               PinOperationCallback, this);
  }

  virtual void ChangeRequirePin(bool require_pin,
                                const std::string& pin) {
    VLOG(1) << "ChangeRequirePin require_pin: " << require_pin
            << " pin: " << pin;
    const NetworkDevice* cellular = FindCellularDevice();
    if (!cellular) {
      NOTREACHED() << "Calling ChangeRequirePin method w/o cellular device.";
      return;
    }
    sim_operation_ = SIM_OPERATION_CHANGE_REQUIRE_PIN;
    chromeos::RequestRequirePin(cellular->device_path().c_str(),
                                pin.c_str(), require_pin,
                                PinOperationCallback, this);
  }

  virtual void EnterPin(const std::string& pin) {
    const NetworkDevice* cellular = FindCellularDevice();
    if (!cellular) {
      NOTREACHED() << "Calling EnterPin method w/o cellular device.";
      return;
    }
    sim_operation_ = SIM_OPERATION_ENTER_PIN;
    chromeos::RequestEnterPin(cellular->device_path().c_str(),
                              pin.c_str(),
                              PinOperationCallback, this);
  }

  virtual void UnblockPin(const std::string& puk,
                          const std::string& new_pin) {
    const NetworkDevice* cellular = FindCellularDevice();
    if (!cellular) {
      NOTREACHED() << "Calling UnblockPin method w/o cellular device.";
      return;
    }
    sim_operation_ = SIM_OPERATION_UNBLOCK_PIN;
    chromeos::RequestUnblockPin(cellular->device_path().c_str(),
                                puk.c_str(), new_pin.c_str(),
                                PinOperationCallback, this);
  }

  static void PinOperationCallback(void* object,
                                   const char* path,
                                   NetworkMethodErrorType error,
                                   const char* error_message) {
    NetworkLibraryImpl* networklib = static_cast<NetworkLibraryImpl*>(object);
    DCHECK(networklib);
    PinOperationError pin_error;
    VLOG(1) << "PinOperationCallback, error: " << error
            << " error_msg: " << error_message;
    if (error == chromeos::NETWORK_METHOD_ERROR_NONE) {
      pin_error = PIN_ERROR_NONE;
      VLOG(1) << "Pin operation completed successfuly";
      // TODO(nkostylev): Might be cleaned up and exposed in flimflam API.
      // http://crosbug.com/14253
      // Since this option state is not exposed we have to update it manually.
      networklib->FlipSimPinRequiredStateIfNeeded();
    } else {
      if (error_message &&
            (strcmp(error_message, kErrorIncorrectPinMsg) == 0 ||
             strcmp(error_message, kErrorPinRequiredMsg) == 0)) {
        pin_error = PIN_ERROR_INCORRECT_CODE;
      } else if (error_message &&
                 strcmp(error_message, kErrorPinBlockedMsg) == 0) {
        pin_error = PIN_ERROR_BLOCKED;
      } else {
        pin_error = PIN_ERROR_UNKNOWN;
        NOTREACHED() << "Unknown PIN error: " << error_message;
      }
    }
    networklib->NotifyPinOperationCompleted(pin_error);
  }

  virtual void RequestCellularScan() {
    const NetworkDevice* cellular = FindCellularDevice();
    if (!cellular) {
      NOTREACHED() << "Calling RequestCellularScan method w/o cellular device.";
      return;
    }
    chromeos::ProposeScan(cellular->device_path().c_str());
  }

  virtual void RequestCellularRegister(const std::string& network_id) {
    const NetworkDevice* cellular = FindCellularDevice();
    if (!cellular) {
      NOTREACHED() << "Calling CellularRegister method w/o cellular device.";
      return;
    }
    chromeos::RequestCellularRegister(cellular->device_path().c_str(),
                                      network_id.c_str(),
                                      CellularRegisterCallback,
                                      this);
  }

  static void CellularRegisterCallback(void* object,
                                       const char* path,
                                       NetworkMethodErrorType error,
                                       const char* error_message) {
    NetworkLibraryImpl* networklib = static_cast<NetworkLibraryImpl*>(object);
    DCHECK(networklib);
    // TODO(dpolukhin): Notify observers about network registration status
    // but not UI doesn't assume such notification so just ignore result.
  }

  virtual void SetCellularDataRoamingAllowed(bool new_value) {
    const NetworkDevice* cellular = FindCellularDevice();
    if (!cellular) {
      NOTREACHED() << "Calling SetCellularDataRoamingAllowed method "
                      "w/o cellular device.";
      return;
    }
    scoped_ptr<Value> value(Value::CreateBooleanValue(new_value));
    chromeos::SetNetworkDeviceProperty(cellular->device_path().c_str(),
                                       kCellularAllowRoamingProperty,
                                       value.get());
  }

  /////////////////////////////////////////////////////////////////////////////

  virtual void RequestNetworkScan() {
    if (EnsureCrosLoaded()) {
      if (wifi_enabled()) {
        wifi_scanning_ = true;  // Cleared when updates are received.
        chromeos::RequestNetworkScan(kTypeWifi);
        RequestRememberedNetworksUpdate();
      }
      if (cellular_network())
        cellular_network()->RefreshDataPlansIfNeeded();
    }
  }

  virtual bool GetWifiAccessPoints(WifiAccessPointVector* result) {
    if (!EnsureCrosLoaded())
      return false;
    CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    DeviceNetworkList* network_list = GetDeviceNetworkList();
    if (network_list == NULL)
      return false;
    result->clear();
    result->reserve(network_list->network_size);
    const base::Time now = base::Time::Now();
    for (size_t i = 0; i < network_list->network_size; ++i) {
      DCHECK(network_list->networks[i].address);
      DCHECK(network_list->networks[i].name);
      WifiAccessPoint ap;
      ap.mac_address = SafeString(network_list->networks[i].address);
      ap.name = SafeString(network_list->networks[i].name);
      ap.timestamp = now -
          base::TimeDelta::FromSeconds(network_list->networks[i].age_seconds);
      ap.signal_strength = network_list->networks[i].strength;
      ap.channel = network_list->networks[i].channel;
      result->push_back(ap);
    }
    FreeDeviceNetworkList(network_list);
    return true;
  }

  static void NetworkConnectCallback(void *object,
                                     const char *path,
                                     NetworkMethodErrorType error,
                                     const char* error_message) {
    NetworkLibraryImpl* networklib = static_cast<NetworkLibraryImpl*>(object);
    DCHECK(networklib);

    Network* network = networklib->FindNetworkByPath(path);
    if (!network) {
      LOG(ERROR) << "No network for path: " << path;
      return;
    }

    if (error != NETWORK_METHOD_ERROR_NONE) {
      LOG(WARNING) << "Error from ServiceConnect callback for: "
                   << network->name()
                   << " Error: " << error << " Message: " << error_message;
      if (error_message &&
          strcmp(error_message, kErrorPassphraseRequiredMsg) == 0) {
        // This will trigger the connection failed notification.
        // TODO(stevenjb): Remove if chromium-os:13203 gets fixed.
        network->set_state(STATE_FAILURE);
        network->set_error(ERROR_BAD_PASSPHRASE);
        networklib->NotifyNetworkManagerChanged(true);  // Forced update.
      }
      return;
    }

    VLOG(1) << "Connected to service: " << network->name();

    // Update local cache and notify listeners.
    if (network->type() == TYPE_WIFI) {
      WifiNetwork* wifi = static_cast<WifiNetwork *>(network);
      // If the user asked not to save credentials, flimflam will have
      // forgotten them.  Wipe our cache as well.
      if (!wifi->save_credentials()) {
        wifi->EraseCredentials();
      }
      networklib->active_wifi_ = wifi;
    } else if (network->type() == TYPE_CELLULAR) {
      networklib->active_cellular_ = static_cast<CellularNetwork *>(network);
    } else if (network->type() == TYPE_VPN) {
      networklib->active_virtual_ = static_cast<VirtualNetwork *>(network);
    } else {
      LOG(ERROR) << "Network of unexpected type: " << network->type();
    }

    // If we succeed, this network will be remembered; request an update.
    // TODO(stevenjb): flimflam should do this automatically.
    networklib->RequestRememberedNetworksUpdate();
    // Notify observers.
    networklib->NotifyNetworkManagerChanged(true);  // Forced update.
    networklib->NotifyUserConnectionInitiated(network);
  }


  void CallConnectToNetwork(Network* network) {
    DCHECK(network);
    if (!EnsureCrosLoaded() || !network)
      return;
    // In order to be certain to trigger any notifications, set the connecting
    // state locally and notify observers. Otherwise there might be a state
    // change without a forced notify.
    network->set_connecting(true);
    NotifyNetworkManagerChanged(true);  // Forced update.
    RequestNetworkServiceConnect(network->service_path().c_str(),
                                 NetworkConnectCallback, this);
  }

  virtual void ConnectToWifiNetwork(WifiNetwork* wifi) {
    // This will happen if a network resets or gets out of range.
    if (wifi->user_passphrase_ != wifi->passphrase_ ||
        wifi->passphrase_required())
      wifi->SetPassphrase(wifi->user_passphrase_);
    CallConnectToNetwork(wifi);
  }

  // Use this to connect to a wifi network by service path.
  virtual void ConnectToWifiNetwork(const std::string& service_path) {
    WifiNetwork* wifi = FindWifiNetworkByPath(service_path);
    if (!wifi) {
      LOG(WARNING) << "Attempt to connect to non existing network: "
                   << service_path;
      return;
    }
    ConnectToWifiNetwork(wifi);
  }

  // Use this to connect to an unlisted wifi network.
  // This needs to request information about the named service.
  // The connection attempt will occur in the callback.
  virtual void ConnectToWifiNetwork(ConnectionSecurity security,
                                    const std::string& ssid,
                                    const std::string& passphrase,
                                    const std::string& identity,
                                    const std::string& certpath) {
    if (!EnsureCrosLoaded())
      return;
    // Store the connection data to be used by the callback.
    connect_data_.service_name = ssid;
    connect_data_.passphrase = passphrase;
    connect_data_.identity = identity;
    connect_data_.cert_path = certpath;
    // Asynchronously request service properties and call
    // WifiServiceUpdateAndConnect.
    RequestHiddenWifiNetwork(ssid.c_str(),
                             SecurityToString(security),
                             WifiServiceUpdateAndConnect,
                             this);
  }

  // Callback
  static void WifiServiceUpdateAndConnect(void* object,
                                          const char* service_path,
                                          const Value* info) {
    NetworkLibraryImpl* networklib = static_cast<NetworkLibraryImpl*>(object);
    DCHECK(networklib);
    if (service_path && info) {
      DCHECK_EQ(info->GetType(), Value::TYPE_DICTIONARY);
      const DictionaryValue* dict = static_cast<const DictionaryValue*>(info);
      Network* network =
          networklib->ParseNetwork(std::string(service_path), dict);
      DCHECK(network->type() == TYPE_WIFI);
      networklib->ConnectToWifiNetworkUsingConnectData(
          static_cast<WifiNetwork*>(network));
    }
  }

  void ConnectToWifiNetworkUsingConnectData(WifiNetwork* wifi) {
    ConnectData& data = connect_data_;
    if (wifi->name() != data.service_name) {
      LOG(WARNING) << "Wifi network name does not match ConnectData: "
                   << wifi->name() << " != " << data.service_name;
      return;
    }
    wifi->set_added(true);
    wifi->SetIdentity(data.identity);
    wifi->SetPassphrase(data.passphrase);
    if (!data.cert_path.empty())
      wifi->SetCertPath(data.cert_path);

    ConnectToWifiNetwork(wifi);
  }

  virtual void ConnectToCellularNetwork(CellularNetwork* cellular) {
    CallConnectToNetwork(cellular);
  }

  // Records information that cellular play payment had happened.
  virtual void SignalCellularPlanPayment() {
    DCHECK(!HasRecentCellularPlanPayment());
    cellular_plan_payment_time_ = base::Time::Now();
  }

  // Returns true if cellular plan payment had been recorded recently.
  virtual bool HasRecentCellularPlanPayment() {
    return (base::Time::Now() -
            cellular_plan_payment_time_).InHours() < kRecentPlanPaymentHours;
  }

  virtual void ConnectToVirtualNetwork(VirtualNetwork* vpn) {
    CallConnectToNetwork(vpn);
  }

  virtual void ConnectToVirtualNetworkPSK(
      const std::string& service_name,
      const std::string& server,
      const std::string& psk,
      const std::string& username,
      const std::string& user_passphrase) {
    if (!EnsureCrosLoaded())
      return;
    // Store the connection data to be used by the callback.
    connect_data_.service_name = service_name;
    connect_data_.psk_key = psk;
    connect_data_.server_hostname = server;
    connect_data_.identity = username;
    connect_data_.passphrase = user_passphrase;
    RequestVirtualNetwork(service_name.c_str(),
                          server.c_str(),
                          kProviderL2tpIpsec,
                          VPNServiceUpdateAndConnect,
                          this);
  }

  // Callback
  static void VPNServiceUpdateAndConnect(void* object,
                                         const char* service_path,
                                         const Value* info) {
    NetworkLibraryImpl* networklib = static_cast<NetworkLibraryImpl*>(object);
    DCHECK(networklib);
    if (service_path && info) {
      VLOG(1) << "Connecting to new VPN Service: " << service_path;
      DCHECK_EQ(info->GetType(), Value::TYPE_DICTIONARY);
      const DictionaryValue* dict = static_cast<const DictionaryValue*>(info);
      Network* network =
          networklib->ParseNetwork(std::string(service_path), dict);
      DCHECK(network->type() == TYPE_VPN);
      networklib->ConnectToVirtualNetworkUsingConnectData(
          static_cast<VirtualNetwork*>(network));
    } else {
      LOG(WARNING) << "Unable to create VPN Service: " << service_path;
    }
  }

  void ConnectToVirtualNetworkUsingConnectData(VirtualNetwork* vpn) {
    ConnectData& data = connect_data_;
    if (vpn->name() != data.service_name) {
      LOG(WARNING) << "Virtual network name does not match ConnectData: "
                   << vpn->name() << " != " << data.service_name;
      return;
    }

    vpn->set_added(true);
    vpn->set_server_hostname(data.server_hostname);
    vpn->SetCACert("");
    vpn->SetUserCert("");
    vpn->SetUserCertKey("");
    vpn->SetPSKPassphrase(data.psk_key);
    vpn->SetUsername(data.identity);
    vpn->SetUserPassphrase(data.passphrase);

    CallConnectToNetwork(vpn);
  }

  virtual void DisconnectFromNetwork(const Network* network) {
    DCHECK(network);
    if (!EnsureCrosLoaded() || !network)
      return;
    VLOG(1) << "Disconnect from network: " << network->service_path();
    if (chromeos::DisconnectFromNetwork(network->service_path().c_str())) {
      // Update local cache and notify listeners.
      Network* found_network = FindNetworkByPath(network->service_path());
      if (found_network) {
        found_network->set_connected(false);
        if (found_network == active_wifi_)
          active_wifi_ = NULL;
        else if (found_network == active_cellular_)
          active_cellular_ = NULL;
        else if (found_network == active_virtual_)
          active_virtual_ = NULL;
      }
      NotifyNetworkManagerChanged(true);  // Forced update.
    }
  }

  virtual void ForgetWifiNetwork(const std::string& service_path) {
    if (!EnsureCrosLoaded())
      return;
    DeleteRememberedService(service_path.c_str());
    DeleteRememberedWifiNetwork(service_path);
    NotifyNetworkManagerChanged(true);  // Forced update.
  }

  virtual std::string GetCellularHomeCarrierId() const {
    std::string carrier_id;
    const NetworkDevice* cellular = FindCellularDevice();
    if (cellular) {
      return cellular->home_provider_id();

    }
    return carrier_id;
  }

  virtual bool ethernet_available() const {
    return available_devices_ & (1 << TYPE_ETHERNET);
  }
  virtual bool wifi_available() const {
    return available_devices_ & (1 << TYPE_WIFI);
  }
  virtual bool cellular_available() const {
    return available_devices_ & (1 << TYPE_CELLULAR);
  }

  virtual bool ethernet_enabled() const {
    return enabled_devices_ & (1 << TYPE_ETHERNET);
  }
  virtual bool wifi_enabled() const {
    return enabled_devices_ & (1 << TYPE_WIFI);
  }
  virtual bool cellular_enabled() const {
    return enabled_devices_ & (1 << TYPE_CELLULAR);
  }

  virtual bool wifi_scanning() const {
    return wifi_scanning_;
  }

  virtual bool offline_mode() const { return offline_mode_; }

  // Note: This does not include any virtual networks.
  virtual const Network* active_network() const {
    // Use flimflam's ordering of the services to determine what the active
    // network is (i.e. don't assume priority of network types).
    Network* result = NULL;
    if (ethernet_ && ethernet_->is_active())
      result = ethernet_;
    if ((active_wifi_ && active_wifi_->is_active()) &&
        (!result ||
         active_wifi_->priority_order_ < result->priority_order_))
      result = active_wifi_;
    if ((active_cellular_ && active_cellular_->is_active()) &&
        (!result ||
         active_cellular_->priority_order_ < result->priority_order_))
      result = active_cellular_;
    return result;
  }

  virtual const Network* connected_network() const {
    // Use flimflam's ordering of the services to determine what the connected
    // network is (i.e. don't assume priority of network types).
    Network* result = NULL;
    if (ethernet_ && ethernet_->connected())
      result = ethernet_;
    if ((active_wifi_ && active_wifi_->connected()) &&
        (!result ||
         active_wifi_->priority_order_ < result->priority_order_))
      result = active_wifi_;
    if ((active_cellular_ && active_cellular_->connected()) &&
        (!result ||
         active_cellular_->priority_order_ < result->priority_order_))
      result = active_cellular_;
    return result;
  }

  virtual void EnableEthernetNetworkDevice(bool enable) {
    if (is_locked_)
      return;
    EnableNetworkDeviceType(TYPE_ETHERNET, enable);
  }

  virtual void EnableWifiNetworkDevice(bool enable) {
    if (is_locked_)
      return;
    EnableNetworkDeviceType(TYPE_WIFI, enable);
  }

  virtual void EnableCellularNetworkDevice(bool enable) {
    if (is_locked_)
      return;
    EnableNetworkDeviceType(TYPE_CELLULAR, enable);
  }

  virtual void EnableOfflineMode(bool enable) {
    if (!EnsureCrosLoaded())
      return;

    // If network device is already enabled/disabled, then don't do anything.
    if (enable && offline_mode_) {
      VLOG(1) << "Trying to enable offline mode when it's already enabled.";
      return;
    }
    if (!enable && !offline_mode_) {
      VLOG(1) << "Trying to disable offline mode when it's already disabled.";
      return;
    }

    if (SetOfflineMode(enable)) {
      offline_mode_ = enable;
    }
  }

  virtual NetworkIPConfigVector GetIPConfigs(const std::string& device_path,
                                             std::string* hardware_address,
                                             HardwareAddressFormat format) {
    DCHECK(hardware_address);
    hardware_address->clear();
    NetworkIPConfigVector ipconfig_vector;
    if (EnsureCrosLoaded() && !device_path.empty()) {
      IPConfigStatus* ipconfig_status = ListIPConfigs(device_path.c_str());
      if (ipconfig_status) {
        for (int i = 0; i < ipconfig_status->size; i++) {
          IPConfig ipconfig = ipconfig_status->ips[i];
          ipconfig_vector.push_back(
              NetworkIPConfig(device_path, ipconfig.type, ipconfig.address,
                              ipconfig.netmask, ipconfig.gateway,
                              ipconfig.name_servers));
        }
        *hardware_address = ipconfig_status->hardware_address;
        FreeIPConfigStatus(ipconfig_status);
        // Sort the list of ip configs by type.
        std::sort(ipconfig_vector.begin(), ipconfig_vector.end());
      }
    }

    for (size_t i = 0; i < hardware_address->size(); ++i)
      (*hardware_address)[i] = toupper((*hardware_address)[i]);
    if (format == FORMAT_COLON_SEPARATED_HEX) {
      if (hardware_address->size() % 2 == 0) {
        std::string output;
        for (size_t i = 0; i < hardware_address->size(); ++i) {
          if ((i != 0) && (i % 2 == 0))
            output.push_back(':');
          output.push_back((*hardware_address)[i]);
        }
        *hardware_address = output;
      }
    } else {
      DCHECK(format == FORMAT_RAW_HEX);
    }
    return ipconfig_vector;
  }

 private:

  typedef std::map<std::string, Network*> NetworkMap;
  typedef std::map<std::string, int> PriorityMap;
  typedef std::map<std::string, NetworkDevice*> NetworkDeviceMap;
  typedef std::map<std::string, CellularDataPlanVector*> CellularDataPlanMap;

  class NetworkObserverList : public ObserverList<NetworkObserver> {
   public:
    NetworkObserverList(NetworkLibraryImpl* library,
                        const std::string& service_path) {
      network_monitor_ = MonitorNetworkService(&NetworkStatusChangedHandler,
                                               service_path.c_str(),
                                               library);
    }

    virtual ~NetworkObserverList() {
      if (network_monitor_)
        DisconnectPropertyChangeMonitor(network_monitor_);
    }

   private:
    static void NetworkStatusChangedHandler(void* object,
                                            const char* path,
                                            const char* key,
                                            const Value* value) {
      NetworkLibraryImpl* networklib = static_cast<NetworkLibraryImpl*>(object);
      DCHECK(networklib);
      networklib->UpdateNetworkStatus(path, key, value);
    }
    PropertyChangeMonitor network_monitor_;
    DISALLOW_COPY_AND_ASSIGN(NetworkObserverList);
  };

  typedef std::map<std::string, NetworkObserverList*> NetworkObserverMap;

  class NetworkDeviceObserverList : public ObserverList<NetworkDeviceObserver> {
   public:
    NetworkDeviceObserverList(NetworkLibraryImpl* library,
                              const std::string& device_path) {
      device_monitor_ = MonitorNetworkDevice(
          &NetworkDevicePropertyChangedHandler,
          device_path.c_str(),
          library);
    }

    virtual ~NetworkDeviceObserverList() {
      if (device_monitor_)
        DisconnectPropertyChangeMonitor(device_monitor_);
    }

   private:
    static void NetworkDevicePropertyChangedHandler(void* object,
                                                    const char* path,
                                                    const char* key,
                                                    const Value* value) {
      NetworkLibraryImpl* networklib = static_cast<NetworkLibraryImpl*>(object);
      DCHECK(networklib);
      networklib->UpdateNetworkDeviceStatus(path, key, value);
    }
    PropertyChangeMonitor device_monitor_;
    DISALLOW_COPY_AND_ASSIGN(NetworkDeviceObserverList);
  };

  typedef std::map<std::string, NetworkDeviceObserverList*>
      NetworkDeviceObserverMap;

  ////////////////////////////////////////////////////////////////////////////
  // Callbacks.

  static void NetworkManagerStatusChangedHandler(void* object,
                                                 const char* path,
                                                 const char* key,
                                                 const Value* value) {
    NetworkLibraryImpl* networklib = static_cast<NetworkLibraryImpl*>(object);
    DCHECK(networklib);
    networklib->NetworkManagerStatusChanged(key, value);
  }

  // This processes all Manager update messages.
  void NetworkManagerStatusChanged(const char* key, const Value* value) {
    CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    base::TimeTicks start = base::TimeTicks::Now();
    VLOG(1) << "NetworkManagerStatusChanged: KEY=" << key;
    if (!key)
      return;
    int index = property_index_parser().Get(std::string(key));
    switch (index) {
      case PROPERTY_INDEX_STATE:
        // Currently we ignore the network manager state.
        break;
      case PROPERTY_INDEX_AVAILABLE_TECHNOLOGIES: {
        DCHECK_EQ(value->GetType(), Value::TYPE_LIST);
        const ListValue* vlist = static_cast<const ListValue*>(value);
        UpdateAvailableTechnologies(vlist);
        break;
      }
      case PROPERTY_INDEX_ENABLED_TECHNOLOGIES: {
        DCHECK_EQ(value->GetType(), Value::TYPE_LIST);
        const ListValue* vlist = static_cast<const ListValue*>(value);
        UpdateEnabledTechnologies(vlist);
        break;
      }
      case PROPERTY_INDEX_CONNECTED_TECHNOLOGIES: {
        DCHECK_EQ(value->GetType(), Value::TYPE_LIST);
        const ListValue* vlist = static_cast<const ListValue*>(value);
        UpdateConnectedTechnologies(vlist);
        break;
      }
      case PROPERTY_INDEX_DEFAULT_TECHNOLOGY:
        // Currently we ignore DefaultTechnology.
        break;
      case PROPERTY_INDEX_OFFLINE_MODE: {
        DCHECK_EQ(value->GetType(), Value::TYPE_BOOLEAN);
        value->GetAsBoolean(&offline_mode_);
        NotifyNetworkManagerChanged(false);  // Not forced.
        break;
      }
      case PROPERTY_INDEX_ACTIVE_PROFILE: {
        DCHECK_EQ(value->GetType(), Value::TYPE_STRING);
        value->GetAsString(&active_profile_path_);
        RequestRememberedNetworksUpdate();
        break;
      }
      case PROPERTY_INDEX_PROFILES:
        // Currently we ignore Profiles (list of all profiles).
        break;
      case PROPERTY_INDEX_SERVICES: {
        DCHECK_EQ(value->GetType(), Value::TYPE_LIST);
        const ListValue* vlist = static_cast<const ListValue*>(value);
        UpdateNetworkServiceList(vlist);
        break;
      }
      case PROPERTY_INDEX_SERVICE_WATCH_LIST: {
        DCHECK_EQ(value->GetType(), Value::TYPE_LIST);
        const ListValue* vlist = static_cast<const ListValue*>(value);
        UpdateWatchedNetworkServiceList(vlist);
        break;
      }
      case PROPERTY_INDEX_DEVICE:
      case PROPERTY_INDEX_DEVICES: {
        DCHECK_EQ(value->GetType(), Value::TYPE_LIST);
        const ListValue* vlist = static_cast<const ListValue*>(value);
        UpdateNetworkDeviceList(vlist);
        break;
      }
      default:
        LOG(WARNING) << "Unhandled key: " << key;
        break;
    }
    base::TimeDelta delta = base::TimeTicks::Now() - start;
    VLOG(1) << "  time: " << delta.InMilliseconds() << " ms.";
    HISTOGRAM_TIMES("CROS_NETWORK_UPDATE", delta);
  }

  static void NetworkManagerUpdate(void* object,
                                   const char* manager_path,
                                   const Value* info) {
    NetworkLibraryImpl* networklib = static_cast<NetworkLibraryImpl*>(object);
    DCHECK(networklib);
    if (!info) {
      LOG(ERROR) << "Error retrieving manager properties: " << manager_path;
      return;
    }
    VLOG(1) << "Received NetworkManagerUpdate.";
    DCHECK_EQ(info->GetType(), Value::TYPE_DICTIONARY);
    const DictionaryValue* dict = static_cast<const DictionaryValue*>(info);
    networklib->ParseNetworkManager(dict);
  }

  void ParseNetworkManager(const DictionaryValue* dict) {
    for (DictionaryValue::key_iterator iter = dict->begin_keys();
         iter != dict->end_keys(); ++iter) {
      const std::string& key = *iter;
      Value* value;
      bool res = dict->GetWithoutPathExpansion(key, &value);
      CHECK(res);
      NetworkManagerStatusChanged(key.c_str(), value);
    }
  }

  static void ProfileUpdate(void* object,
                            const char* profile_path,
                            const Value* info) {
    NetworkLibraryImpl* networklib = static_cast<NetworkLibraryImpl*>(object);
    DCHECK(networklib);
    if (!info) {
      LOG(ERROR) << "Error retrieving profile: " << profile_path;
      return;
    }
    VLOG(1) << "Received ProfileUpdate for: " << profile_path;
    DCHECK_EQ(info->GetType(), Value::TYPE_DICTIONARY);
    const DictionaryValue* dict = static_cast<const DictionaryValue*>(info);
    ListValue* entries(NULL);
    dict->GetList(kEntriesProperty, &entries);
    DCHECK(entries);
    networklib->UpdateRememberedServiceList(profile_path, entries);
  }

  static void NetworkServiceUpdate(void* object,
                                   const char* service_path,
                                   const Value* info) {
    NetworkLibraryImpl* networklib = static_cast<NetworkLibraryImpl*>(object);
    DCHECK(networklib);
    if (service_path) {
      if (!info) {
        // Network no longer exists.
        networklib->DeleteNetwork(std::string(service_path));
      } else {
        DCHECK_EQ(info->GetType(), Value::TYPE_DICTIONARY);
        const DictionaryValue* dict = static_cast<const DictionaryValue*>(info);
        networklib->ParseNetwork(std::string(service_path), dict);
      }
    }
  }

  static void RememberedNetworkServiceUpdate(void* object,
                                             const char* service_path,
                                             const Value* info) {
    NetworkLibraryImpl* networklib = static_cast<NetworkLibraryImpl*>(object);
    DCHECK(networklib);
    if (service_path) {
      if (!info) {
        // Remembered network no longer exists.
        networklib->DeleteRememberedWifiNetwork(std::string(service_path));
      } else {
        DCHECK_EQ(info->GetType(), Value::TYPE_DICTIONARY);
        const DictionaryValue* dict = static_cast<const DictionaryValue*>(info);
        networklib->ParseRememberedNetwork(std::string(service_path), dict);
      }
    }
  }

  static void NetworkDeviceUpdate(void* object,
                                  const char* device_path,
                                  const Value* info) {
    NetworkLibraryImpl* networklib = static_cast<NetworkLibraryImpl*>(object);
    DCHECK(networklib);
    if (device_path) {
      if (!info) {
        // device no longer exists.
        networklib->DeleteDevice(std::string(device_path));
      } else {
        DCHECK_EQ(info->GetType(), Value::TYPE_DICTIONARY);
        const DictionaryValue* dict = static_cast<const DictionaryValue*>(info);
        networklib->ParseNetworkDevice(std::string(device_path), dict);
      }
    }
  }

  static void DataPlanUpdateHandler(void* object,
                                    const char* modem_service_path,
                                    const CellularDataPlanList* dataplan) {
    NetworkLibraryImpl* networklib = static_cast<NetworkLibraryImpl*>(object);
    DCHECK(networklib);
    if (modem_service_path && dataplan) {
      networklib->UpdateCellularDataPlan(std::string(modem_service_path),
                                         dataplan);
    }
  }

  ////////////////////////////////////////////////////////////////////////////
  // Network technology functions.

  void UpdateTechnologies(const ListValue* technologies, int* bitfieldp) {
    DCHECK(bitfieldp);
    if (!technologies)
      return;
    int bitfield = 0;
    for (ListValue::const_iterator iter = technologies->begin();
         iter != technologies->end(); ++iter) {
      std::string technology;
      (*iter)->GetAsString(&technology);
      if (!technology.empty()) {
        ConnectionType type = ParseType(technology);
        bitfield |= 1 << type;
      }
    }
    *bitfieldp = bitfield;
    NotifyNetworkManagerChanged(false);  // Not forced.
  }

  void UpdateAvailableTechnologies(const ListValue* technologies) {
    UpdateTechnologies(technologies, &available_devices_);
  }

  void UpdateEnabledTechnologies(const ListValue* technologies) {
    UpdateTechnologies(technologies, &enabled_devices_);
    if (!ethernet_enabled())
      ethernet_ = NULL;
    if (!wifi_enabled()) {
      active_wifi_ = NULL;
      wifi_networks_.clear();
    }
    if (!cellular_enabled()) {
      active_cellular_ = NULL;
      cellular_networks_.clear();
    }
  }

  void UpdateConnectedTechnologies(const ListValue* technologies) {
    UpdateTechnologies(technologies, &connected_devices_);
  }

  ////////////////////////////////////////////////////////////////////////////
  // Network list management functions.

  // Note: sometimes flimflam still returns networks when the device type is
  // disabled. Always check the appropriate enabled() state before adding
  // networks to a list or setting an active network so that we do not show them
  // in the UI.

  // This relies on services being requested from flimflam in priority order,
  // and the updates getting processed and received in order.
  void UpdateActiveNetwork(Network* network) {
    ConnectionType type(network->type());
    if (type == TYPE_ETHERNET) {
      if (ethernet_enabled()) {
        // Set ethernet_ to the first connected ethernet service, or the first
        // disconnected ethernet service if none are connected.
        if (ethernet_ == NULL || !ethernet_->connected())
          ethernet_ = static_cast<EthernetNetwork*>(network);
      }
    } else if (type == TYPE_WIFI) {
      if (wifi_enabled()) {
        // Set active_wifi_ to the first connected or connecting wifi service.
        if (active_wifi_ == NULL && network->connecting_or_connected())
          active_wifi_ = static_cast<WifiNetwork*>(network);
      }
    } else if (type == TYPE_CELLULAR) {
      if (cellular_enabled()) {
        // Set active_cellular_ to first connected/connecting celluar service.
        if (active_cellular_ == NULL && network->connecting_or_connected())
          active_cellular_ = static_cast<CellularNetwork*>(network);
      }
    } else if (type == TYPE_VPN) {
      // Set active_virtual_ to the first connected or connecting vpn service.
      if (active_virtual_ == NULL && network->connecting_or_connected())
        active_virtual_ = static_cast<VirtualNetwork*>(network);
    }
  }

  void AddNetwork(Network* network) {
    std::pair<NetworkMap::iterator,bool> result =
        network_map_.insert(std::make_pair(network->service_path(), network));
    DCHECK(result.second);  // Should only get called with new network.
    VLOG(2) << "Adding Network: " << network->service_path()
            << " (" << network->name() << ")";
    ConnectionType type(network->type());
    if (type == TYPE_WIFI) {
      if (wifi_enabled())
        wifi_networks_.push_back(static_cast<WifiNetwork*>(network));
    } else if (type == TYPE_CELLULAR) {
      if (cellular_enabled())
        cellular_networks_.push_back(static_cast<CellularNetwork*>(network));
    } else if (type == TYPE_VPN) {
      virtual_networks_.push_back(static_cast<VirtualNetwork*>(network));
    }
    // Do not set the active network here. Wait until we parse the network.
  }

  // This only gets called when NetworkServiceUpdate receives a NULL update
  // for an existing network, e.g. an error occurred while fetching a network.
  void DeleteNetwork(const std::string& service_path) {
    NetworkMap::iterator found = network_map_.find(service_path);
    if (found == network_map_.end()) {
      // This occurs when we receive an update request followed by a disconnect
      // which triggers another update. See UpdateNetworkServiceList.
      return;
    }
    Network* network = found->second;
    network_map_.erase(found);
    if (!network->unique_id().empty())
      network_unique_id_map_.erase(network->unique_id());
    ConnectionType type(network->type());
    if (type == TYPE_ETHERNET) {
      if (network == ethernet_) {
        // This should never happen.
        LOG(ERROR) << "Deleting active ethernet network: " << service_path;
        ethernet_ = NULL;
      }
    } else if (type == TYPE_WIFI) {
      WifiNetworkVector::iterator iter = std::find(
          wifi_networks_.begin(), wifi_networks_.end(), network);
      if (iter != wifi_networks_.end())
        wifi_networks_.erase(iter);
      if (network == active_wifi_) {
        // This should never happen.
        LOG(ERROR) << "Deleting active wifi network: " << service_path;
        active_wifi_ = NULL;
      }
    } else if (type == TYPE_CELLULAR) {
      CellularNetworkVector::iterator iter = std::find(
          cellular_networks_.begin(), cellular_networks_.end(), network);
      if (iter != cellular_networks_.end())
        cellular_networks_.erase(iter);
      if (network == active_cellular_) {
        // This should never happen.
        LOG(ERROR) << "Deleting active cellular network: " << service_path;
        active_cellular_ = NULL;
      }
      // Find and delete any existing data plans associated with |service_path|.
      CellularDataPlanMap::iterator found =  data_plan_map_.find(service_path);
      if (found != data_plan_map_.end()) {
        CellularDataPlanVector* data_plans = found->second;
        delete data_plans;
        data_plan_map_.erase(found);
      }
    } else if (type == TYPE_VPN) {
      VirtualNetworkVector::iterator iter = std::find(
          virtual_networks_.begin(), virtual_networks_.end(), network);
      if (iter != virtual_networks_.end())
        virtual_networks_.erase(iter);
      if (network == active_virtual_) {
        // This should never happen.
        LOG(ERROR) << "Deleting active virtual network: " << service_path;
        active_virtual_ = NULL;
      }
    }
    delete network;
  }

  void AddRememberedWifiNetwork(WifiNetwork* wifi) {
    std::pair<NetworkMap::iterator,bool> result =
        remembered_network_map_.insert(
            std::make_pair(wifi->service_path(), wifi));
    DCHECK(result.second);  // Should only get called with new network.
    remembered_wifi_networks_.push_back(wifi);
  }

  void DeleteRememberedWifiNetwork(const std::string& service_path) {
    NetworkMap::iterator found = remembered_network_map_.find(service_path);
    if (found == remembered_network_map_.end()) {
      LOG(WARNING) << "Attempt to delete non-existant remembered network: "
                   << service_path;
      return;
    }
    Network* remembered_network = found->second;
    remembered_network_map_.erase(found);
    WifiNetworkVector::iterator iter = std::find(
        remembered_wifi_networks_.begin(), remembered_wifi_networks_.end(),
        remembered_network);
    if (iter != remembered_wifi_networks_.end())
      remembered_wifi_networks_.erase(iter);
    Network* network = FindNetworkFromRemembered(remembered_network);
    if (network && network->type() == TYPE_WIFI) {
      // Clear the stored credentials for any visible forgotten networks.
      WifiNetwork* wifi = static_cast<WifiNetwork*>(network);
      wifi->EraseCredentials();
    } else {
      // Network is not in visible list.
      VLOG(2) << "Remembered Network not found: "
              << remembered_network->unique_id();
    }
    delete remembered_network;
  }

  // Update all network lists, and request associated service updates.
  void UpdateNetworkServiceList(const ListValue* services) {
    // TODO(stevenjb): Wait for wifi_scanning_ to be false.
    // Copy the list of existing networks to "old" and clear the map and lists.
    NetworkMap old_network_map = network_map_;
    ClearNetworks(false /*don't delete*/);
    // Clear the list of update requests.
    int network_priority_order = 0;
    network_update_requests_.clear();
    // wifi_scanning_ will remain false unless we request a network update.
    wifi_scanning_ = false;
    // |services| represents a complete list of visible networks.
    for (ListValue::const_iterator iter = services->begin();
         iter != services->end(); ++iter) {
      std::string service_path;
      (*iter)->GetAsString(&service_path);
      if (!service_path.empty()) {
        // If we find the network in "old", add it immediately to the map
        // and lists. Otherwise it will get added when NetworkServiceUpdate
        // calls ParseNetwork.
        NetworkMap::iterator found = old_network_map.find(service_path);
        if (found != old_network_map.end()) {
          AddNetwork(found->second);
          old_network_map.erase(found);
        }
        // Always request network updates.
        // TODO(stevenjb): Investigate why we are missing updates then
        // rely on watched network updates and only request updates here for
        // new networks.
        // Use update_request map to store network priority.
        network_update_requests_[service_path] = network_priority_order++;
        wifi_scanning_ = true;
        RequestNetworkServiceInfo(service_path.c_str(),
                                  &NetworkServiceUpdate,
                                  this);
      }
    }
    // Delete any old networks that no longer exist.
    for (NetworkMap::iterator iter = old_network_map.begin();
         iter != old_network_map.end(); ++iter) {
      delete iter->second;
    }
  }

  // Request updates for watched networks. Does not affect network lists.
  // Existing networks will be updated. There should not be any new networks
  // in this list, but if there are they will be added appropriately.
  void UpdateWatchedNetworkServiceList(const ListValue* services) {
    for (ListValue::const_iterator iter = services->begin();
         iter != services->end(); ++iter) {
      std::string service_path;
      (*iter)->GetAsString(&service_path);
      if (!service_path.empty()) {
        VLOG(1) << "Watched Service: " << service_path;
        RequestNetworkServiceInfo(
            service_path.c_str(), &NetworkServiceUpdate, this);
      }
    }
  }

  // Request the active profile which lists the remembered networks.
  void RequestRememberedNetworksUpdate() {
    if (!active_profile_path_.empty()) {
      RequestNetworkProfile(
          active_profile_path_.c_str(), &ProfileUpdate, this);
    }
  }

  // Update the list of remembered (profile) networks, and request associated
  // service updates.
  void UpdateRememberedServiceList(const char* profile_path,
                                   const ListValue* profile_entries) {
    // Copy the list of existing networks to "old" and clear the map and list.
    NetworkMap old_network_map = remembered_network_map_;
    ClearRememberedNetworks(false /*don't delete*/);
    // |profile_entries| represents a complete list of remembered networks.
    for (ListValue::const_iterator iter = profile_entries->begin();
         iter != profile_entries->end(); ++iter) {
      std::string service_path;
      (*iter)->GetAsString(&service_path);
      if (!service_path.empty()) {
        // If we find the network in "old", add it immediately to the map
        // and list. Otherwise it will get added when
        // RememberedNetworkServiceUpdate calls ParseRememberedNetwork.
        NetworkMap::iterator found = old_network_map.find(service_path);
        if (found != old_network_map.end()) {
          Network* network = found->second;
          if (network->type() == TYPE_WIFI) {
            WifiNetwork* wifi = static_cast<WifiNetwork*>(network);
            AddRememberedWifiNetwork(wifi);
            old_network_map.erase(found);
          }
        }
        // Always request updates for remembered networks.
        RequestNetworkProfileEntry(profile_path,
                                   service_path.c_str(),
                                   &RememberedNetworkServiceUpdate,
                                   this);
      }
    }
    // Delete any old networks that no longer exist.
    for (NetworkMap::iterator iter = old_network_map.begin();
         iter != old_network_map.end(); ++iter) {
      delete iter->second;
    }
  }

  Network* CreateNewNetwork(ConnectionType type,
                            const std::string& service_path) {
    switch (type) {
      case TYPE_ETHERNET: {
        EthernetNetwork* ethernet = new EthernetNetwork(service_path);
        return ethernet;
      }
      case TYPE_WIFI: {
        WifiNetwork* wifi = new WifiNetwork(service_path);
        return wifi;
      }
      case TYPE_CELLULAR: {
        CellularNetwork* cellular = new CellularNetwork(service_path);
        return cellular;
      }
      case TYPE_VPN: {
        VirtualNetwork* vpn = new VirtualNetwork(service_path);
        return vpn;
      }
      default: {
        LOG(WARNING) << "Unknown service type: " << type;
        return new Network(service_path, type);
      }
    }
  }

  Network* ParseNetwork(const std::string& service_path,
                        const DictionaryValue* info) {
    Network* network = FindNetworkByPath(service_path);
    if (!network) {
      ConnectionType type = ParseTypeFromDictionary(info);
      network = CreateNewNetwork(type, service_path);
      AddNetwork(network);
    }

    // Erase entry from network_unique_id_map_ in case unique id changes.
    if (!network->unique_id().empty())
      network_unique_id_map_.erase(network->unique_id());

    network->ParseInfo(info);  // virtual.

    if (!network->unique_id().empty())
      network_unique_id_map_[network->unique_id()] = network;

    UpdateActiveNetwork(network);

    // Find and erase entry in update_requests, and set network priority.
    PriorityMap::iterator found2 = network_update_requests_.find(service_path);
    if (found2 != network_update_requests_.end()) {
      network->priority_order_ = found2->second;
      network_update_requests_.erase(found2);
      if (network_update_requests_.empty()) {
        // Clear wifi_scanning_ when we have no pending requests.
        wifi_scanning_ = false;
      }
    } else {
      // TODO(stevenjb): Enable warning once UpdateNetworkServiceList is fixed.
      // LOG(WARNING) << "ParseNetwork called with no update request entry: "
      //              << service_path;
    }

    VLOG(1) << "ParseNetwork: " << network->name();
    NotifyNetworkManagerChanged(false);  // Not forced.
    return network;
  }

  // Returns NULL if |service_path| refers to a network that is not a
  // remembered type.
  Network* ParseRememberedNetwork(const std::string& service_path,
                                  const DictionaryValue* info) {
    Network* network;
    NetworkMap::iterator found = remembered_network_map_.find(service_path);
    if (found != remembered_network_map_.end()) {
      network = found->second;
    } else {
      ConnectionType type = ParseTypeFromDictionary(info);
      if (type == TYPE_WIFI) {
        network = CreateNewNetwork(type, service_path);
        WifiNetwork* wifi = static_cast<WifiNetwork*>(network);
        AddRememberedWifiNetwork(wifi);
      } else {
        VLOG(1) << "Ignoring remembered network: " << service_path
                << " Type: " << ConnectionTypeToString(type);
        return NULL;
      }
    }
    network->ParseInfo(info);  // virtual.
    VLOG(1) << "ParseRememberedNetwork: " << network->name();
    NotifyNetworkManagerChanged(false);  // Not forced.
    return network;
  }

  void ClearNetworks(bool delete_networks) {
    if (delete_networks)
      STLDeleteValues(&network_map_);
    network_map_.clear();
    network_unique_id_map_.clear();
    ethernet_ = NULL;
    active_wifi_ = NULL;
    active_cellular_ = NULL;
    active_virtual_ = NULL;
    wifi_networks_.clear();
    cellular_networks_.clear();
    virtual_networks_.clear();
  }

  void ClearRememberedNetworks(bool delete_networks) {
    if (delete_networks)
      STLDeleteValues(&remembered_network_map_);
    remembered_network_map_.clear();
    remembered_wifi_networks_.clear();
  }

  ////////////////////////////////////////////////////////////////////////////
  // NetworkDevice list management functions.

  // Returns pointer to device or NULL if device is not found by path.
  // Use FindNetworkDeviceByPath when you're not intending to change device.
  NetworkDevice* GetNetworkDeviceByPath(const std::string& path) {
    NetworkDeviceMap::iterator iter = device_map_.find(path);
    if (iter != device_map_.end())
      return iter->second;
    LOG(WARNING) << "Device path not found: " << path;
    return NULL;
  }

  // Update device list, and request associated device updates.
  // |devices| represents a complete list of devices.
  void UpdateNetworkDeviceList(const ListValue* devices) {
    NetworkDeviceMap old_device_map = device_map_;
    device_map_.clear();
    VLOG(2) << "Updating Device List.";
    for (ListValue::const_iterator iter = devices->begin();
         iter != devices->end(); ++iter) {
      std::string device_path;
      (*iter)->GetAsString(&device_path);
      if (!device_path.empty()) {
        NetworkDeviceMap::iterator found = old_device_map.find(device_path);
        if (found != old_device_map.end()) {
          VLOG(2) << " Adding existing device: " << device_path;
          device_map_[device_path] = found->second;
          old_device_map.erase(found);
        }
        RequestNetworkDeviceInfo(
            device_path.c_str(), &NetworkDeviceUpdate, this);
      }
    }
    // Delete any old devices that no longer exist.
    for (NetworkDeviceMap::iterator iter = old_device_map.begin();
         iter != old_device_map.end(); ++iter) {
      DeleteDeviceFromDeviceObserversMap(iter->first);
      // Delete device.
      delete iter->second;
    }
  }

  void DeleteDeviceFromDeviceObserversMap(const std::string& device_path) {
    // Delete all device observers associated with this device.
    NetworkDeviceObserverMap::iterator map_iter =
        network_device_observers_.find(device_path);
    if (map_iter != network_device_observers_.end()) {
      delete map_iter->second;
      network_device_observers_.erase(map_iter);
    }
  }

  void DeleteDevice(const std::string& device_path) {
    NetworkDeviceMap::iterator found = device_map_.find(device_path);
    if (found == device_map_.end()) {
      LOG(WARNING) << "Attempt to delete non-existant device: "
                   << device_path;
      return;
    }
    VLOG(2) << " Deleting device: " << device_path;
    NetworkDevice* device = found->second;
    device_map_.erase(found);
    DeleteDeviceFromDeviceObserversMap(device_path);
    delete device;
  }

  void ParseNetworkDevice(const std::string& device_path,
                          const DictionaryValue* info) {
    NetworkDeviceMap::iterator found = device_map_.find(device_path);
    NetworkDevice* device;
    if (found != device_map_.end()) {
      device = found->second;
    } else {
      device = new NetworkDevice(device_path);
      VLOG(2) << " Adding device: " << device_path;
      device_map_[device_path] = device;
      if (network_device_observers_.find(device_path) ==
          network_device_observers_.end()) {
        network_device_observers_[device_path] =
            new NetworkDeviceObserverList(this, device_path);
      }
    }
    device->ParseInfo(info);
    VLOG(1) << "ParseNetworkDevice:" << device->name();
    NotifyNetworkManagerChanged(false);  // Not forced.
  }

  ////////////////////////////////////////////////////////////////////////////

  void EnableNetworkDeviceType(ConnectionType device, bool enable) {
    if (!EnsureCrosLoaded())
      return;

    // If network device is already enabled/disabled, then don't do anything.
    if (enable && (enabled_devices_ & (1 << device))) {
      LOG(WARNING) << "Trying to enable a device that's already enabled: "
                   << device;
      return;
    }
    if (!enable && !(enabled_devices_ & (1 << device))) {
      LOG(WARNING) << "Trying to disable a device that's already disabled: "
                   << device;
      return;
    }

    RequestNetworkDeviceEnable(ConnectionTypeToString(device), enable);
  }

  ////////////////////////////////////////////////////////////////////////////
  // Notifications.

  // We call this any time something in NetworkLibrary changes.
  // TODO(stevenjb): We should consider breaking this into multiple
  // notifications, e.g. connection state, devices, services, etc.
  void NotifyNetworkManagerChanged(bool force_update) {
    CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    // Cancel any pending signals.
    if (notify_task_) {
      notify_task_->Cancel();
      notify_task_ = NULL;
    }
    if (force_update) {
      // Signal observers now.
      SignalNetworkManagerObservers();
    } else {
      // Schedule a deleayed signal to limit the frequency of notifications.
      notify_task_ = NewRunnableMethod(
          this, &NetworkLibraryImpl::SignalNetworkManagerObservers);
      BrowserThread::PostDelayedTask(BrowserThread::UI, FROM_HERE, notify_task_,
                                     kNetworkNotifyDelayMs);
    }
  }

  void SignalNetworkManagerObservers() {
    notify_task_ = NULL;
    FOR_EACH_OBSERVER(NetworkManagerObserver,
                      network_manager_observers_,
                      OnNetworkManagerChanged(this));
  }

  void NotifyNetworkChanged(Network* network) {
    VLOG(2) << "Network changed: " << network->name();
    DCHECK(network);
    NetworkObserverMap::const_iterator iter = network_observers_.find(
        network->service_path());
    if (iter != network_observers_.end()) {
      FOR_EACH_OBSERVER(NetworkObserver,
                        *(iter->second),
                        OnNetworkChanged(this, network));
    } else {
      NOTREACHED() <<
          "There weren't supposed to be any property change observers of " <<
           network->service_path();
    }
  }

  void NotifyNetworkDeviceChanged(NetworkDevice* device) {
    DCHECK(device);
    NetworkDeviceObserverMap::const_iterator iter =
        network_device_observers_.find(device->device_path());
    if (iter != network_device_observers_.end()) {
      NetworkDeviceObserverList* device_observer_list = iter->second;
      FOR_EACH_OBSERVER(NetworkDeviceObserver,
                        *device_observer_list,
                        OnNetworkDeviceChanged(this, device));
    } else {
      NOTREACHED() <<
          "There weren't supposed to be any property change observers of " <<
           device->device_path();
    }
  }

  void NotifyCellularDataPlanChanged() {
    FOR_EACH_OBSERVER(CellularDataPlanObserver,
                      data_plan_observers_,
                      OnCellularDataPlanChanged(this));
  }

  void NotifyPinOperationCompleted(PinOperationError error) {
    FOR_EACH_OBSERVER(PinOperationObserver,
                      pin_operation_observers_,
                      OnPinOperationCompleted(this, error));
    sim_operation_ = SIM_OPERATION_NONE;
  }

  void NotifyUserConnectionInitiated(const Network* network) {
    FOR_EACH_OBSERVER(UserActionObserver,
                      user_action_observers_,
                      OnConnectionInitiated(this, network));
  }

  ////////////////////////////////////////////////////////////////////////////
  // Device updates.

  void FlipSimPinRequiredStateIfNeeded() {
    if (sim_operation_ != SIM_OPERATION_CHANGE_REQUIRE_PIN)
      return;

    const NetworkDevice* cellular = FindCellularDevice();
    if (cellular) {
      NetworkDevice* device = GetNetworkDeviceByPath(cellular->device_path());
      if (device->sim_pin_required() == SIM_PIN_NOT_REQUIRED)
        device->sim_pin_required_ = SIM_PIN_REQUIRED;
      else if (device->sim_pin_required() == SIM_PIN_REQUIRED)
        device->sim_pin_required_ = SIM_PIN_NOT_REQUIRED;
    }
  }

  void UpdateNetworkDeviceStatus(const char* path,
                                 const char* key,
                                 const Value* value) {
    CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    if (key == NULL || value == NULL)
      return;
    NetworkDevice* device = GetNetworkDeviceByPath(path);
    if (device) {
      VLOG(1) << "UpdateNetworkDeviceStatus: " << device->name() << "." << key;
      int index = property_index_parser().Get(std::string(key));
      if (!device->ParseValue(index, value)) {
        LOG(WARNING) << "UpdateNetworkDeviceStatus: Error parsing: "
                     << path << "." << key;
      } else if (strcmp(key, kCellularAllowRoamingProperty) == 0) {
        bool settings_value =
            UserCrosSettingsProvider::cached_data_roaming_enabled();
        if (device->data_roaming_allowed() != settings_value) {
          // Switch back to signed settings value.
          SetCellularDataRoamingAllowed(settings_value);
          return;
        }
      }
      // Notify only observers on device property change.
      NotifyNetworkDeviceChanged(device);
      // If a device's power state changes, new properties may become
      // defined.
      if (strcmp(key, kPoweredProperty) == 0) {
        RequestNetworkDeviceInfo(path, &NetworkDeviceUpdate, this);
      }
    }
  }

  ////////////////////////////////////////////////////////////////////////////
  // Service updates.

  void UpdateNetworkStatus(const char* path,
                           const char* key,
                           const Value* value) {
    CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    if (key == NULL || value == NULL)
      return;
    Network* network = FindNetworkByPath(path);
    if (network) {
      VLOG(2) << "UpdateNetworkStatus: " << network->name() << "." << key;
      // Note: ParseValue is virtual.
      int index = property_index_parser().Get(std::string(key));
      if (!network->ParseValue(index, value)) {
        LOG(WARNING) << "UpdateNetworkStatus: Error parsing: "
                     << path << "." << key;
      }
      NotifyNetworkChanged(network);
      // Anything observing the manager needs to know about any service change.
      NotifyNetworkManagerChanged(false);  // Not forced.
    }
  }

  ////////////////////////////////////////////////////////////////////////////
  // Data Plans.

  const CellularDataPlan* GetSignificantDataPlanFromVector(
      const CellularDataPlanVector* plans) const {
    const CellularDataPlan* significant = NULL;
    for (CellularDataPlanVector::const_iterator iter = plans->begin();
         iter != plans->end(); ++iter) {
      // Set significant to the first plan or to first non metered base plan.
      if (significant == NULL ||
          significant->plan_type == CELLULAR_DATA_PLAN_METERED_BASE)
        significant = *iter;
    }
    return significant;
  }

  CellularNetwork::DataLeft GetDataLeft(
      CellularDataPlanVector* data_plans) {
    const CellularDataPlan* plan = GetSignificantDataPlanFromVector(data_plans);
    if (!plan)
      return CellularNetwork::DATA_UNKNOWN;
    if (plan->plan_type == CELLULAR_DATA_PLAN_UNLIMITED) {
      base::TimeDelta remaining = plan->remaining_time();
      if (remaining <= base::TimeDelta::FromSeconds(0))
        return CellularNetwork::DATA_NONE;
      if (remaining <= base::TimeDelta::FromSeconds(kCellularDataVeryLowSecs))
        return CellularNetwork::DATA_VERY_LOW;
      if (remaining <= base::TimeDelta::FromSeconds(kCellularDataLowSecs))
        return CellularNetwork::DATA_LOW;
      return CellularNetwork::DATA_NORMAL;
    } else if (plan->plan_type == CELLULAR_DATA_PLAN_METERED_PAID ||
               plan->plan_type == CELLULAR_DATA_PLAN_METERED_BASE) {
      int64 remaining = plan->remaining_data();
      if (remaining <= 0)
        return CellularNetwork::DATA_NONE;
      if (remaining <= kCellularDataVeryLowBytes)
        return CellularNetwork::DATA_VERY_LOW;
      // For base plans, we do not care about low data.
      if (remaining <= kCellularDataLowBytes &&
          plan->plan_type != CELLULAR_DATA_PLAN_METERED_BASE)
        return CellularNetwork::DATA_LOW;
      return CellularNetwork::DATA_NORMAL;
    }
    return CellularNetwork::DATA_UNKNOWN;
  }

  void UpdateCellularDataPlan(const std::string& service_path,
                              const CellularDataPlanList* data_plan_list) {
    VLOG(1) << "Updating cellular data plans for: " << service_path;
    CellularDataPlanVector* data_plans = NULL;
    // Find and delete any existing data plans associated with |service_path|.
    CellularDataPlanMap::iterator found = data_plan_map_.find(service_path);
    if (found != data_plan_map_.end()) {
      data_plans = found->second;
      data_plans->reset();  // This will delete existing data plans.
    } else {
      data_plans = new CellularDataPlanVector;
      data_plan_map_[service_path] = data_plans;
    }
    for (size_t i = 0; i < data_plan_list->plans_size; i++) {
      const CellularDataPlanInfo* info(data_plan_list->GetCellularDataPlan(i));
      CellularDataPlan* plan = new CellularDataPlan(*info);
      data_plans->push_back(plan);
      VLOG(2) << " Plan: " << plan->GetPlanDesciption()
              << " : " << plan->GetDataRemainingDesciption();
    }
    // Now, update any matching cellular network's cached data
    CellularNetwork* cellular = FindCellularNetworkByPath(service_path);
    if (cellular) {
      CellularNetwork::DataLeft data_left;
      // If the network needs a new plan, then there's no data.
      if (cellular->needs_new_plan())
        data_left = CellularNetwork::DATA_NONE;
      else
        data_left = GetDataLeft(data_plans);
      VLOG(2) << " Data left: " << data_left
              << " Need plan: " << cellular->needs_new_plan();
      cellular->set_data_left(data_left);
    }
    NotifyCellularDataPlanChanged();
  }

  ////////////////////////////////////////////////////////////////////////////

  void Init() {
    // First, get the currently available networks. This data is cached
    // on the connman side, so the call should be quick.
    if (EnsureCrosLoaded()) {
      VLOG(1) << "Requesting initial network manager info from libcros.";
      RequestNetworkManagerInfo(&NetworkManagerUpdate, this);
    }
  }

  void InitTestData() {
    is_locked_ = false;

    // Devices
    int devices =
        (1 << TYPE_ETHERNET) | (1 << TYPE_WIFI) | (1 << TYPE_CELLULAR);
    available_devices_ = devices;
    enabled_devices_ = devices;
    connected_devices_ = devices;

    NetworkDevice* cellular = new NetworkDevice("cellular");
    scoped_ptr<Value> cellular_type(Value::CreateStringValue(kTypeCellular));
    cellular->ParseValue(PROPERTY_INDEX_TYPE, cellular_type.get());
    cellular->IMSI_ = "123456789012345";
    device_map_["cellular"] = cellular;

    // Networks
    ClearNetworks(true /*delete networks*/);

    ethernet_ = new EthernetNetwork("eth1");
    ethernet_->set_connected(true);
    AddNetwork(ethernet_);

    WifiNetwork* wifi1 = new WifiNetwork("fw1");
    wifi1->set_name("Fake Wifi Connected");
    wifi1->set_strength(90);
    wifi1->set_connected(true);
    wifi1->set_active(true);
    wifi1->set_encryption(SECURITY_NONE);
    AddNetwork(wifi1);

    WifiNetwork* wifi2 = new WifiNetwork("fw2");
    wifi2->set_name("Fake Wifi");
    wifi2->set_strength(70);
    wifi2->set_connected(false);
    wifi2->set_encryption(SECURITY_NONE);
    AddNetwork(wifi2);

    WifiNetwork* wifi3 = new WifiNetwork("fw3");
    wifi3->set_name("Fake Wifi Encrypted");
    wifi3->set_strength(60);
    wifi3->set_connected(false);
    wifi3->set_encryption(SECURITY_WEP);
    wifi3->set_passphrase_required(true);
    AddNetwork(wifi3);

    WifiNetwork* wifi4 = new WifiNetwork("fw4");
    wifi4->set_name("Fake Wifi 802.1x");
    wifi4->set_strength(50);
    wifi4->set_connected(false);
    wifi4->set_connectable(false);
    wifi4->set_encryption(SECURITY_8021X);
    wifi4->set_identity("nobody@google.com");
    wifi4->set_cert_path("SETTINGS:key_id=3,cert_id=3,pin=111111");
    AddNetwork(wifi4);

    WifiNetwork* wifi5 = new WifiNetwork("fw5");
    wifi5->set_name("Fake Wifi UTF-8 SSID ");
    wifi5->SetSsid("Fake Wifi UTF-8 SSID \u3042\u3044\u3046");
    wifi5->set_strength(25);
    wifi5->set_connected(false);
    AddNetwork(wifi5);

    WifiNetwork* wifi6 = new WifiNetwork("fw6");
    wifi6->set_name("Fake Wifi latin-1 SSID ");
    wifi6->SetSsid("Fake Wifi latin-1 SSID \xc0\xcb\xcc\xd6\xfb");
    wifi6->set_strength(20);
    wifi6->set_connected(false);
    AddNetwork(wifi6);

    active_wifi_ = wifi1;

    CellularNetwork* cellular1 = new CellularNetwork("fc1");
    cellular1->set_name("Fake Cellular");
    cellular1->set_strength(70);
    cellular1->set_connected(true);
    cellular1->set_active(true);
    cellular1->set_activation_state(ACTIVATION_STATE_ACTIVATED);
    cellular1->set_payment_url(std::string("http://www.google.com"));
    cellular1->set_usage_url(std::string("http://www.google.com"));
    cellular1->set_network_technology(NETWORK_TECHNOLOGY_EVDO);
    cellular1->set_roaming_state(ROAMING_STATE_ROAMING);

    CellularDataPlan* base_plan = new CellularDataPlan();
    base_plan->plan_name = "Base plan";
    base_plan->plan_type = CELLULAR_DATA_PLAN_METERED_BASE;
    base_plan->plan_data_bytes = 100ll * 1024 * 1024;
    base_plan->data_bytes_used = 75ll * 1024 * 1024;
    CellularDataPlanVector* data_plans = new CellularDataPlanVector();
    data_plan_map_[cellular1->service_path()] = data_plans;
    data_plans->push_back(base_plan);

    CellularDataPlan* paid_plan = new CellularDataPlan();
    paid_plan->plan_name = "Paid plan";
    paid_plan->plan_type = CELLULAR_DATA_PLAN_METERED_PAID;
    paid_plan->plan_data_bytes = 5ll * 1024 * 1024 * 1024;
    paid_plan->data_bytes_used = 3ll * 1024 * 1024 * 1024;
    data_plans->push_back(paid_plan);

    AddNetwork(cellular1);
    active_cellular_ = cellular1;

    CellularNetwork* cellular2 = new CellularNetwork("fc2");
    cellular2->set_name("Fake Cellular 2");
    cellular2->set_strength(70);
    cellular2->set_connected(true);
    cellular2->set_activation_state(ACTIVATION_STATE_ACTIVATED);
    cellular2->set_network_technology(NETWORK_TECHNOLOGY_UMTS);
    AddNetwork(cellular2);

    // Remembered Networks
    ClearRememberedNetworks(true /*delete networks*/);
    WifiNetwork* remembered_wifi2 = new WifiNetwork("fw2");
    remembered_wifi2->set_name("Fake Wifi 2");
    remembered_wifi2->set_strength(70);
    remembered_wifi2->set_connected(true);
    remembered_wifi2->set_encryption(SECURITY_WEP);
    AddRememberedWifiNetwork(remembered_wifi2);

    // VPNs.
    VirtualNetwork* vpn1 = new VirtualNetwork("fv1");
    vpn1->set_name("Fake VPN Provider 1");
    vpn1->set_server_hostname("vpn1server.fake.com");
    vpn1->set_provider_type(VirtualNetwork::PROVIDER_TYPE_L2TP_IPSEC_PSK);
    vpn1->set_username("VPN User 1");
    vpn1->set_connected(false);
    AddNetwork(vpn1);

    VirtualNetwork* vpn2 = new VirtualNetwork("fv2");
    vpn2->set_name("Fake VPN Provider 2");
    vpn2->set_server_hostname("vpn2server.fake.com");
    vpn2->set_provider_type(VirtualNetwork::PROVIDER_TYPE_L2TP_IPSEC_USER_CERT);
    vpn2->set_username("VPN User 2");
    vpn2->set_connected(true);
    AddNetwork(vpn2);

    VirtualNetwork* vpn3 = new VirtualNetwork("fv3");
    vpn3->set_name("Fake VPN Provider 3");
    vpn3->set_server_hostname("vpn3server.fake.com");
    vpn3->set_provider_type(VirtualNetwork::PROVIDER_TYPE_OPEN_VPN);
    vpn3->set_connected(false);
    AddNetwork(vpn3);

    active_virtual_ = vpn2;

    wifi_scanning_ = false;
    offline_mode_ = false;
  }

  // Network manager observer list
  ObserverList<NetworkManagerObserver> network_manager_observers_;

  // Cellular data plan observer list
  ObserverList<CellularDataPlanObserver> data_plan_observers_;

  // PIN operation observer list.
  ObserverList<PinOperationObserver> pin_operation_observers_;

  // User action observer list
  ObserverList<UserActionObserver> user_action_observers_;

  // Network observer map
  NetworkObserverMap network_observers_;

  // Network device observer map.
  NetworkDeviceObserverMap network_device_observers_;

  // For monitoring network manager status changes.
  PropertyChangeMonitor network_manager_monitor_;

  // For monitoring data plan changes to the connected cellular network.
  DataPlanUpdateMonitor data_plan_monitor_;

  // Network login observer.
  scoped_ptr<NetworkLoginObserver> network_login_observer_;

  // A service path based map of all Networks.
  NetworkMap network_map_;

  // A unique_id_ based map of Networks.
  NetworkMap network_unique_id_map_;

  // A service path based map of all remembered Networks.
  NetworkMap remembered_network_map_;

  // A list of services that we are awaiting updates for.
  PriorityMap network_update_requests_;

  // A device path based map of all NetworkDevices.
  NetworkDeviceMap device_map_;

  // A network service path based map of all CellularDataPlanVectors.
  CellularDataPlanMap data_plan_map_;

  // The ethernet network.
  EthernetNetwork* ethernet_;

  // The list of available wifi networks.
  WifiNetworkVector wifi_networks_;

  // The current connected (or connecting) wifi network.
  WifiNetwork* active_wifi_;

  // The remembered wifi networks.
  WifiNetworkVector remembered_wifi_networks_;

  // The list of available cellular networks.
  CellularNetworkVector cellular_networks_;

  // The current connected (or connecting) cellular network.
  CellularNetwork* active_cellular_;

  // The list of available virtual networks.
  VirtualNetworkVector virtual_networks_;

  // The current connected (or connecting) virtual network.
  VirtualNetwork* active_virtual_;

  // The path of the active profile (for retrieving remembered services).
  std::string active_profile_path_;

  // The current available network devices. Bitwise flag of ConnectionTypes.
  int available_devices_;

  // The current enabled network devices. Bitwise flag of ConnectionTypes.
  int enabled_devices_;

  // The current connected network devices. Bitwise flag of ConnectionTypes.
  int connected_devices_;

  // True if we are currently scanning for wifi networks.
  bool wifi_scanning_;

  // Currently not implemented. TODO: implement or eliminate.
  bool offline_mode_;

  // True if access network library is locked.
  bool is_locked_;

  // Type of pending SIM operation, SIM_OPERATION_NONE otherwise.
  SimOperationType sim_operation_;

  // Delayed task to notify a network change.
  CancelableTask* notify_task_;

  // Cellular plan payment time.
  base::Time cellular_plan_payment_time_;

  // Temporary connection data for async connect calls.
  struct ConnectData {
    std::string service_name;
    std::string passphrase;
    std::string identity;
    std::string cert_path;
    std::string psk_key;
    std::string server_hostname;
  };
  ConnectData connect_data_;

  DISALLOW_COPY_AND_ASSIGN(NetworkLibraryImpl);
};

class NetworkLibraryStubImpl : public NetworkLibrary {
 public:
  NetworkLibraryStubImpl()
      : ip_address_("1.1.1.1"),
        ethernet_(new EthernetNetwork("eth0")),
        active_wifi_(NULL),
        active_cellular_(NULL) {
  }
  ~NetworkLibraryStubImpl() { if (ethernet_) delete ethernet_; }
  virtual void AddNetworkManagerObserver(NetworkManagerObserver* observer) {}
  virtual void RemoveNetworkManagerObserver(NetworkManagerObserver* observer) {}
  virtual void AddNetworkObserver(const std::string& service_path,
                                  NetworkObserver* observer) {}
  virtual void RemoveNetworkObserver(const std::string& service_path,
                                     NetworkObserver* observer) {}
  virtual void RemoveObserverForAllNetworks(NetworkObserver* observer) {}
  virtual void AddNetworkDeviceObserver(const std::string& device_path,
                                        NetworkDeviceObserver* observer) {}
  virtual void RemoveNetworkDeviceObserver(const std::string& device_path,
                                           NetworkDeviceObserver* observer) {}
  virtual void Lock() {}
  virtual void Unlock() {}
  virtual bool IsLocked() { return false; }
  virtual void AddCellularDataPlanObserver(
      CellularDataPlanObserver* observer) {}
  virtual void RemoveCellularDataPlanObserver(
      CellularDataPlanObserver* observer) {}
  virtual void AddPinOperationObserver(PinOperationObserver* observer) {}
  virtual void RemovePinOperationObserver(PinOperationObserver* observer) {}
  virtual void AddUserActionObserver(UserActionObserver* observer) {}
  virtual void RemoveUserActionObserver(UserActionObserver* observer) {}

  virtual const EthernetNetwork* ethernet_network() const {
    return ethernet_;
  }
  virtual bool ethernet_connecting() const { return false; }
  virtual bool ethernet_connected() const { return true; }

  virtual const WifiNetwork* wifi_network() const {
    return active_wifi_;
  }
  virtual bool wifi_connecting() const { return false; }
  virtual bool wifi_connected() const { return false; }

  virtual const CellularNetwork* cellular_network() const {
    return active_cellular_;
  }
  virtual bool cellular_connecting() const { return false; }
  virtual bool cellular_connected() const { return false; }

  virtual const VirtualNetwork* virtual_network() const {
    return active_virtual_;
  }
  virtual bool virtual_network_connecting() const { return false; }
  virtual bool virtual_network_connected() const { return false; }

  bool Connected() const { return true; }
  bool Connecting() const { return false; }
  const std::string& IPAddress() const { return ip_address_; }
  virtual const WifiNetworkVector& wifi_networks() const {
    return wifi_networks_;
  }
  virtual const WifiNetworkVector& remembered_wifi_networks() const {
    return wifi_networks_;
  }
  virtual const CellularNetworkVector& cellular_networks() const {
    return cellular_networks_;
  }
  virtual const VirtualNetworkVector& virtual_networks() const {
    return virtual_networks_;
  }
  virtual bool has_cellular_networks() const {
    return cellular_networks_.begin() != cellular_networks_.end();
  }
  /////////////////////////////////////////////////////////////////////////////

  virtual const NetworkDevice* FindNetworkDeviceByPath(
      const std::string& path) const { return NULL; }
  virtual const NetworkDevice* FindCellularDevice() const {
    return NULL;
  }
  virtual const NetworkDevice* FindEthernetDevice() const {
    return NULL;
  }
  virtual const NetworkDevice* FindWifiDevice() const {
    return NULL;
  }
  virtual Network* FindNetworkByPath(
      const std::string& path) const { return NULL; }
  virtual WifiNetwork* FindWifiNetworkByPath(
      const std::string& path) const { return NULL; }
  virtual CellularNetwork* FindCellularNetworkByPath(
      const std::string& path) const { return NULL; }
  virtual VirtualNetwork* FindVirtualNetworkByPath(
      const std::string& path) const { return NULL; }
  virtual Network* FindNetworkFromRemembered(
      const Network* remembered) const { return NULL; }
  virtual const CellularDataPlanVector* GetDataPlans(
      const std::string& path) const { return NULL; }
  virtual const CellularDataPlan* GetSignificantDataPlan(
      const std::string& path) const { return NULL; }

  virtual void ChangePin(const std::string& old_pin,
                         const std::string& new_pin) {}
  virtual void ChangeRequirePin(bool require_pin, const std::string& pin) {}
  virtual void EnterPin(const std::string& pin) {}
  virtual void UnblockPin(const std::string& puk, const std::string& new_pin) {}
  virtual void RequestCellularScan() {}
  virtual void RequestCellularRegister(const std::string& network_id) {}
  virtual void SetCellularDataRoamingAllowed(bool new_value) {}

  virtual void RequestNetworkScan() {}
  virtual bool GetWifiAccessPoints(WifiAccessPointVector* result) {
    return false;
  }

  virtual void ConnectToWifiNetwork(WifiNetwork* network) {}
  virtual void ConnectToWifiNetwork(const std::string& service_path) {}
  virtual void ConnectToWifiNetwork(ConnectionSecurity security,
                                    const std::string& ssid,
                                    const std::string& passphrase,
                                    const std::string& identity,
                                    const std::string& certpath) {}
  virtual void ConnectToCellularNetwork(CellularNetwork* network) {}
  virtual void ConnectToVirtualNetwork(VirtualNetwork* network) {}
  virtual void ConnectToVirtualNetworkPSK(
      const std::string& service_name,
      const std::string& server,
      const std::string& psk,
      const std::string& username,
      const std::string& user_passphrase) {}
  virtual void SignalCellularPlanPayment() {}
  virtual bool HasRecentCellularPlanPayment() { return false; }
  virtual void DisconnectFromNetwork(const Network* network) {}
  virtual void ForgetWifiNetwork(const std::string& service_path) {}
  virtual std::string GetCellularHomeCarrierId() const { return std::string(); }
  virtual bool ethernet_available() const { return true; }
  virtual bool wifi_available() const { return false; }
  virtual bool cellular_available() const { return false; }
  virtual bool ethernet_enabled() const { return true; }
  virtual bool wifi_enabled() const { return false; }
  virtual bool cellular_enabled() const { return false; }
  virtual bool wifi_scanning() const { return false; }
  virtual const Network* active_network() const { return NULL; }
  virtual const Network* connected_network() const { return NULL; }
  virtual bool offline_mode() const { return false; }
  virtual void EnableEthernetNetworkDevice(bool enable) {}
  virtual void EnableWifiNetworkDevice(bool enable) {}
  virtual void EnableCellularNetworkDevice(bool enable) {}
  virtual void EnableOfflineMode(bool enable) {}
  virtual NetworkIPConfigVector GetIPConfigs(const std::string& device_path,
                                             std::string* hardware_address,
                                             HardwareAddressFormat) {
    hardware_address->clear();
    return NetworkIPConfigVector();
  }

 private:
  std::string ip_address_;
  EthernetNetwork* ethernet_;
  WifiNetwork* active_wifi_;
  CellularNetwork* active_cellular_;
  VirtualNetwork* active_virtual_;
  WifiNetworkVector wifi_networks_;
  CellularNetworkVector cellular_networks_;
  VirtualNetworkVector virtual_networks_;
};

// static
NetworkLibrary* NetworkLibrary::GetImpl(bool stub) {
  if (stub)
    return new NetworkLibraryStubImpl();
  else
    return new NetworkLibraryImpl();
}

}  // namespace chromeos

// Allows InvokeLater without adding refcounting. This class is a Singleton and
// won't be deleted until it's last InvokeLater is run.
DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::NetworkLibraryImpl);