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