1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "components/metrics/net/network_metrics_provider.h"
6
7#include <string>
8#include <vector>
9
10#include "base/compiler_specific.h"
11#include "base/strings/string_number_conversions.h"
12#include "base/strings/string_split.h"
13#include "base/strings/string_util.h"
14#include "base/task_runner_util.h"
15
16#if defined(OS_CHROMEOS)
17#include "components/metrics/net/wifi_access_point_info_provider_chromeos.h"
18#endif // OS_CHROMEOS
19
20using metrics::SystemProfileProto;
21
22NetworkMetricsProvider::NetworkMetricsProvider(
23    base::TaskRunner* io_task_runner)
24    : io_task_runner_(io_task_runner),
25      connection_type_is_ambiguous_(false),
26      wifi_phy_layer_protocol_is_ambiguous_(false),
27      wifi_phy_layer_protocol_(net::WIFI_PHY_LAYER_PROTOCOL_UNKNOWN),
28      weak_ptr_factory_(this) {
29  net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
30  connection_type_ = net::NetworkChangeNotifier::GetConnectionType();
31  ProbeWifiPHYLayerProtocol();
32}
33
34NetworkMetricsProvider::~NetworkMetricsProvider() {
35  net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
36}
37
38void NetworkMetricsProvider::OnDidCreateMetricsLog() {
39  net::NetworkChangeNotifier::LogOperatorCodeHistogram(
40      net::NetworkChangeNotifier::GetConnectionType());
41}
42
43void NetworkMetricsProvider::ProvideSystemProfileMetrics(
44    SystemProfileProto* system_profile) {
45  SystemProfileProto::Network* network = system_profile->mutable_network();
46  network->set_connection_type_is_ambiguous(connection_type_is_ambiguous_);
47  network->set_connection_type(GetConnectionType());
48  network->set_wifi_phy_layer_protocol_is_ambiguous(
49      wifi_phy_layer_protocol_is_ambiguous_);
50  network->set_wifi_phy_layer_protocol(GetWifiPHYLayerProtocol());
51
52  // Resets the "ambiguous" flags, since a new metrics log session has started.
53  connection_type_is_ambiguous_ = false;
54  // TODO(isherman): This line seems unnecessary.
55  connection_type_ = net::NetworkChangeNotifier::GetConnectionType();
56  wifi_phy_layer_protocol_is_ambiguous_ = false;
57
58  if (!wifi_access_point_info_provider_.get()) {
59#if defined(OS_CHROMEOS)
60    wifi_access_point_info_provider_.reset(
61        new WifiAccessPointInfoProviderChromeos());
62#else
63    wifi_access_point_info_provider_.reset(
64        new WifiAccessPointInfoProvider());
65#endif // OS_CHROMEOS
66  }
67
68  // Connected wifi access point information.
69  WifiAccessPointInfoProvider::WifiAccessPointInfo info;
70  if (wifi_access_point_info_provider_->GetInfo(&info))
71    WriteWifiAccessPointProto(info, network);
72}
73
74void NetworkMetricsProvider::OnConnectionTypeChanged(
75    net::NetworkChangeNotifier::ConnectionType type) {
76  if (type == net::NetworkChangeNotifier::CONNECTION_NONE)
77    return;
78  if (type != connection_type_ &&
79      connection_type_ != net::NetworkChangeNotifier::CONNECTION_NONE) {
80    connection_type_is_ambiguous_ = true;
81  }
82  connection_type_ = type;
83
84  ProbeWifiPHYLayerProtocol();
85}
86
87SystemProfileProto::Network::ConnectionType
88NetworkMetricsProvider::GetConnectionType() const {
89  switch (connection_type_) {
90    case net::NetworkChangeNotifier::CONNECTION_NONE:
91    case net::NetworkChangeNotifier::CONNECTION_UNKNOWN:
92      return SystemProfileProto::Network::CONNECTION_UNKNOWN;
93    case net::NetworkChangeNotifier::CONNECTION_ETHERNET:
94      return SystemProfileProto::Network::CONNECTION_ETHERNET;
95    case net::NetworkChangeNotifier::CONNECTION_WIFI:
96      return SystemProfileProto::Network::CONNECTION_WIFI;
97    case net::NetworkChangeNotifier::CONNECTION_2G:
98      return SystemProfileProto::Network::CONNECTION_2G;
99    case net::NetworkChangeNotifier::CONNECTION_3G:
100      return SystemProfileProto::Network::CONNECTION_3G;
101    case net::NetworkChangeNotifier::CONNECTION_4G:
102      return SystemProfileProto::Network::CONNECTION_4G;
103    case net::NetworkChangeNotifier::CONNECTION_BLUETOOTH:
104      return SystemProfileProto::Network::CONNECTION_BLUETOOTH;
105  }
106  NOTREACHED();
107  return SystemProfileProto::Network::CONNECTION_UNKNOWN;
108}
109
110SystemProfileProto::Network::WifiPHYLayerProtocol
111NetworkMetricsProvider::GetWifiPHYLayerProtocol() const {
112  switch (wifi_phy_layer_protocol_) {
113    case net::WIFI_PHY_LAYER_PROTOCOL_NONE:
114      return SystemProfileProto::Network::WIFI_PHY_LAYER_PROTOCOL_NONE;
115    case net::WIFI_PHY_LAYER_PROTOCOL_ANCIENT:
116      return SystemProfileProto::Network::WIFI_PHY_LAYER_PROTOCOL_ANCIENT;
117    case net::WIFI_PHY_LAYER_PROTOCOL_A:
118      return SystemProfileProto::Network::WIFI_PHY_LAYER_PROTOCOL_A;
119    case net::WIFI_PHY_LAYER_PROTOCOL_B:
120      return SystemProfileProto::Network::WIFI_PHY_LAYER_PROTOCOL_B;
121    case net::WIFI_PHY_LAYER_PROTOCOL_G:
122      return SystemProfileProto::Network::WIFI_PHY_LAYER_PROTOCOL_G;
123    case net::WIFI_PHY_LAYER_PROTOCOL_N:
124      return SystemProfileProto::Network::WIFI_PHY_LAYER_PROTOCOL_N;
125    case net::WIFI_PHY_LAYER_PROTOCOL_UNKNOWN:
126      return SystemProfileProto::Network::WIFI_PHY_LAYER_PROTOCOL_UNKNOWN;
127  }
128  NOTREACHED();
129  return SystemProfileProto::Network::WIFI_PHY_LAYER_PROTOCOL_UNKNOWN;
130}
131
132void NetworkMetricsProvider::ProbeWifiPHYLayerProtocol() {
133  PostTaskAndReplyWithResult(
134      io_task_runner_,
135      FROM_HERE,
136      base::Bind(&net::GetWifiPHYLayerProtocol),
137      base::Bind(&NetworkMetricsProvider::OnWifiPHYLayerProtocolResult,
138                 weak_ptr_factory_.GetWeakPtr()));
139}
140
141void NetworkMetricsProvider::OnWifiPHYLayerProtocolResult(
142    net::WifiPHYLayerProtocol mode) {
143  if (wifi_phy_layer_protocol_ != net::WIFI_PHY_LAYER_PROTOCOL_UNKNOWN &&
144      mode != wifi_phy_layer_protocol_) {
145    wifi_phy_layer_protocol_is_ambiguous_ = true;
146  }
147  wifi_phy_layer_protocol_ = mode;
148}
149
150void NetworkMetricsProvider::WriteWifiAccessPointProto(
151    const WifiAccessPointInfoProvider::WifiAccessPointInfo& info,
152    SystemProfileProto::Network* network_proto) {
153  SystemProfileProto::Network::WifiAccessPoint* access_point_info =
154      network_proto->mutable_access_point_info();
155  SystemProfileProto::Network::WifiAccessPoint::SecurityMode security =
156      SystemProfileProto::Network::WifiAccessPoint::SECURITY_UNKNOWN;
157  switch (info.security) {
158    case WifiAccessPointInfoProvider::WIFI_SECURITY_NONE:
159      security = SystemProfileProto::Network::WifiAccessPoint::SECURITY_NONE;
160      break;
161    case WifiAccessPointInfoProvider::WIFI_SECURITY_WPA:
162      security = SystemProfileProto::Network::WifiAccessPoint::SECURITY_WPA;
163      break;
164    case WifiAccessPointInfoProvider::WIFI_SECURITY_WEP:
165      security = SystemProfileProto::Network::WifiAccessPoint::SECURITY_WEP;
166      break;
167    case WifiAccessPointInfoProvider::WIFI_SECURITY_RSN:
168      security = SystemProfileProto::Network::WifiAccessPoint::SECURITY_RSN;
169      break;
170    case WifiAccessPointInfoProvider::WIFI_SECURITY_802_1X:
171      security = SystemProfileProto::Network::WifiAccessPoint::SECURITY_802_1X;
172      break;
173    case WifiAccessPointInfoProvider::WIFI_SECURITY_PSK:
174      security = SystemProfileProto::Network::WifiAccessPoint::SECURITY_PSK;
175      break;
176    case WifiAccessPointInfoProvider::WIFI_SECURITY_UNKNOWN:
177      security = SystemProfileProto::Network::WifiAccessPoint::SECURITY_UNKNOWN;
178      break;
179  }
180  access_point_info->set_security_mode(security);
181
182  // |bssid| is xx:xx:xx:xx:xx:xx, extract the first three components and
183  // pack into a uint32.
184  std::string bssid = info.bssid;
185  if (bssid.size() == 17 && bssid[2] == ':' && bssid[5] == ':' &&
186      bssid[8] == ':' && bssid[11] == ':' && bssid[14] == ':') {
187    std::string vendor_prefix_str;
188    uint32 vendor_prefix;
189
190    base::RemoveChars(bssid.substr(0, 9), ":", &vendor_prefix_str);
191    DCHECK_EQ(6U, vendor_prefix_str.size());
192    if (base::HexStringToUInt(vendor_prefix_str, &vendor_prefix))
193      access_point_info->set_vendor_prefix(vendor_prefix);
194    else
195      NOTREACHED();
196  }
197
198  // Return if vendor information is not provided.
199  if (info.model_number.empty() && info.model_name.empty() &&
200      info.device_name.empty() && info.oui_list.empty())
201    return;
202
203  SystemProfileProto::Network::WifiAccessPoint::VendorInformation* vendor =
204      access_point_info->mutable_vendor_info();
205  if (!info.model_number.empty())
206    vendor->set_model_number(info.model_number);
207  if (!info.model_name.empty())
208    vendor->set_model_name(info.model_name);
209  if (!info.device_name.empty())
210    vendor->set_device_name(info.device_name);
211
212  // Return if OUI list is not provided.
213  if (info.oui_list.empty())
214    return;
215
216  // Parse OUI list.
217  std::vector<std::string> oui_list;
218  base::SplitString(info.oui_list, ' ', &oui_list);
219  for (std::vector<std::string>::const_iterator it = oui_list.begin();
220       it != oui_list.end();
221       ++it) {
222    uint32 oui;
223    if (base::HexStringToUInt(*it, &oui))
224      vendor->add_element_identifier(oui);
225    else
226      NOTREACHED();
227  }
228}
229