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