blob: 2fda3b45da98c589fe39e0bda66cc0346eaf4569 [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>
Paul Stewart3c504012013-01-17 17:49:58 -080016#include <base/string_number_conversions.h>
Wade Guthrie7c2d34e2013-05-09 14:02:20 -070017#include <base/string_split.h>
18#include <base/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;
Paul Stewart3c504012013-01-17 17:49:58 -080036using std::set;
37using std::string;
38using std::vector;
39
Paul Stewart21f2aae2013-01-17 17:10:08 -080040namespace shill {
41
Paul Stewart3c504012013-01-17 17:49:58 -080042// Note that WiFiProvider generates some manager-level errors, because it
43// implements the WiFi portion of the Manager.GetService flimflam API. The
44// API is implemented here, rather than in manager, to keep WiFi-specific
45// logic in the right place.
46const char WiFiProvider::kManagerErrorSSIDRequired[] = "must specify SSID";
47const char WiFiProvider::kManagerErrorSSIDTooLong[] = "SSID is too long";
48const char WiFiProvider::kManagerErrorSSIDTooShort[] = "SSID is too short";
49const char WiFiProvider::kManagerErrorUnsupportedSecurityMode[] =
50 "security mode is unsupported";
51const char WiFiProvider::kManagerErrorUnsupportedServiceMode[] =
52 "service mode is unsupported";
Wade Guthrie7c2d34e2013-05-09 14:02:20 -070053const char WiFiProvider::kFrequencyDelimiter = ':';
54const char WiFiProvider::kStartWeekHeader[] = "@";
55const time_t WiFiProvider::kIllegalStartWeek =
56 std::numeric_limits<time_t>::max();
Jason Abele28666542013-05-15 11:58:21 -070057const char WiFiProvider::kStorageId[] = "provider_of_wifi";
Wade Guthrie60a37062013-04-02 11:39:09 -070058const char WiFiProvider::kStorageFrequencies[] = "Frequencies";
Wade Guthrie7c2d34e2013-05-09 14:02:20 -070059const int WiFiProvider::kMaxStorageFrequencies = 20;
60const time_t WiFiProvider::kWeeksToKeepFrequencyCounts = 3;
61const time_t WiFiProvider::kSecondsPerWeek = 60 * 60 * 24 * 7;
Paul Stewart3c504012013-01-17 17:49:58 -080062
Paul Stewart21f2aae2013-01-17 17:10:08 -080063WiFiProvider::WiFiProvider(ControlInterface *control_interface,
64 EventDispatcher *dispatcher,
65 Metrics *metrics,
66 Manager *manager)
67 : control_interface_(control_interface),
68 dispatcher_(dispatcher),
69 metrics_(metrics),
Paul Stewart6c351ff2013-02-25 15:13:03 -080070 manager_(manager),
Wade Guthrie60a37062013-04-02 11:39:09 -070071 running_(false),
Wade Guthrie7c2d34e2013-05-09 14:02:20 -070072 total_frequency_connections_(-1L),
73 time_(Time::GetInstance()) {}
Paul Stewart21f2aae2013-01-17 17:10:08 -080074
75WiFiProvider::~WiFiProvider() {}
76
Paul Stewart6c351ff2013-02-25 15:13:03 -080077void WiFiProvider::Start() {
78 running_ = true;
79}
Paul Stewart21f2aae2013-01-17 17:10:08 -080080
81void WiFiProvider::Stop() {
Paul Stewart3c504012013-01-17 17:49:58 -080082 SLOG(WiFi, 2) << __func__;
83 while (!services_.empty()) {
84 WiFiServiceRefPtr service = services_.back();
85 ForgetService(service);
86 SLOG(WiFi, 3) << "WiFiProvider deregistering service "
87 << service->unique_name();
88 manager_->DeregisterService(service);
89 }
Paul Stewart0427cc12013-03-25 13:50:39 -070090 service_by_endpoint_.clear();
Paul Stewart6c351ff2013-02-25 15:13:03 -080091 running_ = false;
Paul Stewart21f2aae2013-01-17 17:10:08 -080092}
93
Paul Stewart3c504012013-01-17 17:49:58 -080094void WiFiProvider::CreateServicesFromProfile(const ProfileRefPtr &profile) {
95 const StoreInterface *storage = profile->GetConstStorage();
96 KeyValueStore args;
97 args.SetString(flimflam::kTypeProperty, flimflam::kTypeWifi);
98 set<string> groups = storage->GetGroupsWithProperties(args);
99 bool created_hidden_service = false;
100 for (set<string>::const_iterator it = groups.begin(); it != groups.end();
101 ++it) {
102 string ssid_hex;
103 vector<uint8_t> ssid_bytes;
104 if (!storage->GetString(*it, WiFiService::kStorageSSID, &ssid_hex) ||
105 !base::HexStringToBytes(ssid_hex, &ssid_bytes)) {
106 SLOG(WiFi, 2) << "Storage group " << *it << " is missing valid \""
107 << WiFiService::kStorageSSID << "\" property";
108 continue;
109 }
110 string network_mode;
111 if (!storage->GetString(*it, WiFiService::kStorageMode, &network_mode) ||
112 network_mode.empty()) {
113 SLOG(WiFi, 2) << "Storage group " << *it << " is missing \""
114 << WiFiService::kStorageMode << "\" property";
115 continue;
116 }
117 string security;
118 if (!storage->GetString(*it, WiFiService::kStorageSecurity, &security) ||
119 !WiFiService::IsValidSecurityMethod(security)) {
120 SLOG(WiFi, 2) << "Storage group " << *it << " has missing or invalid \""
121 << WiFiService::kStorageSecurity << "\" property";
122 continue;
123 }
124 bool is_hidden = false;
125 if (!storage->GetBool(*it, WiFiService::kStorageHiddenSSID, &is_hidden)) {
126 SLOG(WiFi, 2) << "Storage group " << *it << " is missing \""
127 << 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;
Wade Guthrie68d41092013-04-02 12:56:02 -0700156 manager_->RequestScan(Device::kProgressiveScan, flimflam::kTypeWifi,
157 &unused_error);
Paul Stewart3c504012013-01-17 17:49:58 -0800158 }
Paul Stewart21f2aae2013-01-17 17:10:08 -0800159}
160
Paul Stewartd2e1c362013-03-03 19:06:07 -0800161WiFiServiceRefPtr WiFiProvider::FindSimilarService(
162 const KeyValueStore &args, Error *error) const {
163 vector<uint8_t> ssid;
164 string mode;
165 string security;
166 bool hidden_ssid;
167
168 if (!GetServiceParametersFromArgs(
169 args, &ssid, &mode, &security, &hidden_ssid, error)) {
170 return NULL;
171 }
172
173 WiFiServiceRefPtr service(FindService(ssid, mode, security));
174 if (!service) {
175 error->Populate(Error::kNotFound, "Matching service was not found");
176 }
177
178 return service;
179}
180
181WiFiServiceRefPtr WiFiProvider::CreateTemporaryService(
182 const KeyValueStore &args, Error *error) {
183 vector<uint8_t> ssid;
184 string mode;
185 string security;
186 bool hidden_ssid;
187
188 if (!GetServiceParametersFromArgs(
189 args, &ssid, &mode, &security, &hidden_ssid, error)) {
190 return NULL;
191 }
192
193 return new WiFiService(control_interface_,
194 dispatcher_,
195 metrics_,
196 manager_,
197 this,
198 ssid,
199 mode,
200 security,
201 hidden_ssid);
202}
203
Paul Stewart21f2aae2013-01-17 17:10:08 -0800204WiFiServiceRefPtr WiFiProvider::GetService(
205 const KeyValueStore &args, Error *error) {
Paul Stewartd2e1c362013-03-03 19:06:07 -0800206 vector<uint8_t> ssid_bytes;
207 string mode;
208 string security_method;
209 bool hidden_ssid;
Paul Stewart3c504012013-01-17 17:49:58 -0800210
Paul Stewartd2e1c362013-03-03 19:06:07 -0800211 if (!GetServiceParametersFromArgs(
212 args, &ssid_bytes, &mode, &security_method, &hidden_ssid, error)) {
Paul Stewart3c504012013-01-17 17:49:58 -0800213 return NULL;
214 }
215
Paul Stewartd2e1c362013-03-03 19:06:07 -0800216 WiFiServiceRefPtr service(FindService(ssid_bytes, mode, security_method));
Paul Stewart3c504012013-01-17 17:49:58 -0800217 if (!service) {
218 service = AddService(ssid_bytes,
Paul Stewartd2e1c362013-03-03 19:06:07 -0800219 mode,
Paul Stewart3c504012013-01-17 17:49:58 -0800220 security_method,
221 hidden_ssid);
222 }
223
224 return service;
Paul Stewart21f2aae2013-01-17 17:10:08 -0800225}
226
227WiFiServiceRefPtr WiFiProvider::FindServiceForEndpoint(
Paul Stewart3c504012013-01-17 17:49:58 -0800228 const WiFiEndpointConstRefPtr &endpoint) {
Paul Stewart0427cc12013-03-25 13:50:39 -0700229 EndpointServiceMap::iterator service_it =
230 service_by_endpoint_.find(endpoint);
231 if (service_it == service_by_endpoint_.end())
232 return NULL;
233 return service_it->second;
Paul Stewart3c504012013-01-17 17:49:58 -0800234}
235
236void WiFiProvider::OnEndpointAdded(const WiFiEndpointConstRefPtr &endpoint) {
Paul Stewart6c351ff2013-02-25 15:13:03 -0800237 if (!running_) {
238 return;
239 }
240
Paul Stewart0427cc12013-03-25 13:50:39 -0700241 WiFiServiceRefPtr service = FindService(endpoint->ssid(),
242 endpoint->network_mode(),
243 endpoint->security_mode());
Paul Stewart3c504012013-01-17 17:49:58 -0800244 if (!service) {
245 const bool hidden_ssid = false;
Paul Stewart08a54eb2013-03-11 12:07:36 -0700246 service = AddService(
247 endpoint->ssid(),
248 endpoint->network_mode(),
249 WiFiService::GetSecurityClass(endpoint->security_mode()),
250 hidden_ssid);
Paul Stewart3c504012013-01-17 17:49:58 -0800251 }
252
253 service->AddEndpoint(endpoint);
Paul Stewart0427cc12013-03-25 13:50:39 -0700254 service_by_endpoint_[endpoint] = service;
Paul Stewart3c504012013-01-17 17:49:58 -0800255
256 SLOG(WiFi, 1) << "Assigned endpoint " << endpoint->bssid_string()
257 << " to service " << service->unique_name() << ".";
258
259 manager_->UpdateService(service);
260}
261
262WiFiServiceRefPtr WiFiProvider::OnEndpointRemoved(
263 const WiFiEndpointConstRefPtr &endpoint) {
Paul Stewart6c351ff2013-02-25 15:13:03 -0800264 if (!running_) {
265 return NULL;
266 }
267
Paul Stewart3c504012013-01-17 17:49:58 -0800268 WiFiServiceRefPtr service = FindServiceForEndpoint(endpoint);
269
270 CHECK(service) << "Can't find Service for Endpoint "
271 << "(with BSSID " << endpoint->bssid_string() << ").";
272 SLOG(WiFi, 1) << "Removing endpoint " << endpoint->bssid_string()
273 << " from Service " << service->unique_name();
274 service->RemoveEndpoint(endpoint);
Paul Stewart0427cc12013-03-25 13:50:39 -0700275 service_by_endpoint_.erase(endpoint);
Paul Stewart3c504012013-01-17 17:49:58 -0800276
277 if (service->HasEndpoints() || service->IsRemembered()) {
278 // Keep services around if they are in a profile or have remaining
279 // endpoints.
280 manager_->UpdateService(service);
281 return NULL;
282 }
283
284 ForgetService(service);
285 manager_->DeregisterService(service);
286
287 return service;
288}
289
Paul Stewart0427cc12013-03-25 13:50:39 -0700290void WiFiProvider::OnEndpointUpdated(const WiFiEndpointConstRefPtr &endpoint) {
291 WiFiService *service = FindServiceForEndpoint(endpoint);
292 CHECK(service);
293
294 // If the service still matches the endpoint in its new configuration,
295 // we need only to update the service.
296 if (service->ssid() == endpoint->ssid() &&
297 service->mode() == endpoint->network_mode() &&
298 service->IsSecurityMatch(endpoint->security_mode())) {
299 service->NotifyEndpointUpdated(endpoint);
300 return;
301 }
302
303 // The endpoint no longer matches the associated service. Remove the
304 // endpoint, so current references to the endpoint are reset, then add
305 // it again so it can be associated with a new service.
306 OnEndpointRemoved(endpoint);
307 OnEndpointAdded(endpoint);
308}
309
Paul Stewart3c504012013-01-17 17:49:58 -0800310bool WiFiProvider::OnServiceUnloaded(const WiFiServiceRefPtr &service) {
311 // If the service still has endpoints, it should remain in the service list.
312 if (service->HasEndpoints()) {
313 return false;
314 }
315
316 // This is the one place where we forget the service but do not also
317 // deregister the service with the manager. However, by returning
318 // true below, the manager will do so itself.
319 ForgetService(service);
320 return true;
321}
322
Wade Guthrie60a37062013-04-02 11:39:09 -0700323void WiFiProvider::LoadAndFixupServiceEntries(
Paul Stewart3c504012013-01-17 17:49:58 -0800324 StoreInterface *storage, bool is_default_profile) {
325 if (WiFiService::FixupServiceEntries(storage)) {
326 storage->Flush();
327 Metrics::ServiceFixupProfileType profile_type =
328 is_default_profile ?
329 Metrics::kMetricServiceFixupDefaultProfile :
330 Metrics::kMetricServiceFixupUserProfile;
331 metrics_->SendEnumToUMA(
332 metrics_->GetFullMetricName(Metrics::kMetricServiceFixupEntries,
333 Technology::kWifi),
334 profile_type,
335 Metrics::kMetricServiceFixupMax);
336 }
Wade Guthrie60a37062013-04-02 11:39:09 -0700337 // TODO(wdg): Determine how this should be structured for, currently
338 // non-existant, autotests. |kStorageFrequencies| should only exist in the
339 // default profile except for autotests where a test_profile is pushed. This
340 // may need to be modified for that case.
341 if (is_default_profile) {
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700342 COMPILE_ASSERT(kMaxStorageFrequencies > kWeeksToKeepFrequencyCounts,
343 persistently_storing_more_frequencies_than_we_can_hold);
344 total_frequency_connections_ = 0L;
345 connect_count_by_frequency_.clear();
346 time_t this_week = time_->GetSecondsSinceEpoch() / kSecondsPerWeek;
347 for (int freq = 0; freq < kMaxStorageFrequencies; ++freq) {
348 ConnectFrequencyMap connect_count_by_frequency;
349 string freq_string = StringPrintf("%s%d", kStorageFrequencies, freq);
350 vector<string> frequencies;
351 if (!storage->GetStringList(kStorageId, freq_string, &frequencies)) {
352 SLOG(WiFi, 7) << "Frequency list " << freq_string << " not found";
353 break;
354 }
355 time_t start_week = StringListToFrequencyMap(frequencies,
356 &connect_count_by_frequency);
357 if (start_week == kIllegalStartWeek) {
358 continue; // |StringListToFrequencyMap| will have output an error msg.
359 }
360
361 if (start_week > this_week) {
362 LOG(WARNING) << "Discarding frequency count info from the future";
363 continue;
364 }
365 connect_count_by_frequency_dated_[start_week] =
366 connect_count_by_frequency;
367
368 for (const auto &freq_count :
369 connect_count_by_frequency_dated_[start_week]) {
370 connect_count_by_frequency_[freq_count.first] += freq_count.second;
371 total_frequency_connections_ += freq_count.second;
Wade Guthrie60a37062013-04-02 11:39:09 -0700372 }
373 }
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700374 SLOG(WiFi, 7) << __func__ << " - total count="
375 << total_frequency_connections_;
Wade Guthrie60a37062013-04-02 11:39:09 -0700376 }
377}
378
379bool WiFiProvider::Save(StoreInterface *storage) const {
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700380 int freq = 0;
381 // Iterating backwards since I want to make sure that I get the newest data.
382 ConnectFrequencyMapDated::const_reverse_iterator freq_count;
383 for (freq_count = connect_count_by_frequency_dated_.crbegin();
384 freq_count != connect_count_by_frequency_dated_.crend();
385 ++freq_count) {
386 vector<string> frequencies;
387 FrequencyMapToStringList(freq_count->first, freq_count->second,
388 &frequencies);
389 string freq_string = StringPrintf("%s%d", kStorageFrequencies, freq);
390 storage->SetStringList(kStorageId, freq_string, frequencies);
391 if (++freq >= kMaxStorageFrequencies) {
392 LOG(WARNING) << "Internal frequency count list has more entries than the "
393 << "string list we had allocated for it.";
394 break;
395 }
396 }
Wade Guthrie60a37062013-04-02 11:39:09 -0700397 return true;
Paul Stewart3c504012013-01-17 17:49:58 -0800398}
399
400WiFiServiceRefPtr WiFiProvider::AddService(const vector<uint8_t> &ssid,
401 const string &mode,
402 const string &security,
403 bool is_hidden) {
404 WiFiServiceRefPtr service = new WiFiService(control_interface_,
405 dispatcher_,
406 metrics_,
407 manager_,
408 this,
409 ssid,
410 mode,
411 security,
412 is_hidden);
413
414 services_.push_back(service);
415 manager_->RegisterService(service);
416 return service;
417}
418
419WiFiServiceRefPtr WiFiProvider::FindService(const vector<uint8_t> &ssid,
420 const string &mode,
421 const string &security) const {
422 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
423 it != services_.end();
424 ++it) {
425 if ((*it)->ssid() == ssid && (*it)->mode() == mode &&
426 (*it)->IsSecurityMatch(security)) {
427 return *it;
428 }
429 }
Paul Stewart21f2aae2013-01-17 17:10:08 -0800430 return NULL;
431}
432
Paul Stewart3c504012013-01-17 17:49:58 -0800433ByteArrays WiFiProvider::GetHiddenSSIDList() {
434 // Create a unique set of hidden SSIDs.
435 set<ByteArray> hidden_ssids_set;
436 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
437 it != services_.end();
438 ++it) {
439 if ((*it)->hidden_ssid() && (*it)->IsRemembered()) {
440 hidden_ssids_set.insert((*it)->ssid());
441 }
442 }
443 SLOG(WiFi, 2) << "Found " << hidden_ssids_set.size() << " hidden services";
444 return ByteArrays(hidden_ssids_set.begin(), hidden_ssids_set.end());
445}
446
447void WiFiProvider::ForgetService(const WiFiServiceRefPtr &service) {
448 vector<WiFiServiceRefPtr>::iterator it;
449 it = std::find(services_.begin(), services_.end(), service);
450 if (it == services_.end()) {
451 return;
452 }
453 (*it)->ResetWiFi();
454 services_.erase(it);
455}
456
Paul Stewartd2e1c362013-03-03 19:06:07 -0800457// static
458bool WiFiProvider::GetServiceParametersFromArgs(const KeyValueStore &args,
459 vector<uint8_t> *ssid_bytes,
460 string *mode,
461 string *security_method,
462 bool *hidden_ssid,
463 Error *error) {
464 CHECK_EQ(args.LookupString(flimflam::kTypeProperty, ""), flimflam::kTypeWifi);
465
466 string mode_test =
467 args.LookupString(flimflam::kModeProperty, flimflam::kModeManaged);
468 if (!WiFiService::IsValidMode(mode_test)) {
469 Error::PopulateAndLog(error, Error::kNotSupported,
470 kManagerErrorUnsupportedServiceMode);
471 return false;
472 }
473
474 if (!args.ContainsString(flimflam::kSSIDProperty)) {
475 Error::PopulateAndLog(error, Error::kInvalidArguments,
476 kManagerErrorSSIDRequired);
477 return false;
478 }
479
480 string ssid = args.GetString(flimflam::kSSIDProperty);
481
482 if (ssid.length() < 1) {
483 Error::PopulateAndLog(error, Error::kInvalidNetworkName,
484 kManagerErrorSSIDTooShort);
485 return false;
486 }
487
488 if (ssid.length() > IEEE_80211::kMaxSSIDLen) {
489 Error::PopulateAndLog(error, Error::kInvalidNetworkName,
490 kManagerErrorSSIDTooLong);
491 return false;
492 }
493
494 string security_method_test = args.LookupString(flimflam::kSecurityProperty,
495 flimflam::kSecurityNone);
496
497 if (!WiFiService::IsValidSecurityMethod(security_method_test)) {
498 Error::PopulateAndLog(error, Error::kNotSupported,
499 kManagerErrorUnsupportedSecurityMode);
500 return false;
501 }
502
503 *ssid_bytes = vector<uint8_t>(ssid.begin(), ssid.end());
504 *mode = mode_test;
505 *security_method = security_method_test;
506
507 // If the caller hasn't specified otherwise, we assume it is a hidden service.
508 *hidden_ssid = args.LookupBool(flimflam::kWifiHiddenSsid, true);
509
510 return true;
511}
512
Wade Guthrie60a37062013-04-02 11:39:09 -0700513// static
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700514time_t WiFiProvider::StringListToFrequencyMap(const vector<string> &strings,
Wade Guthrie60a37062013-04-02 11:39:09 -0700515 ConnectFrequencyMap *numbers) {
516 if (!numbers) {
517 LOG(ERROR) << "Null |numbers| parameter";
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700518 return kIllegalStartWeek;
Wade Guthrie60a37062013-04-02 11:39:09 -0700519 }
520
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700521 // Extract the start week from the first string.
522 vector<string>::const_iterator strings_it = strings.begin();
523 if (strings_it == strings.end()) {
524 SLOG(WiFi, 7) << "Empty |strings|.";
525 return kIllegalStartWeek;
Wade Guthrie60a37062013-04-02 11:39:09 -0700526 }
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700527 time_t start_week = GetStringListStartWeek(*strings_it);
528 if (start_week == kIllegalStartWeek) {
529 return kIllegalStartWeek;
530 }
531
532 // Extract the frequency:count values from the remaining strings.
533 for (++strings_it; strings_it != strings.end(); ++strings_it) {
534 ParseStringListFreqCount(*strings_it, numbers);
535 }
536 return start_week;
Wade Guthrie60a37062013-04-02 11:39:09 -0700537}
538
539// static
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700540time_t WiFiProvider::GetStringListStartWeek(const string &week_string) {
541 if (!StartsWithASCII(week_string, kStartWeekHeader, false)) {
542 LOG(ERROR) << "Found no leading '" << kStartWeekHeader << "' in '"
543 << week_string << "'";
544 return kIllegalStartWeek;
545 }
546 return atoll(week_string.c_str() + 1);
547}
548
549// static
550void WiFiProvider::ParseStringListFreqCount(const string &freq_count_string,
551 ConnectFrequencyMap *numbers) {
552 vector<string> freq_count;
553 SplitString(freq_count_string, kFrequencyDelimiter, &freq_count);
554 if (freq_count.size() != 2) {
555 LOG(WARNING) << "Found " << freq_count.size() - 1 << " '"
556 << kFrequencyDelimiter << "' in '" << freq_count_string
557 << "'. Expected 1.";
558 return;
559 }
560 uint16 freq = atoi(freq_count[0].c_str());
561 uint64 connections = atoll(freq_count[1].c_str());
562 (*numbers)[freq] = connections;
563}
564
565// static
566void WiFiProvider::FrequencyMapToStringList(time_t start_week,
567 const ConnectFrequencyMap &numbers,
Wade Guthrie60a37062013-04-02 11:39:09 -0700568 vector<string> *strings) {
569 if (!strings) {
570 LOG(ERROR) << "Null |strings| parameter";
571 return;
572 }
573
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700574 strings->push_back(StringPrintf("%s%" PRIu64, kStartWeekHeader,
575 static_cast<uint64_t>(start_week)));
576
577 for (const auto &freq_conn : numbers) {
Wade Guthrie60a37062013-04-02 11:39:09 -0700578 // Use base::Int64ToString() instead of using something like "%llu"
579 // (not correct for native 64 bit architectures) or PRId64 (does not
580 // work correctly using cros_workon_make due to include intricacies).
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700581 strings->push_back(StringPrintf("%u%c%s",
582 freq_conn.first, kFrequencyDelimiter,
583 base::Int64ToString(freq_conn.second).c_str()));
Wade Guthrie60a37062013-04-02 11:39:09 -0700584 }
585}
586
587void WiFiProvider::IncrementConnectCount(uint16 frequency_mhz) {
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700588 CHECK(total_frequency_connections_ < std::numeric_limits<int64_t>::max());
Wade Guthrie60a37062013-04-02 11:39:09 -0700589
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700590 ++connect_count_by_frequency_[frequency_mhz];
Wade Guthrie60a37062013-04-02 11:39:09 -0700591 ++total_frequency_connections_;
Wade Guthrie60a37062013-04-02 11:39:09 -0700592
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700593 time_t this_week = time_->GetSecondsSinceEpoch() / kSecondsPerWeek;
594 ++connect_count_by_frequency_dated_[this_week][frequency_mhz];
595
596 ConnectFrequencyMapDated::iterator oldest =
597 connect_count_by_frequency_dated_.begin();
598 time_t oldest_legal_week = this_week - kWeeksToKeepFrequencyCounts;
599 while (oldest->first < oldest_legal_week) {
Wade Guthrie086eb1e2013-05-31 17:31:13 -0700600 SLOG(WiFi, 6) << "Discarding frequency count info that's "
Wade Guthrie7c2d34e2013-05-09 14:02:20 -0700601 << this_week - oldest->first << " weeks old";
602 for (const auto &freq_count : oldest->second) {
603 connect_count_by_frequency_[freq_count.first] -= freq_count.second;
604 if (connect_count_by_frequency_[freq_count.first] <= 0) {
605 connect_count_by_frequency_.erase(freq_count.first);
606 }
607 total_frequency_connections_ -= freq_count.second;
608 }
609 connect_count_by_frequency_dated_.erase(oldest);
610 oldest = connect_count_by_frequency_dated_.begin();
611 }
612
613 manager_->UpdateWiFiProvider();
Wade Guthrie60a37062013-04-02 11:39:09 -0700614 metrics_->SendToUMA(
615 Metrics::kMetricFrequenciesConnectedEver,
616 connect_count_by_frequency_.size(),
617 Metrics::kMetricFrequenciesConnectedMin,
618 Metrics::kMetricFrequenciesConnectedMax,
619 Metrics::kMetricFrequenciesConnectedNumBuckets);
620}
621
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700622WiFiProvider::FrequencyCountList WiFiProvider::GetScanFrequencies() const {
623 FrequencyCountList freq_connects_list;
624 for (const auto freq_count : connect_count_by_frequency_) {
625 freq_connects_list.push_back(FrequencyCount(freq_count.first,
626 freq_count.second));
627 }
628 return freq_connects_list;
629}
630
Paul Stewart21f2aae2013-01-17 17:10:08 -0800631} // namespace shill