1 //
2 // Copyright (C) 2013 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include "shill/wifi/wifi_provider.h"
18
19 #include <stdlib.h>
20
21 #include <algorithm>
22 #include <limits>
23 #include <set>
24 #include <string>
25 #include <vector>
26
27 #include <base/bind.h>
28 #include <base/format_macros.h>
29 #include <base/strings/string_number_conversions.h>
30 #include <base/strings/string_split.h>
31 #include <base/strings/string_util.h>
32
33 #include "shill/error.h"
34 #include "shill/event_dispatcher.h"
35 #include "shill/key_value_store.h"
36 #include "shill/logging.h"
37 #include "shill/manager.h"
38 #include "shill/metrics.h"
39 #include "shill/net/byte_string.h"
40 #include "shill/net/ieee80211.h"
41 #include "shill/net/shill_time.h"
42 #include "shill/profile.h"
43 #include "shill/store_interface.h"
44 #include "shill/technology.h"
45 #include "shill/wifi/wifi_endpoint.h"
46 #include "shill/wifi/wifi_service.h"
47
48 using base::Bind;
49 using base::SplitString;
50 using base::StringPrintf;
51 using std::set;
52 using std::string;
53 using std::vector;
54
55 namespace shill {
56
57 namespace Logging {
58 static auto kModuleLogScope = ScopeLogger::kWiFi;
ObjectID(WiFiProvider * w)59 static string ObjectID(WiFiProvider* w) { return "(wifi_provider)"; }
60 }
61
62 // Note that WiFiProvider generates some manager-level errors, because it
63 // implements the WiFi portion of the Manager.GetService flimflam API. The
64 // API is implemented here, rather than in manager, to keep WiFi-specific
65 // logic in the right place.
66 const char WiFiProvider::kManagerErrorSSIDRequired[] = "must specify SSID";
67 const char WiFiProvider::kManagerErrorSSIDTooLong[] = "SSID is too long";
68 const char WiFiProvider::kManagerErrorSSIDTooShort[] = "SSID is too short";
69 const char WiFiProvider::kManagerErrorUnsupportedSecurityClass[] =
70 "security class is unsupported";
71 const char WiFiProvider::kManagerErrorUnsupportedSecurityMode[] =
72 "security mode is unsupported";
73 const char WiFiProvider::kManagerErrorUnsupportedServiceMode[] =
74 "service mode is unsupported";
75 const char WiFiProvider::kManagerErrorArgumentConflict[] =
76 "provided arguments are inconsistent";
77 const char WiFiProvider::kFrequencyDelimiter = ':';
78 const char WiFiProvider::kStartWeekHeader[] = "@";
79 const time_t WiFiProvider::kIllegalStartWeek =
80 std::numeric_limits<time_t>::max();
81 const char WiFiProvider::kStorageId[] = "provider_of_wifi";
82 const char WiFiProvider::kStorageFrequencies[] = "Frequencies";
83 const int WiFiProvider::kMaxStorageFrequencies = 20;
84 const time_t WiFiProvider::kWeeksToKeepFrequencyCounts = 3;
85 const time_t WiFiProvider::kSecondsPerWeek = 60 * 60 * 24 * 7;
86
WiFiProvider(ControlInterface * control_interface,EventDispatcher * dispatcher,Metrics * metrics,Manager * manager)87 WiFiProvider::WiFiProvider(ControlInterface* control_interface,
88 EventDispatcher* dispatcher,
89 Metrics* metrics,
90 Manager* manager)
91 : control_interface_(control_interface),
92 dispatcher_(dispatcher),
93 metrics_(metrics),
94 manager_(manager),
95 running_(false),
96 total_frequency_connections_(-1L),
97 time_(Time::GetInstance()),
98 disable_vht_(false) {}
99
~WiFiProvider()100 WiFiProvider::~WiFiProvider() {}
101
Start()102 void WiFiProvider::Start() {
103 running_ = true;
104 }
105
Stop()106 void WiFiProvider::Stop() {
107 SLOG(this, 2) << __func__;
108 while (!services_.empty()) {
109 WiFiServiceRefPtr service = services_.back();
110 ForgetService(service);
111 SLOG(this, 3) << "WiFiProvider deregistering service "
112 << service->unique_name();
113 manager_->DeregisterService(service);
114 }
115 service_by_endpoint_.clear();
116 running_ = false;
117 }
118
CreateServicesFromProfile(const ProfileRefPtr & profile)119 void WiFiProvider::CreateServicesFromProfile(const ProfileRefPtr& profile) {
120 const StoreInterface* storage = profile->GetConstStorage();
121 KeyValueStore args;
122 args.SetString(kTypeProperty, kTypeWifi);
123 bool created_hidden_service = false;
124 for (const auto& group : storage->GetGroupsWithProperties(args)) {
125 vector<uint8_t> ssid_bytes;
126 string network_mode;
127 string security;
128 bool is_hidden = false;
129 if (!GetServiceParametersFromStorage(storage,
130 group,
131 &ssid_bytes,
132 &network_mode,
133 &security,
134 &is_hidden,
135 nullptr)) {
136 continue;
137 }
138
139 if (FindService(ssid_bytes, network_mode, security)) {
140 // If service already exists, we have nothing to do, since the
141 // service has already loaded its configuration from storage.
142 // This is guaranteed to happen in the single case where
143 // CreateServicesFromProfile() is called on a WiFiProvider from
144 // Manager::PushProfile():
145 continue;
146 }
147
148 AddService(ssid_bytes, network_mode, security, is_hidden);
149
150 // By registering the service in AddService, the rest of the configuration
151 // will be loaded from the profile into the service via ConfigureService().
152
153 if (is_hidden) {
154 created_hidden_service = true;
155 }
156 }
157
158 // If WiFi is unconnected and we created a hidden service as a result
159 // of opening the profile, we should initiate a WiFi scan, which will
160 // allow us to find any hidden services that we may have created.
161 if (created_hidden_service &&
162 !manager_->IsTechnologyConnected(Technology::kWifi)) {
163 Error unused_error;
164 manager_->RequestScan(Device::kProgressiveScan, kTypeWifi, &unused_error);
165 }
166
167 ReportRememberedNetworkCount();
168
169 // Only report service source metrics when a user profile is pushed.
170 // This ensures that we have an equal number of samples for the
171 // default profile and user profiles.
172 if (!profile->IsDefault()) {
173 ReportServiceSourceMetrics();
174 }
175 }
176
FindSimilarService(const KeyValueStore & args,Error * error) const177 ServiceRefPtr WiFiProvider::FindSimilarService(
178 const KeyValueStore& args, Error* error) const {
179 vector<uint8_t> ssid;
180 string mode;
181 string security;
182 bool hidden_ssid;
183
184 if (!GetServiceParametersFromArgs(
185 args, &ssid, &mode, &security, &hidden_ssid, error)) {
186 return nullptr;
187 }
188
189 WiFiServiceRefPtr service(FindService(ssid, mode, security));
190 if (!service) {
191 error->Populate(Error::kNotFound, "Matching service was not found");
192 }
193
194 return service;
195 }
196
CreateTemporaryService(const KeyValueStore & args,Error * error)197 ServiceRefPtr WiFiProvider::CreateTemporaryService(
198 const KeyValueStore& args, Error* error) {
199 vector<uint8_t> ssid;
200 string mode;
201 string security;
202 bool hidden_ssid;
203
204 if (!GetServiceParametersFromArgs(
205 args, &ssid, &mode, &security, &hidden_ssid, error)) {
206 return nullptr;
207 }
208
209 return new WiFiService(control_interface_,
210 dispatcher_,
211 metrics_,
212 manager_,
213 this,
214 ssid,
215 mode,
216 security,
217 hidden_ssid);
218 }
219
CreateTemporaryServiceFromProfile(const ProfileRefPtr & profile,const std::string & entry_name,Error * error)220 ServiceRefPtr WiFiProvider::CreateTemporaryServiceFromProfile(
221 const ProfileRefPtr& profile, const std::string& entry_name, Error* error) {
222 vector<uint8_t> ssid;
223 string mode;
224 string security;
225 bool hidden_ssid;
226 if (!GetServiceParametersFromStorage(profile->GetConstStorage(),
227 entry_name,
228 &ssid,
229 &mode,
230 &security,
231 &hidden_ssid,
232 error)) {
233 return nullptr;
234 }
235 return new WiFiService(control_interface_,
236 dispatcher_,
237 metrics_,
238 manager_,
239 this,
240 ssid,
241 mode,
242 security,
243 hidden_ssid);
244 }
245
GetService(const KeyValueStore & args,Error * error)246 ServiceRefPtr WiFiProvider::GetService(
247 const KeyValueStore& args, Error* error) {
248 return GetWiFiService(args, error);
249 }
250
GetWiFiService(const KeyValueStore & args,Error * error)251 WiFiServiceRefPtr WiFiProvider::GetWiFiService(
252 const KeyValueStore& args, Error* error) {
253 vector<uint8_t> ssid_bytes;
254 string mode;
255 string security_method;
256 bool hidden_ssid;
257
258 if (!GetServiceParametersFromArgs(
259 args, &ssid_bytes, &mode, &security_method, &hidden_ssid, error)) {
260 return nullptr;
261 }
262
263 WiFiServiceRefPtr service(FindService(ssid_bytes, mode, security_method));
264 if (!service) {
265 service = AddService(ssid_bytes,
266 mode,
267 security_method,
268 hidden_ssid);
269 }
270
271 return service;
272 }
273
FindServiceForEndpoint(const WiFiEndpointConstRefPtr & endpoint)274 WiFiServiceRefPtr WiFiProvider::FindServiceForEndpoint(
275 const WiFiEndpointConstRefPtr& endpoint) {
276 EndpointServiceMap::iterator service_it =
277 service_by_endpoint_.find(endpoint.get());
278 if (service_it == service_by_endpoint_.end())
279 return nullptr;
280 return service_it->second;
281 }
282
OnEndpointAdded(const WiFiEndpointConstRefPtr & endpoint)283 void WiFiProvider::OnEndpointAdded(const WiFiEndpointConstRefPtr& endpoint) {
284 if (!running_) {
285 return;
286 }
287
288 WiFiServiceRefPtr service = FindService(endpoint->ssid(),
289 endpoint->network_mode(),
290 endpoint->security_mode());
291 if (!service) {
292 const bool hidden_ssid = false;
293 service = AddService(
294 endpoint->ssid(),
295 endpoint->network_mode(),
296 WiFiService::ComputeSecurityClass(endpoint->security_mode()),
297 hidden_ssid);
298 }
299
300 service->AddEndpoint(endpoint);
301 service_by_endpoint_[endpoint.get()] = service;
302
303 SLOG(this, 1) << "Assigned endpoint " << endpoint->bssid_string()
304 << " to service " << service->unique_name() << ".";
305
306 manager_->UpdateService(service);
307 }
308
OnEndpointRemoved(const WiFiEndpointConstRefPtr & endpoint)309 WiFiServiceRefPtr WiFiProvider::OnEndpointRemoved(
310 const WiFiEndpointConstRefPtr& endpoint) {
311 if (!running_) {
312 return nullptr;
313 }
314
315 WiFiServiceRefPtr service = FindServiceForEndpoint(endpoint);
316
317 CHECK(service) << "Can't find Service for Endpoint "
318 << "(with BSSID " << endpoint->bssid_string() << ").";
319 SLOG(this, 1) << "Removing endpoint " << endpoint->bssid_string()
320 << " from Service " << service->unique_name();
321 service->RemoveEndpoint(endpoint);
322 service_by_endpoint_.erase(endpoint.get());
323
324 if (service->HasEndpoints() || service->IsRemembered()) {
325 // Keep services around if they are in a profile or have remaining
326 // endpoints.
327 manager_->UpdateService(service);
328 return nullptr;
329 }
330
331 ForgetService(service);
332 manager_->DeregisterService(service);
333
334 return service;
335 }
336
OnEndpointUpdated(const WiFiEndpointConstRefPtr & endpoint)337 void WiFiProvider::OnEndpointUpdated(const WiFiEndpointConstRefPtr& endpoint) {
338 if (!running_) {
339 return;
340 }
341
342 WiFiService* service = FindServiceForEndpoint(endpoint).get();
343 CHECK(service);
344
345 // If the service still matches the endpoint in its new configuration,
346 // we need only to update the service.
347 if (service->ssid() == endpoint->ssid() &&
348 service->mode() == endpoint->network_mode() &&
349 service->IsSecurityMatch(endpoint->security_mode())) {
350 service->NotifyEndpointUpdated(endpoint);
351 return;
352 }
353
354 // The endpoint no longer matches the associated service. Remove the
355 // endpoint, so current references to the endpoint are reset, then add
356 // it again so it can be associated with a new service.
357 OnEndpointRemoved(endpoint);
358 OnEndpointAdded(endpoint);
359 }
360
OnServiceUnloaded(const WiFiServiceRefPtr & service)361 bool WiFiProvider::OnServiceUnloaded(const WiFiServiceRefPtr& service) {
362 // If the service still has endpoints, it should remain in the service list.
363 if (service->HasEndpoints()) {
364 return false;
365 }
366
367 // This is the one place where we forget the service but do not also
368 // deregister the service with the manager. However, by returning
369 // true below, the manager will do so itself.
370 ForgetService(service);
371 return true;
372 }
373
LoadAndFixupServiceEntries(Profile * profile)374 void WiFiProvider::LoadAndFixupServiceEntries(Profile* profile) {
375 CHECK(profile);
376 StoreInterface* storage = profile->GetStorage();
377 bool is_default_profile = profile->IsDefault();
378 if (WiFiService::FixupServiceEntries(storage)) {
379 storage->Flush();
380 Metrics::ServiceFixupProfileType profile_type =
381 is_default_profile ?
382 Metrics::kMetricServiceFixupDefaultProfile :
383 Metrics::kMetricServiceFixupUserProfile;
384 metrics_->SendEnumToUMA(
385 metrics_->GetFullMetricName(Metrics::kMetricServiceFixupEntriesSuffix,
386 Technology::kWifi),
387 profile_type,
388 Metrics::kMetricServiceFixupMax);
389 }
390 // TODO(wdg): Determine how this should be structured for, currently
391 // non-existant, autotests. |kStorageFrequencies| should only exist in the
392 // default profile except for autotests where a test_profile is pushed. This
393 // may need to be modified for that case.
394 if (is_default_profile) {
395 static_assert(kMaxStorageFrequencies > kWeeksToKeepFrequencyCounts,
396 "Persistently storing more frequencies than we can hold");
397 total_frequency_connections_ = 0L;
398 connect_count_by_frequency_.clear();
399 time_t this_week = time_->GetSecondsSinceEpoch() / kSecondsPerWeek;
400 for (int freq = 0; freq < kMaxStorageFrequencies; ++freq) {
401 ConnectFrequencyMap connect_count_by_frequency;
402 string freq_string = StringPrintf("%s%d", kStorageFrequencies, freq);
403 vector<string> frequencies;
404 if (!storage->GetStringList(kStorageId, freq_string, &frequencies)) {
405 SLOG(this, 7) << "Frequency list " << freq_string << " not found";
406 break;
407 }
408 time_t start_week = StringListToFrequencyMap(frequencies,
409 &connect_count_by_frequency);
410 if (start_week == kIllegalStartWeek) {
411 continue; // |StringListToFrequencyMap| will have output an error msg.
412 }
413
414 if (start_week > this_week) {
415 LOG(WARNING) << "Discarding frequency count info from the future";
416 continue;
417 }
418 connect_count_by_frequency_dated_[start_week] =
419 connect_count_by_frequency;
420
421 for (const auto& freq_count :
422 connect_count_by_frequency_dated_[start_week]) {
423 connect_count_by_frequency_[freq_count.first] += freq_count.second;
424 total_frequency_connections_ += freq_count.second;
425 }
426 }
427 SLOG(this, 7) << __func__ << " - total count="
428 << total_frequency_connections_;
429 }
430 }
431
Save(StoreInterface * storage) const432 bool WiFiProvider::Save(StoreInterface* storage) const {
433 int freq = 0;
434 // Iterating backwards since I want to make sure that I get the newest data.
435 ConnectFrequencyMapDated::const_reverse_iterator freq_count;
436 for (freq_count = connect_count_by_frequency_dated_.crbegin();
437 freq_count != connect_count_by_frequency_dated_.crend();
438 ++freq_count) {
439 vector<string> frequencies;
440 FrequencyMapToStringList(freq_count->first, freq_count->second,
441 &frequencies);
442 string freq_string = StringPrintf("%s%d", kStorageFrequencies, freq);
443 storage->SetStringList(kStorageId, freq_string, frequencies);
444 if (++freq >= kMaxStorageFrequencies) {
445 LOG(WARNING) << "Internal frequency count list has more entries than the "
446 << "string list we had allocated for it.";
447 break;
448 }
449 }
450 return true;
451 }
452
AddService(const vector<uint8_t> & ssid,const string & mode,const string & security,bool is_hidden)453 WiFiServiceRefPtr WiFiProvider::AddService(const vector<uint8_t>& ssid,
454 const string& mode,
455 const string& security,
456 bool is_hidden) {
457 WiFiServiceRefPtr service = new WiFiService(control_interface_,
458 dispatcher_,
459 metrics_,
460 manager_,
461 this,
462 ssid,
463 mode,
464 security,
465 is_hidden);
466
467 services_.push_back(service);
468 manager_->RegisterService(service);
469 return service;
470 }
471
FindService(const vector<uint8_t> & ssid,const string & mode,const string & security) const472 WiFiServiceRefPtr WiFiProvider::FindService(const vector<uint8_t>& ssid,
473 const string& mode,
474 const string& security) const {
475 for (const auto& service : services_) {
476 if (service->ssid() == ssid && service->mode() == mode &&
477 service->IsSecurityMatch(security)) {
478 return service;
479 }
480 }
481 return nullptr;
482 }
483
GetHiddenSSIDList()484 ByteArrays WiFiProvider::GetHiddenSSIDList() {
485 // Create a unique set of hidden SSIDs.
486 set<ByteArray> hidden_ssids_set;
487 for (const auto& service : services_) {
488 if (service->hidden_ssid() && service->IsRemembered()) {
489 hidden_ssids_set.insert(service->ssid());
490 }
491 }
492 SLOG(this, 2) << "Found " << hidden_ssids_set.size() << " hidden services";
493 return ByteArrays(hidden_ssids_set.begin(), hidden_ssids_set.end());
494 }
495
ForgetService(const WiFiServiceRefPtr & service)496 void WiFiProvider::ForgetService(const WiFiServiceRefPtr& service) {
497 vector<WiFiServiceRefPtr>::iterator it;
498 it = std::find(services_.begin(), services_.end(), service);
499 if (it == services_.end()) {
500 return;
501 }
502 (*it)->ResetWiFi();
503 services_.erase(it);
504 }
505
ReportRememberedNetworkCount()506 void WiFiProvider::ReportRememberedNetworkCount() {
507 metrics_->SendToUMA(
508 Metrics::kMetricRememberedWiFiNetworkCount,
509 std::count_if(
510 services_.begin(), services_.end(),
511 [](ServiceRefPtr s) { return s->IsRemembered(); }),
512 Metrics::kMetricRememberedWiFiNetworkCountMin,
513 Metrics::kMetricRememberedWiFiNetworkCountMax,
514 Metrics::kMetricRememberedWiFiNetworkCountNumBuckets);
515 }
516
ReportServiceSourceMetrics()517 void WiFiProvider::ReportServiceSourceMetrics() {
518 for (const auto& security_mode :
519 {kSecurityNone, kSecurityWep, kSecurityPsk, kSecurity8021x}) {
520 metrics_->SendToUMA(
521 base::StringPrintf(
522 Metrics::
523 kMetricRememberedSystemWiFiNetworkCountBySecurityModeFormat,
524 security_mode),
525 std::count_if(
526 services_.begin(), services_.end(),
527 [security_mode](WiFiServiceRefPtr s) {
528 return s->IsRemembered() && s->IsSecurityMatch(security_mode) &&
529 s->profile()->IsDefault();
530 }),
531 Metrics::kMetricRememberedWiFiNetworkCountMin,
532 Metrics::kMetricRememberedWiFiNetworkCountMax,
533 Metrics::kMetricRememberedWiFiNetworkCountNumBuckets);
534 metrics_->SendToUMA(
535 base::StringPrintf(
536 Metrics::
537 kMetricRememberedUserWiFiNetworkCountBySecurityModeFormat,
538 security_mode),
539 std::count_if(
540 services_.begin(), services_.end(),
541 [security_mode](WiFiServiceRefPtr s) {
542 return s->IsRemembered() && s->IsSecurityMatch(security_mode) &&
543 !s->profile()->IsDefault();
544 }),
545 Metrics::kMetricRememberedWiFiNetworkCountMin,
546 Metrics::kMetricRememberedWiFiNetworkCountMax,
547 Metrics::kMetricRememberedWiFiNetworkCountNumBuckets);
548 }
549 }
550
551 // static
GetServiceParametersFromArgs(const KeyValueStore & args,vector<uint8_t> * ssid_bytes,string * mode,string * security_method,bool * hidden_ssid,Error * error)552 bool WiFiProvider::GetServiceParametersFromArgs(const KeyValueStore& args,
553 vector<uint8_t>* ssid_bytes,
554 string* mode,
555 string* security_method,
556 bool* hidden_ssid,
557 Error* error) {
558 CHECK_EQ(args.LookupString(kTypeProperty, ""), kTypeWifi);
559
560 string mode_test =
561 args.LookupString(kModeProperty, kModeManaged);
562 if (!WiFiService::IsValidMode(mode_test)) {
563 Error::PopulateAndLog(FROM_HERE, error, Error::kNotSupported,
564 kManagerErrorUnsupportedServiceMode);
565 return false;
566 }
567
568 vector<uint8_t> ssid;
569 if (args.ContainsString(kWifiHexSsid)) {
570 string ssid_hex_string = args.GetString(kWifiHexSsid);
571 if (!base::HexStringToBytes(ssid_hex_string, &ssid)) {
572 Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments,
573 "Hex SSID parameter is not valid");
574 return false;
575 }
576 } else if (args.ContainsString(kSSIDProperty)) {
577 string ssid_string = args.GetString(kSSIDProperty);
578 ssid = vector<uint8_t>(ssid_string.begin(), ssid_string.end());
579 } else {
580 Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments,
581 kManagerErrorSSIDRequired);
582 return false;
583 }
584
585 if (ssid.size() < 1) {
586 Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidNetworkName,
587 kManagerErrorSSIDTooShort);
588 return false;
589 }
590
591 if (ssid.size() > IEEE_80211::kMaxSSIDLen) {
592 Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidNetworkName,
593 kManagerErrorSSIDTooLong);
594 return false;
595 }
596
597 const string kDefaultSecurity = kSecurityNone;
598 if (args.ContainsString(kSecurityProperty) &&
599 args.ContainsString(kSecurityClassProperty) &&
600 args.LookupString(kSecurityClassProperty, kDefaultSecurity) !=
601 args.LookupString(kSecurityProperty, kDefaultSecurity)) {
602 Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments,
603 kManagerErrorArgumentConflict);
604 return false;
605 }
606 if (args.ContainsString(kSecurityClassProperty)) {
607 string security_class_test =
608 args.LookupString(kSecurityClassProperty, kDefaultSecurity);
609 if (!WiFiService::IsValidSecurityClass(security_class_test)) {
610 Error::PopulateAndLog(FROM_HERE, error, Error::kNotSupported,
611 kManagerErrorUnsupportedSecurityClass);
612 return false;
613 }
614 *security_method = security_class_test;
615 } else if (args.ContainsString(kSecurityProperty)) {
616 string security_method_test =
617 args.LookupString(kSecurityProperty, kDefaultSecurity);
618 if (!WiFiService::IsValidSecurityMethod(security_method_test)) {
619 Error::PopulateAndLog(FROM_HERE, error, Error::kNotSupported,
620 kManagerErrorUnsupportedSecurityMode);
621 return false;
622 }
623 *security_method = security_method_test;
624 } else {
625 *security_method = kDefaultSecurity;
626 }
627
628 *ssid_bytes = ssid;
629 *mode = mode_test;
630
631 // If the caller hasn't specified otherwise, we assume it is a hidden service.
632 *hidden_ssid = args.LookupBool(kWifiHiddenSsid, true);
633
634 return true;
635 }
636
637 // static
GetServiceParametersFromStorage(const StoreInterface * storage,const std::string & entry_name,std::vector<uint8_t> * ssid_bytes,std::string * mode,std::string * security,bool * hidden_ssid,Error * error)638 bool WiFiProvider::GetServiceParametersFromStorage(
639 const StoreInterface* storage,
640 const std::string& entry_name,
641 std::vector<uint8_t>* ssid_bytes,
642 std::string* mode,
643 std::string* security,
644 bool* hidden_ssid,
645 Error* error) {
646 // Verify service type.
647 string type;
648 if (!storage->GetString(entry_name, WiFiService::kStorageType, &type) ||
649 type != kTypeWifi) {
650 Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments,
651 "Unspecified or invalid network type");
652 return false;
653 }
654 string ssid_hex;
655 if (!storage->GetString(entry_name, WiFiService::kStorageSSID, &ssid_hex) ||
656 !base::HexStringToBytes(ssid_hex, ssid_bytes)) {
657 Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments,
658 "Unspecified or invalid SSID");
659 return false;
660 }
661 if (!storage->GetString(entry_name, WiFiService::kStorageMode, mode) ||
662 mode->empty()) {
663 Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments,
664 "Network mode not specified");
665 return false;
666 }
667 if (!storage->GetString(entry_name, WiFiService::kStorageSecurity, security)
668 || !WiFiService::IsValidSecurityMethod(*security)) {
669 Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments,
670 "Unspecified or invalid security mode");
671 return false;
672 }
673 if (!storage->GetBool(
674 entry_name, WiFiService::kStorageHiddenSSID, hidden_ssid)) {
675 Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments,
676 "Hidden SSID not specified");
677 return false;
678 }
679 return true;
680 }
681
682 // static
StringListToFrequencyMap(const vector<string> & strings,ConnectFrequencyMap * numbers)683 time_t WiFiProvider::StringListToFrequencyMap(const vector<string>& strings,
684 ConnectFrequencyMap* numbers) {
685 if (!numbers) {
686 LOG(ERROR) << "Null |numbers| parameter";
687 return kIllegalStartWeek;
688 }
689
690 // Extract the start week from the first string.
691 vector<string>::const_iterator strings_it = strings.begin();
692 if (strings_it == strings.end()) {
693 SLOG(nullptr, 7) << "Empty |strings|.";
694 return kIllegalStartWeek;
695 }
696 time_t start_week = GetStringListStartWeek(*strings_it);
697 if (start_week == kIllegalStartWeek) {
698 return kIllegalStartWeek;
699 }
700
701 // Extract the frequency:count values from the remaining strings.
702 for (++strings_it; strings_it != strings.end(); ++strings_it) {
703 ParseStringListFreqCount(*strings_it, numbers);
704 }
705 return start_week;
706 }
707
708 // static
GetStringListStartWeek(const string & week_string)709 time_t WiFiProvider::GetStringListStartWeek(const string& week_string) {
710 if (!base::StartsWith(week_string, kStartWeekHeader,
711 base::CompareCase::INSENSITIVE_ASCII)) {
712 LOG(ERROR) << "Found no leading '" << kStartWeekHeader << "' in '"
713 << week_string << "'";
714 return kIllegalStartWeek;
715 }
716 return atoll(week_string.c_str() + 1);
717 }
718
719 // static
ParseStringListFreqCount(const string & freq_count_string,ConnectFrequencyMap * numbers)720 void WiFiProvider::ParseStringListFreqCount(const string& freq_count_string,
721 ConnectFrequencyMap* numbers) {
722 vector<string> freq_count = SplitString(
723 freq_count_string, std::string{kFrequencyDelimiter},
724 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
725 if (freq_count.size() != 2) {
726 LOG(WARNING) << "Found " << freq_count.size() - 1 << " '"
727 << kFrequencyDelimiter << "' in '" << freq_count_string
728 << "'. Expected 1.";
729 return;
730 }
731 uint16_t freq = atoi(freq_count[0].c_str());
732 uint64_t connections = atoll(freq_count[1].c_str());
733 (*numbers)[freq] = connections;
734 }
735
736 // static
FrequencyMapToStringList(time_t start_week,const ConnectFrequencyMap & numbers,vector<string> * strings)737 void WiFiProvider::FrequencyMapToStringList(time_t start_week,
738 const ConnectFrequencyMap& numbers,
739 vector<string>* strings) {
740 if (!strings) {
741 LOG(ERROR) << "Null |strings| parameter";
742 return;
743 }
744
745 strings->push_back(StringPrintf("%s%" PRIu64, kStartWeekHeader,
746 static_cast<uint64_t>(start_week)));
747
748 for (const auto& freq_conn : numbers) {
749 // Use base::Int64ToString() instead of using something like "%llu"
750 // (not correct for native 64 bit architectures) or PRId64 (does not
751 // work correctly using cros_workon_make due to include intricacies).
752 strings->push_back(StringPrintf("%u%c%s",
753 freq_conn.first, kFrequencyDelimiter,
754 base::Int64ToString(freq_conn.second).c_str()));
755 }
756 }
757
IncrementConnectCount(uint16_t frequency_mhz)758 void WiFiProvider::IncrementConnectCount(uint16_t frequency_mhz) {
759 CHECK(total_frequency_connections_ < std::numeric_limits<int64_t>::max());
760
761 ++connect_count_by_frequency_[frequency_mhz];
762 ++total_frequency_connections_;
763
764 time_t this_week = time_->GetSecondsSinceEpoch() / kSecondsPerWeek;
765 ++connect_count_by_frequency_dated_[this_week][frequency_mhz];
766
767 ConnectFrequencyMapDated::iterator oldest =
768 connect_count_by_frequency_dated_.begin();
769 time_t oldest_legal_week = this_week - kWeeksToKeepFrequencyCounts;
770 while (oldest->first < oldest_legal_week) {
771 SLOG(this, 6) << "Discarding frequency count info that's "
772 << this_week - oldest->first << " weeks old";
773 for (const auto& freq_count : oldest->second) {
774 connect_count_by_frequency_[freq_count.first] -= freq_count.second;
775 if (connect_count_by_frequency_[freq_count.first] <= 0) {
776 connect_count_by_frequency_.erase(freq_count.first);
777 }
778 total_frequency_connections_ -= freq_count.second;
779 }
780 connect_count_by_frequency_dated_.erase(oldest);
781 oldest = connect_count_by_frequency_dated_.begin();
782 }
783
784 manager_->UpdateWiFiProvider();
785 metrics_->SendToUMA(
786 Metrics::kMetricFrequenciesConnectedEver,
787 connect_count_by_frequency_.size(),
788 Metrics::kMetricFrequenciesConnectedMin,
789 Metrics::kMetricFrequenciesConnectedMax,
790 Metrics::kMetricFrequenciesConnectedNumBuckets);
791 }
792
GetScanFrequencies() const793 WiFiProvider::FrequencyCountList WiFiProvider::GetScanFrequencies() const {
794 FrequencyCountList freq_connects_list;
795 for (const auto freq_count : connect_count_by_frequency_) {
796 freq_connects_list.push_back(FrequencyCount(freq_count.first,
797 freq_count.second));
798 }
799 return freq_connects_list;
800 }
801
ReportAutoConnectableServices()802 void WiFiProvider::ReportAutoConnectableServices() {
803 int num_services = NumAutoConnectableServices();
804 // Only report stats when there are wifi services available.
805 if (num_services) {
806 metrics_->NotifyWifiAutoConnectableServices(num_services);
807 }
808 }
809
NumAutoConnectableServices()810 int WiFiProvider::NumAutoConnectableServices() {
811 const char* reason = nullptr;
812 int num_services = 0;
813 // Determine the number of services available for auto-connect.
814 for (const auto& service : services_) {
815 // Service is available for auto connect if it is configured for auto
816 // connect, and is auto-connectable.
817 if (service->auto_connect() && service->IsAutoConnectable(&reason)) {
818 num_services++;
819 }
820 }
821 return num_services;
822 }
823
GetSsidsConfiguredForAutoConnect()824 vector<ByteString> WiFiProvider::GetSsidsConfiguredForAutoConnect() {
825 vector<ByteString> results;
826 for (const auto& service : services_) {
827 if (service->auto_connect()) {
828 // Service configured for auto-connect.
829 ByteString ssid_bytes(service->ssid());
830 results.push_back(ssid_bytes);
831 }
832 }
833 return results;
834 }
835
836 } // namespace shill
837