| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // 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 <string> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/optional.h" |
| #include "base/rand_util.h" |
| #include "base/sequenced_task_runner.h" |
| #include "base/threading/thread_checker.h" |
| #include "base/threading/thread_task_runner_handle.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::DictionaryValue* value) { |
| DCHECK_GE(kMaxCacheSize, value->size()); |
| |
| ParsedPrefs read_prefs; |
| for (const auto& it : value->DictItems()) { |
| nqe::internal::NetworkID network_id = |
| nqe::internal::NetworkID::FromString(it.first); |
| |
| std::string effective_connection_type_string; |
| const bool effective_connection_type_available = |
| it.second.GetAsString(&effective_connection_type_string); |
| DCHECK(effective_connection_type_available); |
| |
| base::Optional<EffectiveConnectionType> effective_connection_type = |
| GetEffectiveConnectionTypeForName(effective_connection_type_string); |
| 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)), |
| pref_task_runner_(base::ThreadTaskRunnerHandle::Get()), |
| prefs_(pref_delegate_->GetDictionaryValue()), |
| network_quality_estimator_(nullptr), |
| read_prefs_startup_(ConvertDictionaryValueToMap(prefs_.get())), |
| pref_weak_ptr_factory_(this) { |
| DCHECK(pref_delegate_); |
| DCHECK(pref_task_runner_); |
| DCHECK_GE(kMaxCacheSize, prefs_->size()); |
| |
| pref_weak_ptr_ = pref_weak_ptr_factory_.GetWeakPtr(); |
| } |
| |
| NetworkQualitiesPrefsManager::~NetworkQualitiesPrefsManager() { |
| if (!network_task_runner_) |
| return; |
| DCHECK(network_task_runner_->RunsTasksInCurrentSequence()); |
| if (network_quality_estimator_) |
| network_quality_estimator_->RemoveNetworkQualitiesCacheObserver(this); |
| } |
| |
| void NetworkQualitiesPrefsManager::InitializeOnNetworkThread( |
| NetworkQualityEstimator* network_quality_estimator) { |
| DCHECK(!network_task_runner_); |
| DCHECK(network_quality_estimator); |
| |
| network_task_runner_ = base::ThreadTaskRunnerHandle::Get(); |
| 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::OnChangeInCachedNetworkQuality( |
| const nqe::internal::NetworkID& network_id, |
| const nqe::internal::CachedNetworkQuality& cached_network_quality) { |
| DCHECK(network_task_runner_->RunsTasksInCurrentSequence()); |
| |
| // Notify |this| on the pref thread. |
| pref_task_runner_->PostTask( |
| FROM_HERE, |
| base::Bind(&NetworkQualitiesPrefsManager:: |
| OnChangeInCachedNetworkQualityOnPrefSequence, |
| pref_weak_ptr_, network_id, cached_network_quality)); |
| } |
| |
| void NetworkQualitiesPrefsManager::ShutdownOnPrefSequence() { |
| DCHECK(pref_task_runner_->RunsTasksInCurrentSequence()); |
| pref_weak_ptr_factory_.InvalidateWeakPtrs(); |
| pref_delegate_.reset(); |
| } |
| |
| void NetworkQualitiesPrefsManager::ClearPrefs() { |
| DCHECK(pref_task_runner_->RunsTasksInCurrentSequence()); |
| prefs_->Clear(); |
| DCHECK_EQ(0u, prefs_->size()); |
| pref_delegate_->SetDictionaryValue(*prefs_); |
| } |
| |
| void NetworkQualitiesPrefsManager::OnChangeInCachedNetworkQualityOnPrefSequence( |
| const nqe::internal::NetworkID& network_id, |
| const nqe::internal::CachedNetworkQuality& cached_network_quality) { |
| // The prefs can only be written on the pref thread. |
| DCHECK(pref_task_runner_->RunsTasksInCurrentSequence()); |
| 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_->SetString(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 (const auto& it : prefs_->DictItems()) { |
| // 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_->RemoveKey(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(pref_task_runner_->RunsTasksInCurrentSequence()); |
| std::unique_ptr<base::DictionaryValue> value( |
| pref_delegate_->GetDictionaryValue()); |
| return ConvertDictionaryValueToMap(value.get()); |
| } |
| |
| } // namespace net |