blob: 94515dfcae07b8b276f955f3fef97721f617c5de [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 set<string> groups = storage->GetGroupsWithProperties(args);
101 bool created_hidden_service = false;
102 for (set<string>::const_iterator it = groups.begin(); it != groups.end();
103 ++it) {
104 string ssid_hex;
105 vector<uint8_t> ssid_bytes;
106 if (!storage->GetString(*it, WiFiService::kStorageSSID, &ssid_hex) ||
107 !base::HexStringToBytes(ssid_hex, &ssid_bytes)) {
108 SLOG(WiFi, 2) << "Storage group " << *it << " is missing valid \""
109 << WiFiService::kStorageSSID << "\" property";
110 continue;
111 }
112 string network_mode;
113 if (!storage->GetString(*it, WiFiService::kStorageMode, &network_mode) ||
114 network_mode.empty()) {
115 SLOG(WiFi, 2) << "Storage group " << *it << " is missing \""
116 << WiFiService::kStorageMode << "\" property";
117 continue;
118 }
119 string security;
120 if (!storage->GetString(*it, WiFiService::kStorageSecurity, &security) ||
121 !WiFiService::IsValidSecurityMethod(security)) {
122 SLOG(WiFi, 2) << "Storage group " << *it << " has missing or invalid \""
123 << WiFiService::kStorageSecurity << "\" property";
124 continue;
125 }
126 bool is_hidden = false;
127 if (!storage->GetBool(*it, WiFiService::kStorageHiddenSSID, &is_hidden)) {
128 SLOG(WiFi, 2) << "Storage group " << *it << " is missing \""
129 << WiFiService::kStorageHiddenSSID << "\" property";
130 continue;
131 }
132
133 if (FindService(ssid_bytes, network_mode, security)) {
134 // If service already exists, we have nothing to do, since the
135 // service has already loaded its configuration from storage.
136 // This is guaranteed to happen in the single case where
137 // CreateServicesFromProfile() is called on a WiFiProvider from
138 // Manager::PushProfile():
139 continue;
140 }
141
142 AddService(ssid_bytes, network_mode, security, is_hidden);
143
144 // By registering the service in AddService, the rest of the configuration
145 // will be loaded from the profile into the service via ConfigureService().
146
147 if (is_hidden) {
148 created_hidden_service = true;
149 }
150 }
151
152 // If WiFi is unconnected and we created a hidden service as a result
153 // of opening the profile, we should initiate a WiFi scan, which will
154 // allow us to find any hidden services that we may have created.
155 if (created_hidden_service &&
156 !manager_->IsTechnologyConnected(Technology::kWifi)) {
157 Error unused_error;
Ben Chanf024ef42013-09-20 14:21:38 -0700158 manager_->RequestScan(Device::kProgressiveScan, kTypeWifi, &unused_error);
Paul Stewart3c504012013-01-17 17:49:58 -0800159 }
Paul Stewart21f2aae2013-01-17 17:10:08 -0800160}
161
Paul Stewart0e51ad92013-07-26 14:42:55 -0700162ServiceRefPtr WiFiProvider::FindSimilarService(
Paul Stewartd2e1c362013-03-03 19:06:07 -0800163 const KeyValueStore &args, Error *error) const {
164 vector<uint8_t> ssid;
165 string mode;
166 string security;
167 bool hidden_ssid;
168
169 if (!GetServiceParametersFromArgs(
170 args, &ssid, &mode, &security, &hidden_ssid, error)) {
171 return NULL;
172 }
173
174 WiFiServiceRefPtr service(FindService(ssid, mode, security));
175 if (!service) {
176 error->Populate(Error::kNotFound, "Matching service was not found");
177 }
178
179 return service;
180}
181
Paul Stewart0e51ad92013-07-26 14:42:55 -0700182ServiceRefPtr WiFiProvider::CreateTemporaryService(
Paul Stewartd2e1c362013-03-03 19:06:07 -0800183 const KeyValueStore &args, Error *error) {
184 vector<uint8_t> ssid;
185 string mode;
186 string security;
187 bool hidden_ssid;
188
189 if (!GetServiceParametersFromArgs(
190 args, &ssid, &mode, &security, &hidden_ssid, error)) {
191 return NULL;
192 }
193
194 return new WiFiService(control_interface_,
195 dispatcher_,
196 metrics_,
197 manager_,
198 this,
199 ssid,
200 mode,
201 security,
202 hidden_ssid);
203}
204
Paul Stewart0e51ad92013-07-26 14:42:55 -0700205ServiceRefPtr WiFiProvider::GetService(
206 const KeyValueStore &args, Error *error) {
207 return GetWiFiService(args, error);
208}
209
210WiFiServiceRefPtr WiFiProvider::GetWiFiService(
Paul Stewart21f2aae2013-01-17 17:10:08 -0800211 const KeyValueStore &args, Error *error) {
Paul Stewartd2e1c362013-03-03 19:06:07 -0800212 vector<uint8_t> ssid_bytes;
213 string mode;
214 string security_method;
215 bool hidden_ssid;
Paul Stewart3c504012013-01-17 17:49:58 -0800216
Paul Stewartd2e1c362013-03-03 19:06:07 -0800217 if (!GetServiceParametersFromArgs(
218 args, &ssid_bytes, &mode, &security_method, &hidden_ssid, error)) {
Paul Stewart3c504012013-01-17 17:49:58 -0800219 return NULL;
220 }
221
Paul Stewartd2e1c362013-03-03 19:06:07 -0800222 WiFiServiceRefPtr service(FindService(ssid_bytes, mode, security_method));
Paul Stewart3c504012013-01-17 17:49:58 -0800223 if (!service) {
224 service = AddService(ssid_bytes,
Paul Stewartd2e1c362013-03-03 19:06:07 -0800225 mode,
Paul Stewart3c504012013-01-17 17:49:58 -0800226 security_method,
227 hidden_ssid);
228 }
229
230 return service;
Paul Stewart21f2aae2013-01-17 17:10:08 -0800231}
232
233WiFiServiceRefPtr WiFiProvider::FindServiceForEndpoint(
Paul Stewart3c504012013-01-17 17:49:58 -0800234 const WiFiEndpointConstRefPtr &endpoint) {
Paul Stewart0427cc12013-03-25 13:50:39 -0700235 EndpointServiceMap::iterator service_it =
236 service_by_endpoint_.find(endpoint);
237 if (service_it == service_by_endpoint_.end())
238 return NULL;
239 return service_it->second;
Paul Stewart3c504012013-01-17 17:49:58 -0800240}
241
242void WiFiProvider::OnEndpointAdded(const WiFiEndpointConstRefPtr &endpoint) {
Paul Stewart6c351ff2013-02-25 15:13:03 -0800243 if (!running_) {
244 return;
245 }
246
Paul Stewart0427cc12013-03-25 13:50:39 -0700247 WiFiServiceRefPtr service = FindService(endpoint->ssid(),
248 endpoint->network_mode(),
249 endpoint->security_mode());
Paul Stewart3c504012013-01-17 17:49:58 -0800250 if (!service) {
251 const bool hidden_ssid = false;
Paul Stewart08a54eb2013-03-11 12:07:36 -0700252 service = AddService(
253 endpoint->ssid(),
254 endpoint->network_mode(),
255 WiFiService::GetSecurityClass(endpoint->security_mode()),
256 hidden_ssid);
Paul Stewart3c504012013-01-17 17:49:58 -0800257 }
258
259 service->AddEndpoint(endpoint);
Paul Stewart0427cc12013-03-25 13:50:39 -0700260 service_by_endpoint_[endpoint] = service;
Paul Stewart3c504012013-01-17 17:49:58 -0800261
262 SLOG(WiFi, 1) << "Assigned endpoint " << endpoint->bssid_string()
263 << " to service " << service->unique_name() << ".";
264
265 manager_->UpdateService(service);
266}
267
268WiFiServiceRefPtr WiFiProvider::OnEndpointRemoved(
269 const WiFiEndpointConstRefPtr &endpoint) {
Paul Stewart6c351ff2013-02-25 15:13:03 -0800270 if (!running_) {
271 return NULL;
272 }
273
Paul Stewart3c504012013-01-17 17:49:58 -0800274 WiFiServiceRefPtr service = FindServiceForEndpoint(endpoint);
275
276 CHECK(service) << "Can't find Service for Endpoint "
277 << "(with BSSID " << endpoint->bssid_string() << ").";
278 SLOG(WiFi, 1) << "Removing endpoint " << endpoint->bssid_string()
279 << " from Service " << service->unique_name();
280 service->RemoveEndpoint(endpoint);
Paul Stewart0427cc12013-03-25 13:50:39 -0700281 service_by_endpoint_.erase(endpoint);
Paul Stewart3c504012013-01-17 17:49:58 -0800282
283 if (service->HasEndpoints() || service->IsRemembered()) {
284 // Keep services around if they are in a profile or have remaining
285 // endpoints.
286 manager_->UpdateService(service);
287 return NULL;
288 }
289
290 ForgetService(service);
291 manager_->DeregisterService(service);
292
293 return service;
294}
295
Paul Stewart0427cc12013-03-25 13:50:39 -0700296void WiFiProvider::OnEndpointUpdated(const WiFiEndpointConstRefPtr &endpoint) {
Paul Stewartb3be59c2013-08-21 21:03:12 -0700297 if (!running_) {
298 return;
299 }
300
Paul Stewart0427cc12013-03-25 13:50:39 -0700301 WiFiService *service = FindServiceForEndpoint(endpoint);
302 CHECK(service);
303
304 // If the service still matches the endpoint in its new configuration,
305 // we need only to update the service.
306 if (service->ssid() == endpoint->ssid() &&
307 service->mode() == endpoint->network_mode() &&
308 service->IsSecurityMatch(endpoint->security_mode())) {
309 service->NotifyEndpointUpdated(endpoint);
310 return;
311 }
312
313 // The endpoint no longer matches the associated service. Remove the
314 // endpoint, so current references to the endpoint are reset, then add
315 // it again so it can be associated with a new service.
316 OnEndpointRemoved(endpoint);
317 OnEndpointAdded(endpoint);
318}
319
Paul Stewart3c504012013-01-17 17:49:58 -0800320bool WiFiProvider::OnServiceUnloaded(const WiFiServiceRefPtr &service) {
321 // If the service still has endpoints, it should remain in the service list.
322 if (service->HasEndpoints()) {
323 return false;
324 }
325
326 // This is the one place where we forget the service but do not also
327 // deregister the service with the manager. However, by returning
328 // true below, the manager will do so itself.
329 ForgetService(service);
330 return true;
331}
332
Wade Guthrie60a37062013-04-02 11:39:09 -0700333void WiFiProvider::LoadAndFixupServiceEntries(
Paul Stewart3c504012013-01-17 17:49:58 -0800334 StoreInterface *storage, bool is_default_profile) {
335 if (WiFiService::FixupServiceEntries(storage)) {
336 storage->Flush();
337 Metrics::ServiceFixupProfileType profile_type =
338 is_default_profile ?
339 Metrics::kMetricServiceFixupDefaultProfile :
340 Metrics::kMetricServiceFixupUserProfile;
341 metrics_->SendEnumToUMA(
342 metrics_->GetFullMetricName(Metrics::kMetricServiceFixupEntries,
343 Technology::kWifi),
344 profile_type,
345 Metrics::kMetricServiceFixupMax);
346 }
Wade Guthrie60a37062013-04-02 11:39:09 -0700347 // TODO(wdg): Determine how this should be structured for, currently
348 // non-existant, autotests. |kStorageFrequencies| should only exist in the
349 // default profile except for autotests where a test_profile is pushed. This
350 // may need to be modified for that case.
351 if (is_default_profile) {
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700352 COMPILE_ASSERT(kMaxStorageFrequencies > kWeeksToKeepFrequencyCounts,
353 persistently_storing_more_frequencies_than_we_can_hold);
354 total_frequency_connections_ = 0L;
355 connect_count_by_frequency_.clear();
356 time_t this_week = time_->GetSecondsSinceEpoch() / kSecondsPerWeek;
357 for (int freq = 0; freq < kMaxStorageFrequencies; ++freq) {
358 ConnectFrequencyMap connect_count_by_frequency;
359 string freq_string = StringPrintf("%s%d", kStorageFrequencies, freq);
360 vector<string> frequencies;
361 if (!storage->GetStringList(kStorageId, freq_string, &frequencies)) {
362 SLOG(WiFi, 7) << "Frequency list " << freq_string << " not found";
363 break;
364 }
365 time_t start_week = StringListToFrequencyMap(frequencies,
366 &connect_count_by_frequency);
367 if (start_week == kIllegalStartWeek) {
368 continue; // |StringListToFrequencyMap| will have output an error msg.
369 }
370
371 if (start_week > this_week) {
372 LOG(WARNING) << "Discarding frequency count info from the future";
373 continue;
374 }
375 connect_count_by_frequency_dated_[start_week] =
376 connect_count_by_frequency;
377
378 for (const auto &freq_count :
379 connect_count_by_frequency_dated_[start_week]) {
380 connect_count_by_frequency_[freq_count.first] += freq_count.second;
381 total_frequency_connections_ += freq_count.second;
Wade Guthrie60a37062013-04-02 11:39:09 -0700382 }
383 }
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700384 SLOG(WiFi, 7) << __func__ << " - total count="
385 << total_frequency_connections_;
Wade Guthrie60a37062013-04-02 11:39:09 -0700386 }
387}
388
389bool WiFiProvider::Save(StoreInterface *storage) const {
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700390 int freq = 0;
391 // Iterating backwards since I want to make sure that I get the newest data.
392 ConnectFrequencyMapDated::const_reverse_iterator freq_count;
393 for (freq_count = connect_count_by_frequency_dated_.crbegin();
394 freq_count != connect_count_by_frequency_dated_.crend();
395 ++freq_count) {
396 vector<string> frequencies;
397 FrequencyMapToStringList(freq_count->first, freq_count->second,
398 &frequencies);
399 string freq_string = StringPrintf("%s%d", kStorageFrequencies, freq);
400 storage->SetStringList(kStorageId, freq_string, frequencies);
401 if (++freq >= kMaxStorageFrequencies) {
402 LOG(WARNING) << "Internal frequency count list has more entries than the "
403 << "string list we had allocated for it.";
404 break;
405 }
406 }
Wade Guthrie60a37062013-04-02 11:39:09 -0700407 return true;
Paul Stewart3c504012013-01-17 17:49:58 -0800408}
409
410WiFiServiceRefPtr WiFiProvider::AddService(const vector<uint8_t> &ssid,
411 const string &mode,
412 const string &security,
413 bool is_hidden) {
414 WiFiServiceRefPtr service = new WiFiService(control_interface_,
415 dispatcher_,
416 metrics_,
417 manager_,
418 this,
419 ssid,
420 mode,
421 security,
422 is_hidden);
423
424 services_.push_back(service);
425 manager_->RegisterService(service);
426 return service;
427}
428
429WiFiServiceRefPtr WiFiProvider::FindService(const vector<uint8_t> &ssid,
430 const string &mode,
431 const string &security) const {
432 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
433 it != services_.end();
434 ++it) {
435 if ((*it)->ssid() == ssid && (*it)->mode() == mode &&
436 (*it)->IsSecurityMatch(security)) {
437 return *it;
438 }
439 }
Paul Stewart21f2aae2013-01-17 17:10:08 -0800440 return NULL;
441}
442
Paul Stewart3c504012013-01-17 17:49:58 -0800443ByteArrays WiFiProvider::GetHiddenSSIDList() {
444 // Create a unique set of hidden SSIDs.
445 set<ByteArray> hidden_ssids_set;
446 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
447 it != services_.end();
448 ++it) {
449 if ((*it)->hidden_ssid() && (*it)->IsRemembered()) {
450 hidden_ssids_set.insert((*it)->ssid());
451 }
452 }
453 SLOG(WiFi, 2) << "Found " << hidden_ssids_set.size() << " hidden services";
454 return ByteArrays(hidden_ssids_set.begin(), hidden_ssids_set.end());
455}
456
457void WiFiProvider::ForgetService(const WiFiServiceRefPtr &service) {
458 vector<WiFiServiceRefPtr>::iterator it;
459 it = std::find(services_.begin(), services_.end(), service);
460 if (it == services_.end()) {
461 return;
462 }
463 (*it)->ResetWiFi();
464 services_.erase(it);
465}
466
Paul Stewartd2e1c362013-03-03 19:06:07 -0800467// static
468bool WiFiProvider::GetServiceParametersFromArgs(const KeyValueStore &args,
469 vector<uint8_t> *ssid_bytes,
470 string *mode,
471 string *security_method,
472 bool *hidden_ssid,
473 Error *error) {
Ben Chanf024ef42013-09-20 14:21:38 -0700474 CHECK_EQ(args.LookupString(kTypeProperty, ""), kTypeWifi);
Paul Stewartd2e1c362013-03-03 19:06:07 -0800475
476 string mode_test =
Ben Chanf024ef42013-09-20 14:21:38 -0700477 args.LookupString(kModeProperty, kModeManaged);
Paul Stewartd2e1c362013-03-03 19:06:07 -0800478 if (!WiFiService::IsValidMode(mode_test)) {
479 Error::PopulateAndLog(error, Error::kNotSupported,
480 kManagerErrorUnsupportedServiceMode);
481 return false;
482 }
483
Paul Stewart4539d262013-10-10 12:56:31 -0700484 vector<uint8_t> ssid;
485 if (args.ContainsString(kWifiHexSsid)) {
486 string ssid_hex_string = args.GetString(kWifiHexSsid);
487 if (!base::HexStringToBytes(ssid_hex_string, &ssid)) {
488 Error::PopulateAndLog(error, Error::kInvalidArguments,
489 "Hex SSID parameter is not valid");
490 return false;
491 }
492 } else if (args.ContainsString(kSSIDProperty)) {
493 string ssid_string = args.GetString(kSSIDProperty);
494 ssid = vector<uint8_t>(ssid_string.begin(), ssid_string.end());
495 } else {
Paul Stewartd2e1c362013-03-03 19:06:07 -0800496 Error::PopulateAndLog(error, Error::kInvalidArguments,
497 kManagerErrorSSIDRequired);
498 return false;
499 }
500
Paul Stewart4539d262013-10-10 12:56:31 -0700501 if (ssid.size() < 1) {
Paul Stewartd2e1c362013-03-03 19:06:07 -0800502 Error::PopulateAndLog(error, Error::kInvalidNetworkName,
503 kManagerErrorSSIDTooShort);
504 return false;
505 }
506
Paul Stewart4539d262013-10-10 12:56:31 -0700507 if (ssid.size() > IEEE_80211::kMaxSSIDLen) {
Paul Stewartd2e1c362013-03-03 19:06:07 -0800508 Error::PopulateAndLog(error, Error::kInvalidNetworkName,
509 kManagerErrorSSIDTooLong);
510 return false;
511 }
512
Ben Chanf024ef42013-09-20 14:21:38 -0700513 string security_method_test = args.LookupString(kSecurityProperty,
514 kSecurityNone);
Paul Stewartd2e1c362013-03-03 19:06:07 -0800515
516 if (!WiFiService::IsValidSecurityMethod(security_method_test)) {
517 Error::PopulateAndLog(error, Error::kNotSupported,
518 kManagerErrorUnsupportedSecurityMode);
519 return false;
520 }
521
Paul Stewart4539d262013-10-10 12:56:31 -0700522 *ssid_bytes = ssid;
Paul Stewartd2e1c362013-03-03 19:06:07 -0800523 *mode = mode_test;
524 *security_method = security_method_test;
525
526 // If the caller hasn't specified otherwise, we assume it is a hidden service.
Ben Chanf024ef42013-09-20 14:21:38 -0700527 *hidden_ssid = args.LookupBool(kWifiHiddenSsid, true);
Paul Stewartd2e1c362013-03-03 19:06:07 -0800528
529 return true;
530}
531
Wade Guthrie60a37062013-04-02 11:39:09 -0700532// static
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700533time_t WiFiProvider::StringListToFrequencyMap(const vector<string> &strings,
Wade Guthrie60a37062013-04-02 11:39:09 -0700534 ConnectFrequencyMap *numbers) {
535 if (!numbers) {
536 LOG(ERROR) << "Null |numbers| parameter";
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700537 return kIllegalStartWeek;
Wade Guthrie60a37062013-04-02 11:39:09 -0700538 }
539
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700540 // Extract the start week from the first string.
541 vector<string>::const_iterator strings_it = strings.begin();
542 if (strings_it == strings.end()) {
543 SLOG(WiFi, 7) << "Empty |strings|.";
544 return kIllegalStartWeek;
Wade Guthrie60a37062013-04-02 11:39:09 -0700545 }
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700546 time_t start_week = GetStringListStartWeek(*strings_it);
547 if (start_week == kIllegalStartWeek) {
548 return kIllegalStartWeek;
549 }
550
551 // Extract the frequency:count values from the remaining strings.
552 for (++strings_it; strings_it != strings.end(); ++strings_it) {
553 ParseStringListFreqCount(*strings_it, numbers);
554 }
555 return start_week;
Wade Guthrie60a37062013-04-02 11:39:09 -0700556}
557
558// static
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700559time_t WiFiProvider::GetStringListStartWeek(const string &week_string) {
560 if (!StartsWithASCII(week_string, kStartWeekHeader, false)) {
561 LOG(ERROR) << "Found no leading '" << kStartWeekHeader << "' in '"
562 << week_string << "'";
563 return kIllegalStartWeek;
564 }
565 return atoll(week_string.c_str() + 1);
566}
567
568// static
569void WiFiProvider::ParseStringListFreqCount(const string &freq_count_string,
570 ConnectFrequencyMap *numbers) {
571 vector<string> freq_count;
572 SplitString(freq_count_string, kFrequencyDelimiter, &freq_count);
573 if (freq_count.size() != 2) {
574 LOG(WARNING) << "Found " << freq_count.size() - 1 << " '"
575 << kFrequencyDelimiter << "' in '" << freq_count_string
576 << "'. Expected 1.";
577 return;
578 }
579 uint16 freq = atoi(freq_count[0].c_str());
580 uint64 connections = atoll(freq_count[1].c_str());
581 (*numbers)[freq] = connections;
582}
583
584// static
585void WiFiProvider::FrequencyMapToStringList(time_t start_week,
586 const ConnectFrequencyMap &numbers,
Wade Guthrie60a37062013-04-02 11:39:09 -0700587 vector<string> *strings) {
588 if (!strings) {
589 LOG(ERROR) << "Null |strings| parameter";
590 return;
591 }
592
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700593 strings->push_back(StringPrintf("%s%" PRIu64, kStartWeekHeader,
594 static_cast<uint64_t>(start_week)));
595
596 for (const auto &freq_conn : numbers) {
Wade Guthrie60a37062013-04-02 11:39:09 -0700597 // Use base::Int64ToString() instead of using something like "%llu"
598 // (not correct for native 64 bit architectures) or PRId64 (does not
599 // work correctly using cros_workon_make due to include intricacies).
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700600 strings->push_back(StringPrintf("%u%c%s",
601 freq_conn.first, kFrequencyDelimiter,
602 base::Int64ToString(freq_conn.second).c_str()));
Wade Guthrie60a37062013-04-02 11:39:09 -0700603 }
604}
605
606void WiFiProvider::IncrementConnectCount(uint16 frequency_mhz) {
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700607 CHECK(total_frequency_connections_ < std::numeric_limits<int64_t>::max());
Wade Guthrie60a37062013-04-02 11:39:09 -0700608
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700609 ++connect_count_by_frequency_[frequency_mhz];
Wade Guthrie60a37062013-04-02 11:39:09 -0700610 ++total_frequency_connections_;
Wade Guthrie60a37062013-04-02 11:39:09 -0700611
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700612 time_t this_week = time_->GetSecondsSinceEpoch() / kSecondsPerWeek;
613 ++connect_count_by_frequency_dated_[this_week][frequency_mhz];
614
615 ConnectFrequencyMapDated::iterator oldest =
616 connect_count_by_frequency_dated_.begin();
617 time_t oldest_legal_week = this_week - kWeeksToKeepFrequencyCounts;
618 while (oldest->first < oldest_legal_week) {
Wade Guthrie086eb1e2013-05-31 17:31:13 -0700619 SLOG(WiFi, 6) << "Discarding frequency count info that's "
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700620 << this_week - oldest->first << " weeks old";
621 for (const auto &freq_count : oldest->second) {
622 connect_count_by_frequency_[freq_count.first] -= freq_count.second;
623 if (connect_count_by_frequency_[freq_count.first] <= 0) {
624 connect_count_by_frequency_.erase(freq_count.first);
625 }
626 total_frequency_connections_ -= freq_count.second;
627 }
628 connect_count_by_frequency_dated_.erase(oldest);
629 oldest = connect_count_by_frequency_dated_.begin();
630 }
631
632 manager_->UpdateWiFiProvider();
Wade Guthrie60a37062013-04-02 11:39:09 -0700633 metrics_->SendToUMA(
634 Metrics::kMetricFrequenciesConnectedEver,
635 connect_count_by_frequency_.size(),
636 Metrics::kMetricFrequenciesConnectedMin,
637 Metrics::kMetricFrequenciesConnectedMax,
638 Metrics::kMetricFrequenciesConnectedNumBuckets);
639}
640
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700641WiFiProvider::FrequencyCountList WiFiProvider::GetScanFrequencies() const {
642 FrequencyCountList freq_connects_list;
643 for (const auto freq_count : connect_count_by_frequency_) {
644 freq_connects_list.push_back(FrequencyCount(freq_count.first,
645 freq_count.second));
646 }
647 return freq_connects_list;
648}
649
Peter Qiu574996a2014-04-04 10:55:47 -0700650void WiFiProvider::ReportAutoConnectableServices() {
651 const char *reason = NULL;
652 int num_services = 0;
653
654 // Determine the number of services available for auto-connect.
655 for (const auto &service : services_) {
656 // Service is available for auto connect if it is configured for auto
657 // connect, and is auto-connectable.
658 if (service->auto_connect() && service->IsAutoConnectable(&reason)) {
659 num_services++;
660 }
661 }
662
663 // Only report stats when there are wifi services available.
664 if (num_services) {
665 metrics_->NotifyWifiAutoConnectableServices(num_services);
666 }
667}
Paul Stewart21f2aae2013-01-17 17:10:08 -0800668} // namespace shill