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 return pref_service_->GetDict(kNetworkQualitiesPref).Clone();
186 }
187
188 private:
189 // Schedules the writing of the lossy prefs.
SchedulePendingLossyWrites()190 void SchedulePendingLossyWrites() {
191 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
192 pref_service_->SchedulePendingLossyWrites();
193 lossy_prefs_writing_task_posted_ = false;
194 }
195
196 raw_ptr<PrefService> pref_service_;
197
198 // True if the task that schedules the writing of the lossy prefs has been
199 // posted.
200 bool lossy_prefs_writing_task_posted_;
201
202 THREAD_CHECKER(thread_checker_);
203
204 base::WeakPtrFactory<NetworkQualitiesPrefDelegateImpl> weak_ptr_factory_{
205 this};
206 };
207
208 } // namespace
209
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)210 CronetPrefsManager::CronetPrefsManager(
211 const std::string& storage_path,
212 scoped_refptr<base::SingleThreadTaskRunner> network_task_runner,
213 scoped_refptr<base::SequencedTaskRunner> file_task_runner,
214 bool enable_network_quality_estimator,
215 bool enable_host_cache_persistence,
216 net::NetLog* net_log,
217 net::URLRequestContextBuilder* context_builder) {
218 DCHECK(network_task_runner->BelongsToCurrentThread());
219 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
220
221 #if BUILDFLAG(IS_WIN)
222 base::FilePath storage_file_path(
223 base::FilePath::FromUTF8Unsafe(storage_path));
224 #else
225 base::FilePath storage_file_path(storage_path);
226 #endif
227
228 // Make sure storage directory has correct version.
229 {
230 base::ScopedAllowBlocking allow_blocking;
231 InitializeStorageDirectory(storage_file_path);
232 }
233
234 base::FilePath filepath =
235 storage_file_path.Append(kPrefsDirectoryName).Append(kPrefsFileName);
236
237 json_pref_store_ = new JsonPrefStore(filepath, std::unique_ptr<PrefFilter>(),
238 file_task_runner);
239
240 // Register prefs and set up the PrefService.
241 PrefServiceFactory factory;
242 factory.set_user_prefs(json_pref_store_);
243 scoped_refptr<PrefRegistrySimple> registry(new PrefRegistrySimple());
244 registry->RegisterDictionaryPref(kHttpServerPropertiesPref);
245
246 if (enable_network_quality_estimator) {
247 // Use lossy prefs to limit the overhead of reading/writing the prefs.
248 registry->RegisterDictionaryPref(kNetworkQualitiesPref,
249 PrefRegistry::LOSSY_PREF);
250 }
251
252 if (enable_host_cache_persistence) {
253 registry->RegisterListPref(kHostCachePref);
254 }
255
256 {
257 base::ScopedAllowBlocking allow_blocking;
258 pref_service_ = factory.Create(registry.get());
259 }
260
261 context_builder->SetHttpServerProperties(
262 std::make_unique<net::HttpServerProperties>(
263 std::make_unique<PrefServiceAdapter>(pref_service_.get()), net_log));
264 }
265
~CronetPrefsManager()266 CronetPrefsManager::~CronetPrefsManager() {
267 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
268 }
269
SetupNqePersistence(net::NetworkQualityEstimator * nqe)270 void CronetPrefsManager::SetupNqePersistence(
271 net::NetworkQualityEstimator* nqe) {
272 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
273 network_qualities_prefs_manager_ =
274 std::make_unique<net::NetworkQualitiesPrefsManager>(
275 std::make_unique<NetworkQualitiesPrefDelegateImpl>(
276 pref_service_.get()));
277
278 network_qualities_prefs_manager_->InitializeOnNetworkThread(nqe);
279 }
280
SetupHostCachePersistence(net::HostCache * host_cache,int host_cache_persistence_delay_ms,net::NetLog * net_log)281 void CronetPrefsManager::SetupHostCachePersistence(
282 net::HostCache* host_cache,
283 int host_cache_persistence_delay_ms,
284 net::NetLog* net_log) {
285 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
286 host_cache_persistence_manager_ =
287 std::make_unique<HostCachePersistenceManager>(
288 host_cache, pref_service_.get(), kHostCachePref,
289 base::Milliseconds(host_cache_persistence_delay_ms), net_log);
290 }
291
PrepareForShutdown()292 void CronetPrefsManager::PrepareForShutdown() {
293 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
294 if (pref_service_)
295 pref_service_->CommitPendingWrite();
296
297 // Shutdown managers on the Pref sequence.
298 if (network_qualities_prefs_manager_)
299 network_qualities_prefs_manager_->ShutdownOnPrefSequence();
300
301 host_cache_persistence_manager_.reset();
302 }
303
304 } // namespace cronet
305