• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/nqe/network_qualities_prefs_manager.h"
6 
7 #include <string>
8 #include <utility>
9 
10 #include "base/functional/bind.h"
11 #include "base/metrics/histogram_macros_local.h"
12 #include "base/rand_util.h"
13 #include "base/task/sequenced_task_runner.h"
14 #include "net/nqe/network_quality_estimator.h"
15 #include "third_party/abseil-cpp/absl/types/optional.h"
16 
17 namespace net {
18 
19 namespace {
20 
21 // Maximum size of the prefs that hold the qualities of different networks.
22 // A single entry in the cache consists of three tuples:
23 // (i)   SSID or MCCMNC of the network. SSID is at most 32 characters in length
24 //       (but is typically shorter than that). MCCMNC is at most 6 characters
25 //       long.
26 // (ii)  Connection type of the network as reported by network
27 //       change notifier (an enum).
28 // (iii) Effective connection type of the network (an enum).
29 constexpr size_t kMaxCacheSize = 20u;
30 
31 // Parses |value| into a map of NetworkIDs and CachedNetworkQualities,
32 // and returns the map.
ConvertDictionaryValueToMap(const base::Value::Dict & value)33 ParsedPrefs ConvertDictionaryValueToMap(const base::Value::Dict& value) {
34   DCHECK_GE(kMaxCacheSize, value.size());
35 
36   ParsedPrefs read_prefs;
37   for (auto it : value) {
38     nqe::internal::NetworkID network_id =
39         nqe::internal::NetworkID::FromString(it.first);
40 
41     if (!it.second.is_string())
42       continue;
43     absl::optional<EffectiveConnectionType> effective_connection_type =
44         GetEffectiveConnectionTypeForName(it.second.GetString());
45     DCHECK(effective_connection_type.has_value());
46 
47     nqe::internal::CachedNetworkQuality cached_network_quality(
48         effective_connection_type.value_or(EFFECTIVE_CONNECTION_TYPE_UNKNOWN));
49     read_prefs[network_id] = cached_network_quality;
50   }
51   return read_prefs;
52 }
53 
54 }  // namespace
55 
NetworkQualitiesPrefsManager(std::unique_ptr<PrefDelegate> pref_delegate)56 NetworkQualitiesPrefsManager::NetworkQualitiesPrefsManager(
57     std::unique_ptr<PrefDelegate> pref_delegate)
58     : pref_delegate_(std::move(pref_delegate)),
59       prefs_(pref_delegate_->GetDictionaryValue()) {
60   DCHECK(pref_delegate_);
61   DCHECK_GE(kMaxCacheSize, prefs_.size());
62 }
63 
~NetworkQualitiesPrefsManager()64 NetworkQualitiesPrefsManager::~NetworkQualitiesPrefsManager() {
65   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
66 
67   ShutdownOnPrefSequence();
68 
69   if (network_quality_estimator_)
70     network_quality_estimator_->RemoveNetworkQualitiesCacheObserver(this);
71 }
72 
InitializeOnNetworkThread(NetworkQualityEstimator * network_quality_estimator)73 void NetworkQualitiesPrefsManager::InitializeOnNetworkThread(
74     NetworkQualityEstimator* network_quality_estimator) {
75   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
76   DCHECK(network_quality_estimator);
77 
78   // Read |prefs_| again since they have now been fully initialized. This
79   // overwrites any values that may have been added to |prefs_| since
80   // construction of |this| via OnChangeInCachedNetworkQuality(). However, it's
81   // expected that InitializeOnNetworkThread will be called soon after
82   // construction of |this|. So, any loss of values would be minimal.
83   prefs_ = pref_delegate_->GetDictionaryValue();
84   read_prefs_startup_ = ConvertDictionaryValueToMap(prefs_);
85 
86   network_quality_estimator_ = network_quality_estimator;
87   network_quality_estimator_->AddNetworkQualitiesCacheObserver(this);
88 
89   // Notify network quality estimator of the read prefs.
90   network_quality_estimator_->OnPrefsRead(read_prefs_startup_);
91 }
92 
ShutdownOnPrefSequence()93 void NetworkQualitiesPrefsManager::ShutdownOnPrefSequence() {
94   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
95   pref_delegate_.reset();
96 }
97 
ClearPrefs()98 void NetworkQualitiesPrefsManager::ClearPrefs() {
99   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
100 
101   LOCAL_HISTOGRAM_COUNTS_100("NQE.PrefsSizeOnClearing", prefs_.size());
102   prefs_.clear();
103   DCHECK_EQ(0u, prefs_.size());
104   pref_delegate_->SetDictionaryValue(prefs_);
105 }
106 
OnChangeInCachedNetworkQuality(const nqe::internal::NetworkID & network_id,const nqe::internal::CachedNetworkQuality & cached_network_quality)107 void NetworkQualitiesPrefsManager::OnChangeInCachedNetworkQuality(
108     const nqe::internal::NetworkID& network_id,
109     const nqe::internal::CachedNetworkQuality& cached_network_quality) {
110   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
111   DCHECK_GE(kMaxCacheSize, prefs_.size());
112 
113   std::string network_id_string = network_id.ToString();
114 
115   // If the network ID contains a period, then return early since the dictionary
116   // prefs cannot contain period in the path.
117   if (network_id_string.find('.') != std::string::npos)
118     return;
119 
120   prefs_.Set(network_id_string,
121              GetNameForEffectiveConnectionType(
122                  cached_network_quality.effective_connection_type()));
123 
124   if (prefs_.size() > kMaxCacheSize) {
125     // Delete one randomly selected value that has a key that is different from
126     // |network_id|.
127     DCHECK_EQ(kMaxCacheSize + 1, prefs_.size());
128     // Generate a random number in the range [0, |kMaxCacheSize| - 1] since the
129     // number of network IDs in |prefs_| other than |network_id| is
130     // |kMaxCacheSize|.
131     int index_to_delete = base::RandInt(0, kMaxCacheSize - 1);
132 
133     for (auto it : prefs_) {
134       // Delete the kth element in the dictionary, not including the element
135       // that represents the current network. k == |index_to_delete|.
136       if (nqe::internal::NetworkID::FromString(it.first) == network_id)
137         continue;
138 
139       if (index_to_delete == 0) {
140         prefs_.Remove(it.first);
141         break;
142       }
143       index_to_delete--;
144     }
145   }
146   DCHECK_GE(kMaxCacheSize, prefs_.size());
147 
148   // Notify the pref delegate so that it updates the prefs on the disk.
149   pref_delegate_->SetDictionaryValue(prefs_);
150 }
151 
ForceReadPrefsForTesting() const152 ParsedPrefs NetworkQualitiesPrefsManager::ForceReadPrefsForTesting() const {
153   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
154   base::Value::Dict value = pref_delegate_->GetDictionaryValue();
155   return ConvertDictionaryValueToMap(value);
156 }
157 
158 }  // namespace net
159