1 // Copyright 2017 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 "components/cronet/cronet_prefs_manager.h"
6
7 #include <memory>
8
9 #include "base/files/file_path.h"
10 #include "base/files/file_util.h"
11 #include "base/functional/bind.h"
12 #include "base/functional/callback.h"
13 #include "base/location.h"
14 #include "base/memory/raw_ptr.h"
15 #include "base/metrics/histogram_macros.h"
16 #include "base/task/sequenced_task_runner.h"
17 #include "base/task/single_thread_task_runner.h"
18 #include "base/threading/thread_restrictions.h"
19 #include "base/time/time.h"
20 #include "build/build_config.h"
21 #include "components/cronet/host_cache_persistence_manager.h"
22 #include "components/prefs/json_pref_store.h"
23 #include "components/prefs/pref_change_registrar.h"
24 #include "components/prefs/pref_registry_simple.h"
25 #include "components/prefs/pref_service.h"
26 #include "components/prefs/pref_service_factory.h"
27 #include "net/http/http_server_properties.h"
28 #include "net/nqe/network_qualities_prefs_manager.h"
29 #include "net/url_request/url_request_context_builder.h"
30
31 namespace cronet {
32 namespace {
33
34 // Name of the pref used for HTTP server properties persistence.
35 const char kHttpServerPropertiesPref[] = "net.http_server_properties";
36 // Name of preference directory.
37 const base::FilePath::CharType kPrefsDirectoryName[] =
38 FILE_PATH_LITERAL("prefs");
39 // Name of preference file.
40 const base::FilePath::CharType kPrefsFileName[] =
41 FILE_PATH_LITERAL("local_prefs.json");
42 // Current version of disk storage.
43 const int32_t kStorageVersion = 1;
44 // Version number used when the version of disk storage is unknown.
45 const uint32_t kStorageVersionUnknown = 0;
46 // Name of the pref used for host cache persistence.
47 const char kHostCachePref[] = "net.host_cache";
48 // Name of the pref used for NQE persistence.
49 const char kNetworkQualitiesPref[] = "net.network_qualities";
50
IsCurrentVersion(const base::FilePath & version_filepath)51 bool IsCurrentVersion(const base::FilePath& version_filepath) {
52 if (!base::PathExists(version_filepath))
53 return false;
54 base::File version_file(version_filepath,
55 base::File::FLAG_OPEN | base::File::FLAG_READ);
56 uint32_t version = kStorageVersionUnknown;
57 int bytes_read =
58 version_file.Read(0, reinterpret_cast<char*>(&version), sizeof(version));
59 if (bytes_read != sizeof(version)) {
60 DLOG(WARNING) << "Cannot read from version file.";
61 return false;
62 }
63 return version == kStorageVersion;
64 }
65
66 // TODO(xunjieli): Handle failures.
InitializeStorageDirectory(const base::FilePath & dir)67 void InitializeStorageDirectory(const base::FilePath& dir) {
68 // Checks version file and clear old storage.
69 base::FilePath version_filepath(dir.AppendASCII("version"));
70 if (IsCurrentVersion(version_filepath)) {
71 // The version is up to date, so there is nothing to do.
72 return;
73 }
74 // Delete old directory recursively and create a new directory.
75 // base::DeletePathRecursively() returns true if the directory does not exist,
76 // so it is fine if there is nothing on disk.
77 if (!(base::DeletePathRecursively(dir) && base::CreateDirectory(dir))) {
78 DLOG(WARNING) << "Cannot purge directory.";
79 return;
80 }
81 base::File new_version_file(version_filepath, base::File::FLAG_CREATE_ALWAYS |
82 base::File::FLAG_WRITE);
83
84 if (!new_version_file.IsValid()) {
85 DLOG(WARNING) << "Cannot create a version file.";
86 return;
87 }
88
89 DCHECK(new_version_file.created());
90 uint32_t new_version = kStorageVersion;
91 int bytes_written = new_version_file.Write(
92 0, reinterpret_cast<char*>(&new_version), sizeof(new_version));
93 if (bytes_written != sizeof(new_version)) {
94 DLOG(WARNING) << "Cannot write to version file.";
95 return;
96 }
97 base::FilePath prefs_dir = dir.Append(kPrefsDirectoryName);
98 if (!base::CreateDirectory(prefs_dir)) {
99 DLOG(WARNING) << "Cannot create prefs directory";
100 return;
101 }
102 }
103
104 // Connects the HttpServerProperties's storage to the prefs.
105 class PrefServiceAdapter : public net::HttpServerProperties::PrefDelegate {
106 public:
PrefServiceAdapter(PrefService * pref_service)107 explicit PrefServiceAdapter(PrefService* pref_service)
108 : pref_service_(pref_service), path_(kHttpServerPropertiesPref) {
109 pref_change_registrar_.Init(pref_service_);
110 }
111
112 PrefServiceAdapter(const PrefServiceAdapter&) = delete;
113 PrefServiceAdapter& operator=(const PrefServiceAdapter&) = delete;
114
~PrefServiceAdapter()115 ~PrefServiceAdapter() override {}
116
117 // PrefDelegate implementation.
GetServerProperties() const118 const base::Value::Dict& GetServerProperties() const override {
119 return pref_service_->GetDict(path_);
120 }
121
SetServerProperties(base::Value::Dict dict,base::OnceClosure callback)122 void SetServerProperties(base::Value::Dict dict,
123 base::OnceClosure callback) override {
124 pref_service_->SetDict(path_, std::move(dict));
125 if (callback)
126 pref_service_->CommitPendingWrite(std::move(callback));
127 }
128
WaitForPrefLoad(base::OnceClosure callback)129 void WaitForPrefLoad(base::OnceClosure callback) override {
130 // Notify the pref manager that settings are already loaded, as a result
131 // of initializing the pref store synchronously.
132 base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
133 FROM_HERE, std::move(callback));
134 }
135
136 private:
137 raw_ptr<PrefService> pref_service_;
138 const std::string path_;
139 PrefChangeRegistrar pref_change_registrar_;
140 }; // class PrefServiceAdapter
141
142 class NetworkQualitiesPrefDelegateImpl
143 : public net::NetworkQualitiesPrefsManager::PrefDelegate {
144 public:
145 // Caller must guarantee that |pref_service| outlives |this|.
NetworkQualitiesPrefDelegateImpl(PrefService * pref_service)146 explicit NetworkQualitiesPrefDelegateImpl(PrefService* pref_service)
147 : pref_service_(pref_service), lossy_prefs_writing_task_posted_(false) {
148 DCHECK(pref_service_);
149 }
150
151 NetworkQualitiesPrefDelegateImpl(const NetworkQualitiesPrefDelegateImpl&) =
152 delete;
153 NetworkQualitiesPrefDelegateImpl& operator=(
154 const NetworkQualitiesPrefDelegateImpl&) = delete;
155
~NetworkQualitiesPrefDelegateImpl()156 ~NetworkQualitiesPrefDelegateImpl() override {}
157
158 // net::NetworkQualitiesPrefsManager::PrefDelegate implementation.
SetDictionaryValue(const base::Value::Dict & dict)159 void SetDictionaryValue(const base::Value::Dict& dict) override {
160 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
161
162 pref_service_->SetDict(kNetworkQualitiesPref, dict.Clone());
163 if (lossy_prefs_writing_task_posted_)
164 return;
165
166 // Post the task that schedules the writing of the lossy prefs.
167 lossy_prefs_writing_task_posted_ = true;
168
169 // Delay after which the task that schedules the writing of the lossy prefs.
170 // This is needed in case the writing of the lossy prefs is not scheduled
171 // automatically. The delay was chosen so that it is large enough that it
172 // does not affect the startup performance.
173 static const int32_t kUpdatePrefsDelaySeconds = 10;
174
175 base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
176 FROM_HERE,
177 base::BindOnce(
178 &NetworkQualitiesPrefDelegateImpl::SchedulePendingLossyWrites,
179 weak_ptr_factory_.GetWeakPtr()),
180 base::Seconds(kUpdatePrefsDelaySeconds));
181 }
182
GetDictionaryValue()183 base::Value::Dict GetDictionaryValue() override {
184 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
185 UMA_HISTOGRAM_EXACT_LINEAR("NQE.Prefs.ReadCount", 1, 2);
186 return pref_service_->GetDict(kNetworkQualitiesPref).Clone();
187 }
188
189 private:
190 // Schedules the writing of the lossy prefs.
SchedulePendingLossyWrites()191 void SchedulePendingLossyWrites() {
192 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
193 UMA_HISTOGRAM_EXACT_LINEAR("NQE.Prefs.WriteCount", 1, 2);
194 pref_service_->SchedulePendingLossyWrites();
195 lossy_prefs_writing_task_posted_ = false;
196 }
197
198 raw_ptr<PrefService> pref_service_;
199
200 // True if the task that schedules the writing of the lossy prefs has been
201 // posted.
202 bool lossy_prefs_writing_task_posted_;
203
204 THREAD_CHECKER(thread_checker_);
205
206 base::WeakPtrFactory<NetworkQualitiesPrefDelegateImpl> weak_ptr_factory_{
207 this};
208 };
209
210 } // namespace
211
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)212 CronetPrefsManager::CronetPrefsManager(
213 const std::string& storage_path,
214 scoped_refptr<base::SingleThreadTaskRunner> network_task_runner,
215 scoped_refptr<base::SequencedTaskRunner> file_task_runner,
216 bool enable_network_quality_estimator,
217 bool enable_host_cache_persistence,
218 net::NetLog* net_log,
219 net::URLRequestContextBuilder* context_builder) {
220 DCHECK(network_task_runner->BelongsToCurrentThread());
221 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
222
223 #if BUILDFLAG(IS_WIN)
224 base::FilePath storage_file_path(
225 base::FilePath::FromUTF8Unsafe(storage_path));
226 #else
227 base::FilePath storage_file_path(storage_path);
228 #endif
229
230 // Make sure storage directory has correct version.
231 {
232 base::ScopedAllowBlocking allow_blocking;
233 InitializeStorageDirectory(storage_file_path);
234 }
235
236 base::FilePath filepath =
237 storage_file_path.Append(kPrefsDirectoryName).Append(kPrefsFileName);
238
239 json_pref_store_ = new JsonPrefStore(filepath, std::unique_ptr<PrefFilter>(),
240 file_task_runner);
241
242 // Register prefs and set up the PrefService.
243 PrefServiceFactory factory;
244 factory.set_user_prefs(json_pref_store_);
245 scoped_refptr<PrefRegistrySimple> registry(new PrefRegistrySimple());
246 registry->RegisterDictionaryPref(kHttpServerPropertiesPref);
247
248 if (enable_network_quality_estimator) {
249 // Use lossy prefs to limit the overhead of reading/writing the prefs.
250 registry->RegisterDictionaryPref(kNetworkQualitiesPref,
251 PrefRegistry::LOSSY_PREF);
252 }
253
254 if (enable_host_cache_persistence) {
255 registry->RegisterListPref(kHostCachePref);
256 }
257
258 {
259 base::ScopedAllowBlocking allow_blocking;
260 pref_service_ = factory.Create(registry.get());
261 }
262
263 context_builder->SetHttpServerProperties(
264 std::make_unique<net::HttpServerProperties>(
265 std::make_unique<PrefServiceAdapter>(pref_service_.get()), net_log));
266 }
267
~CronetPrefsManager()268 CronetPrefsManager::~CronetPrefsManager() {
269 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
270 }
271
SetupNqePersistence(net::NetworkQualityEstimator * nqe)272 void CronetPrefsManager::SetupNqePersistence(
273 net::NetworkQualityEstimator* nqe) {
274 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
275 network_qualities_prefs_manager_ =
276 std::make_unique<net::NetworkQualitiesPrefsManager>(
277 std::make_unique<NetworkQualitiesPrefDelegateImpl>(
278 pref_service_.get()));
279
280 network_qualities_prefs_manager_->InitializeOnNetworkThread(nqe);
281 }
282
SetupHostCachePersistence(net::HostCache * host_cache,int host_cache_persistence_delay_ms,net::NetLog * net_log)283 void CronetPrefsManager::SetupHostCachePersistence(
284 net::HostCache* host_cache,
285 int host_cache_persistence_delay_ms,
286 net::NetLog* net_log) {
287 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
288 host_cache_persistence_manager_ =
289 std::make_unique<HostCachePersistenceManager>(
290 host_cache, pref_service_.get(), kHostCachePref,
291 base::Milliseconds(host_cache_persistence_delay_ms), net_log);
292 }
293
PrepareForShutdown()294 void CronetPrefsManager::PrepareForShutdown() {
295 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
296 if (pref_service_)
297 pref_service_->CommitPendingWrite();
298
299 // Shutdown managers on the Pref sequence.
300 if (network_qualities_prefs_manager_)
301 network_qualities_prefs_manager_->ShutdownOnPrefSequence();
302
303 host_cache_persistence_manager_.reset();
304 }
305
306 } // namespace cronet
307