blob: 6e5381e3190062807d7fb5930cf06c6fb3db334a [file] [log] [blame]
Paul Stewart21f2aae2013-01-17 17:10:08 -08001// Copyright (c) 2013 The Chromium OS 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 "shill/wifi_provider.h"
6
Wade Guthrie60a37062013-04-02 11:39:09 -07007#include <stdlib.h>
8
9#include <limits>
Paul Stewart3c504012013-01-17 17:49:58 -080010#include <set>
11#include <string>
12#include <vector>
13
14#include <base/bind.h>
Wade Guthrie7c2d34e2013-05-09 14:02:20 -070015#include <base/format_macros.h>
Ben Chana0ddf462014-02-06 11:32:42 -080016#include <base/strings/string_number_conversions.h>
17#include <base/strings/string_split.h>
18#include <base/strings/string_util.h>
Paul Stewart3c504012013-01-17 17:49:58 -080019
Paul Stewart21f2aae2013-01-17 17:10:08 -080020#include "shill/error.h"
21#include "shill/event_dispatcher.h"
Paul Stewart3c504012013-01-17 17:49:58 -080022#include "shill/ieee80211.h"
Paul Stewart21f2aae2013-01-17 17:10:08 -080023#include "shill/key_value_store.h"
Paul Stewart3c504012013-01-17 17:49:58 -080024#include "shill/logging.h"
Paul Stewart21f2aae2013-01-17 17:10:08 -080025#include "shill/manager.h"
26#include "shill/metrics.h"
Paul Stewart3c504012013-01-17 17:49:58 -080027#include "shill/profile.h"
Wade Guthrie7c2d34e2013-05-09 14:02:20 -070028#include "shill/shill_time.h"
Paul Stewart3c504012013-01-17 17:49:58 -080029#include "shill/store_interface.h"
30#include "shill/technology.h"
Paul Stewart21f2aae2013-01-17 17:10:08 -080031#include "shill/wifi_endpoint.h"
32#include "shill/wifi_service.h"
33
Paul Stewart3c504012013-01-17 17:49:58 -080034using base::Bind;
Wade Guthrie7c2d34e2013-05-09 14:02:20 -070035using base::SplitString;
Ben Chana0ddf462014-02-06 11:32:42 -080036using base::StringPrintf;
Paul Stewart3c504012013-01-17 17:49:58 -080037using std::set;
38using std::string;
39using std::vector;
40
Paul Stewart21f2aae2013-01-17 17:10:08 -080041namespace shill {
42
Paul Stewart3c504012013-01-17 17:49:58 -080043// Note that WiFiProvider generates some manager-level errors, because it
44// implements the WiFi portion of the Manager.GetService flimflam API. The
45// API is implemented here, rather than in manager, to keep WiFi-specific
46// logic in the right place.
47const char WiFiProvider::kManagerErrorSSIDRequired[] = "must specify SSID";
48const char WiFiProvider::kManagerErrorSSIDTooLong[] = "SSID is too long";
49const char WiFiProvider::kManagerErrorSSIDTooShort[] = "SSID is too short";
50const char WiFiProvider::kManagerErrorUnsupportedSecurityMode[] =
51 "security mode is unsupported";
52const char WiFiProvider::kManagerErrorUnsupportedServiceMode[] =
53 "service mode is unsupported";
Wade Guthrie7c2d34e2013-05-09 14:02:20 -070054const char WiFiProvider::kFrequencyDelimiter = ':';
55const char WiFiProvider::kStartWeekHeader[] = "@";
56const time_t WiFiProvider::kIllegalStartWeek =
57 std::numeric_limits<time_t>::max();
Jason Abele28666542013-05-15 11:58:21 -070058const char WiFiProvider::kStorageId[] = "provider_of_wifi";
Wade Guthrie60a37062013-04-02 11:39:09 -070059const char WiFiProvider::kStorageFrequencies[] = "Frequencies";
Wade Guthrie7c2d34e2013-05-09 14:02:20 -070060const int WiFiProvider::kMaxStorageFrequencies = 20;
61const time_t WiFiProvider::kWeeksToKeepFrequencyCounts = 3;
62const time_t WiFiProvider::kSecondsPerWeek = 60 * 60 * 24 * 7;
Paul Stewart3c504012013-01-17 17:49:58 -080063
Paul Stewart21f2aae2013-01-17 17:10:08 -080064WiFiProvider::WiFiProvider(ControlInterface *control_interface,
65 EventDispatcher *dispatcher,
66 Metrics *metrics,
67 Manager *manager)
68 : control_interface_(control_interface),
69 dispatcher_(dispatcher),
70 metrics_(metrics),
Paul Stewart6c351ff2013-02-25 15:13:03 -080071 manager_(manager),
Wade Guthrie60a37062013-04-02 11:39:09 -070072 running_(false),
Wade Guthrie7c2d34e2013-05-09 14:02:20 -070073 total_frequency_connections_(-1L),
Paul Stewart9d97b7d2014-03-13 21:47:03 -070074 time_(Time::GetInstance()),
75 disable_vht_(false) {}
Paul Stewart21f2aae2013-01-17 17:10:08 -080076
77WiFiProvider::~WiFiProvider() {}
78
Paul Stewart6c351ff2013-02-25 15:13:03 -080079void WiFiProvider::Start() {
80 running_ = true;
81}
Paul Stewart21f2aae2013-01-17 17:10:08 -080082
83void WiFiProvider::Stop() {
Paul Stewart3c504012013-01-17 17:49:58 -080084 SLOG(WiFi, 2) << __func__;
85 while (!services_.empty()) {
86 WiFiServiceRefPtr service = services_.back();
87 ForgetService(service);
88 SLOG(WiFi, 3) << "WiFiProvider deregistering service "
89 << service->unique_name();
90 manager_->DeregisterService(service);
91 }
Paul Stewart0427cc12013-03-25 13:50:39 -070092 service_by_endpoint_.clear();
Paul Stewart6c351ff2013-02-25 15:13:03 -080093 running_ = false;
Paul Stewart21f2aae2013-01-17 17:10:08 -080094}
95
Paul Stewart3c504012013-01-17 17:49:58 -080096void WiFiProvider::CreateServicesFromProfile(const ProfileRefPtr &profile) {
97 const StoreInterface *storage = profile->GetConstStorage();
98 KeyValueStore args;
Ben Chanf024ef42013-09-20 14:21:38 -070099 args.SetString(kTypeProperty, kTypeWifi);
Paul Stewart3c504012013-01-17 17:49:58 -0800100 bool created_hidden_service = false;
Paul Stewart6db7b242014-05-02 15:34:21 -0700101 for (const auto &group : storage->GetGroupsWithProperties(args)) {
Paul Stewart3c504012013-01-17 17:49:58 -0800102 string ssid_hex;
103 vector<uint8_t> ssid_bytes;
Paul Stewart6db7b242014-05-02 15:34:21 -0700104 if (!storage->GetString(group, WiFiService::kStorageSSID, &ssid_hex) ||
Paul Stewart3c504012013-01-17 17:49:58 -0800105 !base::HexStringToBytes(ssid_hex, &ssid_bytes)) {
Paul Stewart6db7b242014-05-02 15:34:21 -0700106 SLOG(WiFi, 2) << "Storage group " << group << " is missing valid \""
Paul Stewart3c504012013-01-17 17:49:58 -0800107 << WiFiService::kStorageSSID << "\" property";
108 continue;
109 }
110 string network_mode;
Paul Stewart6db7b242014-05-02 15:34:21 -0700111 if (!storage->GetString(group, WiFiService::kStorageMode, &network_mode) ||
Paul Stewart3c504012013-01-17 17:49:58 -0800112 network_mode.empty()) {
Paul Stewart6db7b242014-05-02 15:34:21 -0700113 SLOG(WiFi, 2) << "Storage group " << group << " is missing \""
Paul Stewart3c504012013-01-17 17:49:58 -0800114 << WiFiService::kStorageMode << "\" property";
115 continue;
116 }
117 string security;
Paul Stewart6db7b242014-05-02 15:34:21 -0700118 if (!storage->GetString(group, WiFiService::kStorageSecurity, &security) ||
Paul Stewart3c504012013-01-17 17:49:58 -0800119 !WiFiService::IsValidSecurityMethod(security)) {
Paul Stewart6db7b242014-05-02 15:34:21 -0700120 SLOG(WiFi, 2) << "Storage group " << group << " has missing or invalid \""
Paul Stewart3c504012013-01-17 17:49:58 -0800121 << WiFiService::kStorageSecurity << "\" property";
122 continue;
123 }
124 bool is_hidden = false;
Paul Stewart6db7b242014-05-02 15:34:21 -0700125 if (!storage->GetBool(group, WiFiService::kStorageHiddenSSID, &is_hidden)) {
126 SLOG(WiFi, 2) << "Storage group " << group << " is missing \""
Paul Stewart3c504012013-01-17 17:49:58 -0800127 << WiFiService::kStorageHiddenSSID << "\" property";
128 continue;
129 }
130
131 if (FindService(ssid_bytes, network_mode, security)) {
132 // If service already exists, we have nothing to do, since the
133 // service has already loaded its configuration from storage.
134 // This is guaranteed to happen in the single case where
135 // CreateServicesFromProfile() is called on a WiFiProvider from
136 // Manager::PushProfile():
137 continue;
138 }
139
140 AddService(ssid_bytes, network_mode, security, is_hidden);
141
142 // By registering the service in AddService, the rest of the configuration
143 // will be loaded from the profile into the service via ConfigureService().
144
145 if (is_hidden) {
146 created_hidden_service = true;
147 }
148 }
149
150 // If WiFi is unconnected and we created a hidden service as a result
151 // of opening the profile, we should initiate a WiFi scan, which will
152 // allow us to find any hidden services that we may have created.
153 if (created_hidden_service &&
154 !manager_->IsTechnologyConnected(Technology::kWifi)) {
155 Error unused_error;
Ben Chanf024ef42013-09-20 14:21:38 -0700156 manager_->RequestScan(Device::kProgressiveScan, kTypeWifi, &unused_error);
Paul Stewart3c504012013-01-17 17:49:58 -0800157 }
Paul Stewart21f2aae2013-01-17 17:10:08 -0800158}
159
Paul Stewart0e51ad92013-07-26 14:42:55 -0700160ServiceRefPtr WiFiProvider::FindSimilarService(
Paul Stewartd2e1c362013-03-03 19:06:07 -0800161 const KeyValueStore &args, Error *error) const {
162 vector<uint8_t> ssid;
163 string mode;
164 string security;
165 bool hidden_ssid;
166
167 if (!GetServiceParametersFromArgs(
168 args, &ssid, &mode, &security, &hidden_ssid, error)) {
169 return NULL;
170 }
171
172 WiFiServiceRefPtr service(FindService(ssid, mode, security));
173 if (!service) {
174 error->Populate(Error::kNotFound, "Matching service was not found");
175 }
176
177 return service;
178}
179
Paul Stewart0e51ad92013-07-26 14:42:55 -0700180ServiceRefPtr WiFiProvider::CreateTemporaryService(
Paul Stewartd2e1c362013-03-03 19:06:07 -0800181 const KeyValueStore &args, Error *error) {
182 vector<uint8_t> ssid;
183 string mode;
184 string security;
185 bool hidden_ssid;
186
187 if (!GetServiceParametersFromArgs(
188 args, &ssid, &mode, &security, &hidden_ssid, error)) {
189 return NULL;
190 }
191
192 return new WiFiService(control_interface_,
193 dispatcher_,
194 metrics_,
195 manager_,
196 this,
197 ssid,
198 mode,
199 security,
200 hidden_ssid);
201}
202
Paul Stewart0e51ad92013-07-26 14:42:55 -0700203ServiceRefPtr WiFiProvider::GetService(
204 const KeyValueStore &args, Error *error) {
205 return GetWiFiService(args, error);
206}
207
208WiFiServiceRefPtr WiFiProvider::GetWiFiService(
Paul Stewart21f2aae2013-01-17 17:10:08 -0800209 const KeyValueStore &args, Error *error) {
Paul Stewartd2e1c362013-03-03 19:06:07 -0800210 vector<uint8_t> ssid_bytes;
211 string mode;
212 string security_method;
213 bool hidden_ssid;
Paul Stewart3c504012013-01-17 17:49:58 -0800214
Paul Stewartd2e1c362013-03-03 19:06:07 -0800215 if (!GetServiceParametersFromArgs(
216 args, &ssid_bytes, &mode, &security_method, &hidden_ssid, error)) {
Paul Stewart3c504012013-01-17 17:49:58 -0800217 return NULL;
218 }
219
Paul Stewartd2e1c362013-03-03 19:06:07 -0800220 WiFiServiceRefPtr service(FindService(ssid_bytes, mode, security_method));
Paul Stewart3c504012013-01-17 17:49:58 -0800221 if (!service) {
222 service = AddService(ssid_bytes,
Paul Stewartd2e1c362013-03-03 19:06:07 -0800223 mode,
Paul Stewart3c504012013-01-17 17:49:58 -0800224 security_method,
225 hidden_ssid);
226 }
227
228 return service;
Paul Stewart21f2aae2013-01-17 17:10:08 -0800229}
230
231WiFiServiceRefPtr WiFiProvider::FindServiceForEndpoint(
Paul Stewart3c504012013-01-17 17:49:58 -0800232 const WiFiEndpointConstRefPtr &endpoint) {
Paul Stewart0427cc12013-03-25 13:50:39 -0700233 EndpointServiceMap::iterator service_it =
234 service_by_endpoint_.find(endpoint);
235 if (service_it == service_by_endpoint_.end())
236 return NULL;
237 return service_it->second;
Paul Stewart3c504012013-01-17 17:49:58 -0800238}
239
240void WiFiProvider::OnEndpointAdded(const WiFiEndpointConstRefPtr &endpoint) {
Paul Stewart6c351ff2013-02-25 15:13:03 -0800241 if (!running_) {
242 return;
243 }
244
Paul Stewart0427cc12013-03-25 13:50:39 -0700245 WiFiServiceRefPtr service = FindService(endpoint->ssid(),
246 endpoint->network_mode(),
247 endpoint->security_mode());
Paul Stewart3c504012013-01-17 17:49:58 -0800248 if (!service) {
249 const bool hidden_ssid = false;
Paul Stewart08a54eb2013-03-11 12:07:36 -0700250 service = AddService(
251 endpoint->ssid(),
252 endpoint->network_mode(),
253 WiFiService::GetSecurityClass(endpoint->security_mode()),
254 hidden_ssid);
Paul Stewart3c504012013-01-17 17:49:58 -0800255 }
256
257 service->AddEndpoint(endpoint);
Paul Stewart0427cc12013-03-25 13:50:39 -0700258 service_by_endpoint_[endpoint] = service;
Paul Stewart3c504012013-01-17 17:49:58 -0800259
260 SLOG(WiFi, 1) << "Assigned endpoint " << endpoint->bssid_string()
261 << " to service " << service->unique_name() << ".";
262
263 manager_->UpdateService(service);
264}
265
266WiFiServiceRefPtr WiFiProvider::OnEndpointRemoved(
267 const WiFiEndpointConstRefPtr &endpoint) {
Paul Stewart6c351ff2013-02-25 15:13:03 -0800268 if (!running_) {
269 return NULL;
270 }
271
Paul Stewart3c504012013-01-17 17:49:58 -0800272 WiFiServiceRefPtr service = FindServiceForEndpoint(endpoint);
273
274 CHECK(service) << "Can't find Service for Endpoint "
275 << "(with BSSID " << endpoint->bssid_string() << ").";
276 SLOG(WiFi, 1) << "Removing endpoint " << endpoint->bssid_string()
277 << " from Service " << service->unique_name();
278 service->RemoveEndpoint(endpoint);
Paul Stewart0427cc12013-03-25 13:50:39 -0700279 service_by_endpoint_.erase(endpoint);
Paul Stewart3c504012013-01-17 17:49:58 -0800280
281 if (service->HasEndpoints() || service->IsRemembered()) {
282 // Keep services around if they are in a profile or have remaining
283 // endpoints.
284 manager_->UpdateService(service);
285 return NULL;
286 }
287
288 ForgetService(service);
289 manager_->DeregisterService(service);
290
291 return service;
292}
293
Paul Stewart0427cc12013-03-25 13:50:39 -0700294void WiFiProvider::OnEndpointUpdated(const WiFiEndpointConstRefPtr &endpoint) {
Paul Stewartb3be59c2013-08-21 21:03:12 -0700295 if (!running_) {
296 return;
297 }
298
Paul Stewart0427cc12013-03-25 13:50:39 -0700299 WiFiService *service = FindServiceForEndpoint(endpoint);
300 CHECK(service);
301
302 // If the service still matches the endpoint in its new configuration,
303 // we need only to update the service.
304 if (service->ssid() == endpoint->ssid() &&
305 service->mode() == endpoint->network_mode() &&
306 service->IsSecurityMatch(endpoint->security_mode())) {
307 service->NotifyEndpointUpdated(endpoint);
308 return;
309 }
310
311 // The endpoint no longer matches the associated service. Remove the
312 // endpoint, so current references to the endpoint are reset, then add
313 // it again so it can be associated with a new service.
314 OnEndpointRemoved(endpoint);
315 OnEndpointAdded(endpoint);
316}
317
Paul Stewart3c504012013-01-17 17:49:58 -0800318bool WiFiProvider::OnServiceUnloaded(const WiFiServiceRefPtr &service) {
319 // If the service still has endpoints, it should remain in the service list.
320 if (service->HasEndpoints()) {
321 return false;
322 }
323
324 // This is the one place where we forget the service but do not also
325 // deregister the service with the manager. However, by returning
326 // true below, the manager will do so itself.
327 ForgetService(service);
328 return true;
329}
330
Wade Guthrie60a37062013-04-02 11:39:09 -0700331void WiFiProvider::LoadAndFixupServiceEntries(
Paul Stewart3c504012013-01-17 17:49:58 -0800332 StoreInterface *storage, bool is_default_profile) {
333 if (WiFiService::FixupServiceEntries(storage)) {
334 storage->Flush();
335 Metrics::ServiceFixupProfileType profile_type =
336 is_default_profile ?
337 Metrics::kMetricServiceFixupDefaultProfile :
338 Metrics::kMetricServiceFixupUserProfile;
339 metrics_->SendEnumToUMA(
mukesh agrawal132e96f2014-04-24 11:49:42 -0700340 metrics_->GetFullMetricName(Metrics::kMetricServiceFixupEntriesSuffix,
Paul Stewart3c504012013-01-17 17:49:58 -0800341 Technology::kWifi),
342 profile_type,
343 Metrics::kMetricServiceFixupMax);
344 }
Wade Guthrie60a37062013-04-02 11:39:09 -0700345 // TODO(wdg): Determine how this should be structured for, currently
346 // non-existant, autotests. |kStorageFrequencies| should only exist in the
347 // default profile except for autotests where a test_profile is pushed. This
348 // may need to be modified for that case.
349 if (is_default_profile) {
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700350 COMPILE_ASSERT(kMaxStorageFrequencies > kWeeksToKeepFrequencyCounts,
351 persistently_storing_more_frequencies_than_we_can_hold);
352 total_frequency_connections_ = 0L;
353 connect_count_by_frequency_.clear();
354 time_t this_week = time_->GetSecondsSinceEpoch() / kSecondsPerWeek;
355 for (int freq = 0; freq < kMaxStorageFrequencies; ++freq) {
356 ConnectFrequencyMap connect_count_by_frequency;
357 string freq_string = StringPrintf("%s%d", kStorageFrequencies, freq);
358 vector<string> frequencies;
359 if (!storage->GetStringList(kStorageId, freq_string, &frequencies)) {
360 SLOG(WiFi, 7) << "Frequency list " << freq_string << " not found";
361 break;
362 }
363 time_t start_week = StringListToFrequencyMap(frequencies,
364 &connect_count_by_frequency);
365 if (start_week == kIllegalStartWeek) {
366 continue; // |StringListToFrequencyMap| will have output an error msg.
367 }
368
369 if (start_week > this_week) {
370 LOG(WARNING) << "Discarding frequency count info from the future";
371 continue;
372 }
373 connect_count_by_frequency_dated_[start_week] =
374 connect_count_by_frequency;
375
376 for (const auto &freq_count :
377 connect_count_by_frequency_dated_[start_week]) {
378 connect_count_by_frequency_[freq_count.first] += freq_count.second;
379 total_frequency_connections_ += freq_count.second;
Wade Guthrie60a37062013-04-02 11:39:09 -0700380 }
381 }
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700382 SLOG(WiFi, 7) << __func__ << " - total count="
383 << total_frequency_connections_;
Wade Guthrie60a37062013-04-02 11:39:09 -0700384 }
385}
386
387bool WiFiProvider::Save(StoreInterface *storage) const {
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700388 int freq = 0;
389 // Iterating backwards since I want to make sure that I get the newest data.
390 ConnectFrequencyMapDated::const_reverse_iterator freq_count;
391 for (freq_count = connect_count_by_frequency_dated_.crbegin();
392 freq_count != connect_count_by_frequency_dated_.crend();
393 ++freq_count) {
394 vector<string> frequencies;
395 FrequencyMapToStringList(freq_count->first, freq_count->second,
396 &frequencies);
397 string freq_string = StringPrintf("%s%d", kStorageFrequencies, freq);
398 storage->SetStringList(kStorageId, freq_string, frequencies);
399 if (++freq >= kMaxStorageFrequencies) {
400 LOG(WARNING) << "Internal frequency count list has more entries than the "
401 << "string list we had allocated for it.";
402 break;
403 }
404 }
Wade Guthrie60a37062013-04-02 11:39:09 -0700405 return true;
Paul Stewart3c504012013-01-17 17:49:58 -0800406}
407
408WiFiServiceRefPtr WiFiProvider::AddService(const vector<uint8_t> &ssid,
409 const string &mode,
410 const string &security,
411 bool is_hidden) {
412 WiFiServiceRefPtr service = new WiFiService(control_interface_,
413 dispatcher_,
414 metrics_,
415 manager_,
416 this,
417 ssid,
418 mode,
419 security,
420 is_hidden);
421
422 services_.push_back(service);
423 manager_->RegisterService(service);
424 return service;
425}
426
427WiFiServiceRefPtr WiFiProvider::FindService(const vector<uint8_t> &ssid,
428 const string &mode,
429 const string &security) const {
Paul Stewart6db7b242014-05-02 15:34:21 -0700430 for (const auto &service : services_) {
431 if (service->ssid() == ssid && service->mode() == mode &&
432 service->IsSecurityMatch(security)) {
433 return service;
Paul Stewart3c504012013-01-17 17:49:58 -0800434 }
435 }
Paul Stewart21f2aae2013-01-17 17:10:08 -0800436 return NULL;
437}
438
Paul Stewart3c504012013-01-17 17:49:58 -0800439ByteArrays WiFiProvider::GetHiddenSSIDList() {
440 // Create a unique set of hidden SSIDs.
441 set<ByteArray> hidden_ssids_set;
Paul Stewart6db7b242014-05-02 15:34:21 -0700442 for (const auto &service : services_) {
443 if (service->hidden_ssid() && service->IsRemembered()) {
444 hidden_ssids_set.insert(service->ssid());
Paul Stewart3c504012013-01-17 17:49:58 -0800445 }
446 }
447 SLOG(WiFi, 2) << "Found " << hidden_ssids_set.size() << " hidden services";
448 return ByteArrays(hidden_ssids_set.begin(), hidden_ssids_set.end());
449}
450
451void WiFiProvider::ForgetService(const WiFiServiceRefPtr &service) {
452 vector<WiFiServiceRefPtr>::iterator it;
453 it = std::find(services_.begin(), services_.end(), service);
454 if (it == services_.end()) {
455 return;
456 }
457 (*it)->ResetWiFi();
458 services_.erase(it);
459}
460
Paul Stewartd2e1c362013-03-03 19:06:07 -0800461// static
462bool WiFiProvider::GetServiceParametersFromArgs(const KeyValueStore &args,
463 vector<uint8_t> *ssid_bytes,
464 string *mode,
465 string *security_method,
466 bool *hidden_ssid,
467 Error *error) {
Ben Chanf024ef42013-09-20 14:21:38 -0700468 CHECK_EQ(args.LookupString(kTypeProperty, ""), kTypeWifi);
Paul Stewartd2e1c362013-03-03 19:06:07 -0800469
470 string mode_test =
Ben Chanf024ef42013-09-20 14:21:38 -0700471 args.LookupString(kModeProperty, kModeManaged);
Paul Stewartd2e1c362013-03-03 19:06:07 -0800472 if (!WiFiService::IsValidMode(mode_test)) {
473 Error::PopulateAndLog(error, Error::kNotSupported,
474 kManagerErrorUnsupportedServiceMode);
475 return false;
476 }
477
Paul Stewart4539d262013-10-10 12:56:31 -0700478 vector<uint8_t> ssid;
479 if (args.ContainsString(kWifiHexSsid)) {
480 string ssid_hex_string = args.GetString(kWifiHexSsid);
481 if (!base::HexStringToBytes(ssid_hex_string, &ssid)) {
482 Error::PopulateAndLog(error, Error::kInvalidArguments,
483 "Hex SSID parameter is not valid");
484 return false;
485 }
486 } else if (args.ContainsString(kSSIDProperty)) {
487 string ssid_string = args.GetString(kSSIDProperty);
488 ssid = vector<uint8_t>(ssid_string.begin(), ssid_string.end());
489 } else {
Paul Stewartd2e1c362013-03-03 19:06:07 -0800490 Error::PopulateAndLog(error, Error::kInvalidArguments,
491 kManagerErrorSSIDRequired);
492 return false;
493 }
494
Paul Stewart4539d262013-10-10 12:56:31 -0700495 if (ssid.size() < 1) {
Paul Stewartd2e1c362013-03-03 19:06:07 -0800496 Error::PopulateAndLog(error, Error::kInvalidNetworkName,
497 kManagerErrorSSIDTooShort);
498 return false;
499 }
500
Paul Stewart4539d262013-10-10 12:56:31 -0700501 if (ssid.size() > IEEE_80211::kMaxSSIDLen) {
Paul Stewartd2e1c362013-03-03 19:06:07 -0800502 Error::PopulateAndLog(error, Error::kInvalidNetworkName,
503 kManagerErrorSSIDTooLong);
504 return false;
505 }
506
Ben Chanf024ef42013-09-20 14:21:38 -0700507 string security_method_test = args.LookupString(kSecurityProperty,
508 kSecurityNone);
Paul Stewartd2e1c362013-03-03 19:06:07 -0800509
510 if (!WiFiService::IsValidSecurityMethod(security_method_test)) {
511 Error::PopulateAndLog(error, Error::kNotSupported,
512 kManagerErrorUnsupportedSecurityMode);
513 return false;
514 }
515
Paul Stewart4539d262013-10-10 12:56:31 -0700516 *ssid_bytes = ssid;
Paul Stewartd2e1c362013-03-03 19:06:07 -0800517 *mode = mode_test;
518 *security_method = security_method_test;
519
520 // If the caller hasn't specified otherwise, we assume it is a hidden service.
Ben Chanf024ef42013-09-20 14:21:38 -0700521 *hidden_ssid = args.LookupBool(kWifiHiddenSsid, true);
Paul Stewartd2e1c362013-03-03 19:06:07 -0800522
523 return true;
524}
525
Wade Guthrie60a37062013-04-02 11:39:09 -0700526// static
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700527time_t WiFiProvider::StringListToFrequencyMap(const vector<string> &strings,
Wade Guthrie60a37062013-04-02 11:39:09 -0700528 ConnectFrequencyMap *numbers) {
529 if (!numbers) {
530 LOG(ERROR) << "Null |numbers| parameter";
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700531 return kIllegalStartWeek;
Wade Guthrie60a37062013-04-02 11:39:09 -0700532 }
533
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700534 // Extract the start week from the first string.
535 vector<string>::const_iterator strings_it = strings.begin();
536 if (strings_it == strings.end()) {
537 SLOG(WiFi, 7) << "Empty |strings|.";
538 return kIllegalStartWeek;
Wade Guthrie60a37062013-04-02 11:39:09 -0700539 }
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700540 time_t start_week = GetStringListStartWeek(*strings_it);
541 if (start_week == kIllegalStartWeek) {
542 return kIllegalStartWeek;
543 }
544
545 // Extract the frequency:count values from the remaining strings.
546 for (++strings_it; strings_it != strings.end(); ++strings_it) {
547 ParseStringListFreqCount(*strings_it, numbers);
548 }
549 return start_week;
Wade Guthrie60a37062013-04-02 11:39:09 -0700550}
551
552// static
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700553time_t WiFiProvider::GetStringListStartWeek(const string &week_string) {
554 if (!StartsWithASCII(week_string, kStartWeekHeader, false)) {
555 LOG(ERROR) << "Found no leading '" << kStartWeekHeader << "' in '"
556 << week_string << "'";
557 return kIllegalStartWeek;
558 }
559 return atoll(week_string.c_str() + 1);
560}
561
562// static
563void WiFiProvider::ParseStringListFreqCount(const string &freq_count_string,
564 ConnectFrequencyMap *numbers) {
565 vector<string> freq_count;
566 SplitString(freq_count_string, kFrequencyDelimiter, &freq_count);
567 if (freq_count.size() != 2) {
568 LOG(WARNING) << "Found " << freq_count.size() - 1 << " '"
569 << kFrequencyDelimiter << "' in '" << freq_count_string
570 << "'. Expected 1.";
571 return;
572 }
573 uint16 freq = atoi(freq_count[0].c_str());
574 uint64 connections = atoll(freq_count[1].c_str());
575 (*numbers)[freq] = connections;
576}
577
578// static
579void WiFiProvider::FrequencyMapToStringList(time_t start_week,
580 const ConnectFrequencyMap &numbers,
Wade Guthrie60a37062013-04-02 11:39:09 -0700581 vector<string> *strings) {
582 if (!strings) {
583 LOG(ERROR) << "Null |strings| parameter";
584 return;
585 }
586
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700587 strings->push_back(StringPrintf("%s%" PRIu64, kStartWeekHeader,
588 static_cast<uint64_t>(start_week)));
589
590 for (const auto &freq_conn : numbers) {
Wade Guthrie60a37062013-04-02 11:39:09 -0700591 // Use base::Int64ToString() instead of using something like "%llu"
592 // (not correct for native 64 bit architectures) or PRId64 (does not
593 // work correctly using cros_workon_make due to include intricacies).
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700594 strings->push_back(StringPrintf("%u%c%s",
595 freq_conn.first, kFrequencyDelimiter,
596 base::Int64ToString(freq_conn.second).c_str()));
Wade Guthrie60a37062013-04-02 11:39:09 -0700597 }
598}
599
600void WiFiProvider::IncrementConnectCount(uint16 frequency_mhz) {
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700601 CHECK(total_frequency_connections_ < std::numeric_limits<int64_t>::max());
Wade Guthrie60a37062013-04-02 11:39:09 -0700602
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700603 ++connect_count_by_frequency_[frequency_mhz];
Wade Guthrie60a37062013-04-02 11:39:09 -0700604 ++total_frequency_connections_;
Wade Guthrie60a37062013-04-02 11:39:09 -0700605
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700606 time_t this_week = time_->GetSecondsSinceEpoch() / kSecondsPerWeek;
607 ++connect_count_by_frequency_dated_[this_week][frequency_mhz];
608
609 ConnectFrequencyMapDated::iterator oldest =
610 connect_count_by_frequency_dated_.begin();
611 time_t oldest_legal_week = this_week - kWeeksToKeepFrequencyCounts;
612 while (oldest->first < oldest_legal_week) {
Wade Guthrie086eb1e2013-05-31 17:31:13 -0700613 SLOG(WiFi, 6) << "Discarding frequency count info that's "
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700614 << this_week - oldest->first << " weeks old";
615 for (const auto &freq_count : oldest->second) {
616 connect_count_by_frequency_[freq_count.first] -= freq_count.second;
617 if (connect_count_by_frequency_[freq_count.first] <= 0) {
618 connect_count_by_frequency_.erase(freq_count.first);
619 }
620 total_frequency_connections_ -= freq_count.second;
621 }
622 connect_count_by_frequency_dated_.erase(oldest);
623 oldest = connect_count_by_frequency_dated_.begin();
624 }
625
626 manager_->UpdateWiFiProvider();
Wade Guthrie60a37062013-04-02 11:39:09 -0700627 metrics_->SendToUMA(
628 Metrics::kMetricFrequenciesConnectedEver,
629 connect_count_by_frequency_.size(),
630 Metrics::kMetricFrequenciesConnectedMin,
631 Metrics::kMetricFrequenciesConnectedMax,
632 Metrics::kMetricFrequenciesConnectedNumBuckets);
633}
634
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700635WiFiProvider::FrequencyCountList WiFiProvider::GetScanFrequencies() const {
636 FrequencyCountList freq_connects_list;
637 for (const auto freq_count : connect_count_by_frequency_) {
638 freq_connects_list.push_back(FrequencyCount(freq_count.first,
639 freq_count.second));
640 }
641 return freq_connects_list;
642}
643
Peter Qiu574996a2014-04-04 10:55:47 -0700644void WiFiProvider::ReportAutoConnectableServices() {
645 const char *reason = NULL;
646 int num_services = 0;
647
648 // Determine the number of services available for auto-connect.
649 for (const auto &service : services_) {
650 // Service is available for auto connect if it is configured for auto
651 // connect, and is auto-connectable.
652 if (service->auto_connect() && service->IsAutoConnectable(&reason)) {
653 num_services++;
654 }
655 }
656
657 // Only report stats when there are wifi services available.
658 if (num_services) {
659 metrics_->NotifyWifiAutoConnectableServices(num_services);
660 }
661}
Paul Stewart21f2aae2013-01-17 17:10:08 -0800662} // namespace shill