Home | History | Annotate | Download | only in wifi
      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;
     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 
     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 
    100 WiFiProvider::~WiFiProvider() {}
    101 
    102 void WiFiProvider::Start() {
    103   running_ = true;
    104 }
    105 
    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 
    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 
    177 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 
    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 
    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 
    246 ServiceRefPtr WiFiProvider::GetService(
    247     const KeyValueStore& args, Error* error) {
    248   return GetWiFiService(args, error);
    249 }
    250 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    432 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 
    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 
    472 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 
    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 
    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 
    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 
    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
    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
    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
    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
    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
    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
    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 
    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 
    793 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 
    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 
    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 
    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