| // Copyright 2017 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifdef UNSAFE_BUFFERS_BUILD |
| // TODO(crbug.com/40285824): Remove this and convert code to safer constructs. |
| #pragma allow_unsafe_buffers |
| #endif |
| |
| #include "components/cronet/cronet_prefs_manager.h" |
| |
| #include <memory> |
| |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback.h" |
| #include "base/location.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "base/time/time.h" |
| #include "build/build_config.h" |
| #include "components/cronet/host_cache_persistence_manager.h" |
| #include "components/prefs/json_pref_store.h" |
| #include "components/prefs/pref_change_registrar.h" |
| #include "components/prefs/pref_registry_simple.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/prefs/pref_service_factory.h" |
| #include "net/http/http_server_properties.h" |
| #include "net/nqe/network_qualities_prefs_manager.h" |
| #include "net/url_request/url_request_context_builder.h" |
| |
| namespace cronet { |
| namespace { |
| |
| // Name of the pref used for HTTP server properties persistence. |
| const char kHttpServerPropertiesPref[] = "net.http_server_properties"; |
| // Name of preference directory. |
| const base::FilePath::CharType kPrefsDirectoryName[] = |
| FILE_PATH_LITERAL("prefs"); |
| // Name of preference file. |
| const base::FilePath::CharType kPrefsFileName[] = |
| FILE_PATH_LITERAL("local_prefs.json"); |
| // Current version of disk storage. |
| const int32_t kStorageVersion = 1; |
| // Version number used when the version of disk storage is unknown. |
| const uint32_t kStorageVersionUnknown = 0; |
| // Name of the pref used for host cache persistence. |
| const char kHostCachePref[] = "net.host_cache"; |
| // Name of the pref used for NQE persistence. |
| const char kNetworkQualitiesPref[] = "net.network_qualities"; |
| |
| bool IsCurrentVersion(const base::FilePath& version_filepath) { |
| if (!base::PathExists(version_filepath)) |
| return false; |
| base::File version_file(version_filepath, |
| base::File::FLAG_OPEN | base::File::FLAG_READ); |
| uint32_t version = kStorageVersionUnknown; |
| int bytes_read = |
| version_file.Read(0, reinterpret_cast<char*>(&version), sizeof(version)); |
| if (bytes_read != sizeof(version)) { |
| DLOG(WARNING) << "Cannot read from version file."; |
| return false; |
| } |
| return version == kStorageVersion; |
| } |
| |
| // TODO(xunjieli): Handle failures. |
| void InitializeStorageDirectory(const base::FilePath& dir) { |
| // Checks version file and clear old storage. |
| base::FilePath version_filepath(dir.AppendASCII("version")); |
| if (IsCurrentVersion(version_filepath)) { |
| // The version is up to date, so there is nothing to do. |
| return; |
| } |
| // Delete old directory recursively and create a new directory. |
| // base::DeletePathRecursively() returns true if the directory does not exist, |
| // so it is fine if there is nothing on disk. |
| if (!(base::DeletePathRecursively(dir) && base::CreateDirectory(dir))) { |
| DLOG(WARNING) << "Cannot purge directory."; |
| return; |
| } |
| base::File new_version_file(version_filepath, base::File::FLAG_CREATE_ALWAYS | |
| base::File::FLAG_WRITE); |
| |
| if (!new_version_file.IsValid()) { |
| DLOG(WARNING) << "Cannot create a version file."; |
| return; |
| } |
| |
| DCHECK(new_version_file.created()); |
| uint32_t new_version = kStorageVersion; |
| int bytes_written = new_version_file.Write( |
| 0, reinterpret_cast<char*>(&new_version), sizeof(new_version)); |
| if (bytes_written != sizeof(new_version)) { |
| DLOG(WARNING) << "Cannot write to version file."; |
| return; |
| } |
| base::FilePath prefs_dir = dir.Append(kPrefsDirectoryName); |
| if (!base::CreateDirectory(prefs_dir)) { |
| DLOG(WARNING) << "Cannot create prefs directory"; |
| return; |
| } |
| } |
| |
| // Connects the HttpServerProperties's storage to the prefs. |
| class PrefServiceAdapter : public net::HttpServerProperties::PrefDelegate { |
| public: |
| explicit PrefServiceAdapter(PrefService* pref_service) |
| : pref_service_(pref_service), path_(kHttpServerPropertiesPref) { |
| pref_change_registrar_.Init(pref_service_); |
| } |
| |
| PrefServiceAdapter(const PrefServiceAdapter&) = delete; |
| PrefServiceAdapter& operator=(const PrefServiceAdapter&) = delete; |
| |
| ~PrefServiceAdapter() override {} |
| |
| // PrefDelegate implementation. |
| const base::Value::Dict& GetServerProperties() const override { |
| return pref_service_->GetDict(path_); |
| } |
| |
| void SetServerProperties(base::Value::Dict dict, |
| base::OnceClosure callback) override { |
| pref_service_->SetDict(path_, std::move(dict)); |
| if (callback) |
| pref_service_->CommitPendingWrite(std::move(callback)); |
| } |
| |
| void WaitForPrefLoad(base::OnceClosure callback) override { |
| // Notify the pref manager that settings are already loaded, as a result |
| // of initializing the pref store synchronously. |
| base::SequencedTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, std::move(callback)); |
| } |
| |
| private: |
| raw_ptr<PrefService> pref_service_; |
| const std::string path_; |
| PrefChangeRegistrar pref_change_registrar_; |
| }; // class PrefServiceAdapter |
| |
| class NetworkQualitiesPrefDelegateImpl |
| : public net::NetworkQualitiesPrefsManager::PrefDelegate { |
| public: |
| // Caller must guarantee that |pref_service| outlives |this|. |
| explicit NetworkQualitiesPrefDelegateImpl(PrefService* pref_service) |
| : pref_service_(pref_service), lossy_prefs_writing_task_posted_(false) { |
| DCHECK(pref_service_); |
| } |
| |
| NetworkQualitiesPrefDelegateImpl(const NetworkQualitiesPrefDelegateImpl&) = |
| delete; |
| NetworkQualitiesPrefDelegateImpl& operator=( |
| const NetworkQualitiesPrefDelegateImpl&) = delete; |
| |
| ~NetworkQualitiesPrefDelegateImpl() override {} |
| |
| // net::NetworkQualitiesPrefsManager::PrefDelegate implementation. |
| void SetDictionaryValue(const base::Value::Dict& dict) override { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| pref_service_->SetDict(kNetworkQualitiesPref, dict.Clone()); |
| if (lossy_prefs_writing_task_posted_) |
| return; |
| |
| // Post the task that schedules the writing of the lossy prefs. |
| lossy_prefs_writing_task_posted_ = true; |
| |
| // Delay after which the task that schedules the writing of the lossy prefs. |
| // This is needed in case the writing of the lossy prefs is not scheduled |
| // automatically. The delay was chosen so that it is large enough that it |
| // does not affect the startup performance. |
| static const int32_t kUpdatePrefsDelaySeconds = 10; |
| |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask( |
| FROM_HERE, |
| base::BindOnce( |
| &NetworkQualitiesPrefDelegateImpl::SchedulePendingLossyWrites, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::Seconds(kUpdatePrefsDelaySeconds)); |
| } |
| |
| base::Value::Dict GetDictionaryValue() override { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| return pref_service_->GetDict(kNetworkQualitiesPref).Clone(); |
| } |
| |
| private: |
| // Schedules the writing of the lossy prefs. |
| void SchedulePendingLossyWrites() { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| pref_service_->SchedulePendingLossyWrites(); |
| lossy_prefs_writing_task_posted_ = false; |
| } |
| |
| raw_ptr<PrefService> pref_service_; |
| |
| // True if the task that schedules the writing of the lossy prefs has been |
| // posted. |
| bool lossy_prefs_writing_task_posted_; |
| |
| THREAD_CHECKER(thread_checker_); |
| |
| base::WeakPtrFactory<NetworkQualitiesPrefDelegateImpl> weak_ptr_factory_{ |
| this}; |
| }; |
| |
| } // namespace |
| |
| CronetPrefsManager::CronetPrefsManager( |
| const std::string& storage_path, |
| scoped_refptr<base::SingleThreadTaskRunner> network_task_runner, |
| scoped_refptr<base::SequencedTaskRunner> file_task_runner, |
| bool enable_network_quality_estimator, |
| bool enable_host_cache_persistence, |
| net::NetLog* net_log, |
| net::URLRequestContextBuilder* context_builder) { |
| DCHECK(network_task_runner->BelongsToCurrentThread()); |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| #if BUILDFLAG(IS_WIN) |
| base::FilePath storage_file_path( |
| base::FilePath::FromUTF8Unsafe(storage_path)); |
| #else |
| base::FilePath storage_file_path(storage_path); |
| #endif |
| |
| // Make sure storage directory has correct version. |
| { |
| base::ScopedAllowBlocking allow_blocking; |
| InitializeStorageDirectory(storage_file_path); |
| } |
| |
| base::FilePath filepath = |
| storage_file_path.Append(kPrefsDirectoryName).Append(kPrefsFileName); |
| |
| json_pref_store_ = new JsonPrefStore(filepath, std::unique_ptr<PrefFilter>(), |
| file_task_runner); |
| |
| // Register prefs and set up the PrefService. |
| PrefServiceFactory factory; |
| factory.set_user_prefs(json_pref_store_); |
| scoped_refptr<PrefRegistrySimple> registry(new PrefRegistrySimple()); |
| registry->RegisterDictionaryPref(kHttpServerPropertiesPref); |
| |
| if (enable_network_quality_estimator) { |
| // Use lossy prefs to limit the overhead of reading/writing the prefs. |
| registry->RegisterDictionaryPref(kNetworkQualitiesPref, |
| PrefRegistry::LOSSY_PREF); |
| } |
| |
| if (enable_host_cache_persistence) { |
| registry->RegisterListPref(kHostCachePref); |
| } |
| |
| { |
| base::ScopedAllowBlocking allow_blocking; |
| pref_service_ = factory.Create(registry.get()); |
| } |
| |
| context_builder->SetHttpServerProperties( |
| std::make_unique<net::HttpServerProperties>( |
| std::make_unique<PrefServiceAdapter>(pref_service_.get()), net_log)); |
| } |
| |
| CronetPrefsManager::~CronetPrefsManager() { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| } |
| |
| void CronetPrefsManager::SetupNqePersistence( |
| net::NetworkQualityEstimator* nqe) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| network_qualities_prefs_manager_ = |
| std::make_unique<net::NetworkQualitiesPrefsManager>( |
| std::make_unique<NetworkQualitiesPrefDelegateImpl>( |
| pref_service_.get())); |
| |
| network_qualities_prefs_manager_->InitializeOnNetworkThread(nqe); |
| } |
| |
| void CronetPrefsManager::SetupHostCachePersistence( |
| net::HostCache* host_cache, |
| int host_cache_persistence_delay_ms, |
| net::NetLog* net_log) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| host_cache_persistence_manager_ = |
| std::make_unique<HostCachePersistenceManager>( |
| host_cache, pref_service_.get(), kHostCachePref, |
| base::Milliseconds(host_cache_persistence_delay_ms), net_log); |
| } |
| |
| void CronetPrefsManager::PrepareForShutdown() { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| if (pref_service_) |
| pref_service_->CommitPendingWrite(); |
| |
| // Shutdown managers on the Pref sequence. |
| if (network_qualities_prefs_manager_) |
| network_qualities_prefs_manager_->ShutdownOnPrefSequence(); |
| |
| host_cache_persistence_manager_.reset(); |
| } |
| |
| } // namespace cronet |