blob: e569bbcd8c203297afc2f7b909b88d1446555088 [file] [log] [blame]
glevin5dd01a72016-03-23 23:08:121// Copyright 2016 The Chromium Authors. All rights reserved.
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/quirks/quirks_manager.h"
6
7#include <utility>
8
glevin5dd01a72016-03-23 23:08:129#include "base/files/file_util.h"
10#include "base/format_macros.h"
dcheng82beb4f2016-04-26 00:35:0211#include "base/memory/ptr_util.h"
glevin5dd01a72016-03-23 23:08:1212#include "base/path_service.h"
glevin5dd01a72016-03-23 23:08:1213#include "base/strings/stringprintf.h"
Daniel Erat661b99f2017-07-06 15:57:5914#include "base/task_runner.h"
glevin5dd01a72016-03-23 23:08:1215#include "base/task_runner_util.h"
16#include "components/prefs/pref_registry_simple.h"
17#include "components/prefs/scoped_user_pref_update.h"
18#include "components/quirks/pref_names.h"
19#include "components/quirks/quirks_client.h"
glevin5dd01a72016-03-23 23:08:1220#include "net/url_request/url_fetcher.h"
21#include "net/url_request/url_request_context_getter.h"
22#include "url/gurl.h"
23
24namespace quirks {
25
26namespace {
27
28QuirksManager* manager_ = nullptr;
29
30const char kIccExtension[] = ".icc";
31
32// How often we query Quirks Server.
33const int kDaysBetweenServerChecks = 30;
34
glevinf9f834f2016-10-25 22:52:1535// Check if QuirksClient has already downloaded icc file from server.
36base::FilePath CheckForIccFile(const base::FilePath& path) {
glevin5dd01a72016-03-23 23:08:1237 const bool exists = base::PathExists(path);
38 VLOG(1) << (exists ? "File" : "No File") << " found at " << path.value();
39 // TODO(glevin): If file exists, do we want to implement a hash to verify that
40 // the file hasn't been corrupted or tampered with?
glevinf9f834f2016-10-25 22:52:1541 return exists ? path : base::FilePath();
glevin5dd01a72016-03-23 23:08:1242}
43
44} // namespace
45
46std::string IdToHexString(int64_t product_id) {
47 return base::StringPrintf("%08" PRIx64, product_id);
48}
49
50std::string IdToFileName(int64_t product_id) {
51 return IdToHexString(product_id).append(kIccExtension);
52}
53
54////////////////////////////////////////////////////////////////////////////////
55// QuirksManager
56
57QuirksManager::QuirksManager(
dcheng82beb4f2016-04-26 00:35:0258 std::unique_ptr<Delegate> delegate,
Daniel Erat661b99f2017-07-06 15:57:5959 scoped_refptr<base::TaskRunner> task_runner,
glevin5dd01a72016-03-23 23:08:1260 PrefService* local_state,
61 scoped_refptr<net::URLRequestContextGetter> url_context_getter)
62 : waiting_for_login_(true),
63 delegate_(std::move(delegate)),
Daniel Erat661b99f2017-07-06 15:57:5964 task_runner_(task_runner),
glevin5dd01a72016-03-23 23:08:1265 local_state_(local_state),
66 url_context_getter_(url_context_getter),
67 weak_ptr_factory_(this) {}
68
69QuirksManager::~QuirksManager() {
70 clients_.clear();
71 manager_ = nullptr;
72}
73
74// static
75void QuirksManager::Initialize(
dcheng82beb4f2016-04-26 00:35:0276 std::unique_ptr<Delegate> delegate,
Daniel Erat661b99f2017-07-06 15:57:5977 scoped_refptr<base::TaskRunner> task_runner,
glevin5dd01a72016-03-23 23:08:1278 PrefService* local_state,
79 scoped_refptr<net::URLRequestContextGetter> url_context_getter) {
Daniel Erat661b99f2017-07-06 15:57:5980 manager_ = new QuirksManager(std::move(delegate), task_runner, local_state,
glevin5dd01a72016-03-23 23:08:1281 url_context_getter);
82}
83
84// static
85void QuirksManager::Shutdown() {
86 delete manager_;
87}
88
89// static
90QuirksManager* QuirksManager::Get() {
91 DCHECK(manager_);
92 return manager_;
93}
94
95// static
96void QuirksManager::RegisterPrefs(PrefRegistrySimple* registry) {
97 registry->RegisterDictionaryPref(prefs::kQuirksClientLastServerCheck);
98}
99
100// Delay downloads until after login, to ensure that device policy has been set.
101void QuirksManager::OnLoginCompleted() {
102 if (!waiting_for_login_)
103 return;
104
105 waiting_for_login_ = false;
glevin8c8f2d72016-04-20 13:03:28106 if (!clients_.empty() && !QuirksEnabled()) {
107 VLOG(2) << clients_.size() << " client(s) deleted.";
108 clients_.clear();
glevin5b821b52016-04-04 16:42:47109 }
110
dcheng82beb4f2016-04-26 00:35:02111 for (const std::unique_ptr<QuirksClient>& client : clients_)
glevin5dd01a72016-03-23 23:08:12112 client->StartDownload();
113}
114
115void QuirksManager::RequestIccProfilePath(
116 int64_t product_id,
glevin2aa9dd12017-03-16 21:07:59117 const std::string& display_name,
glevin5dd01a72016-03-23 23:08:12118 const RequestFinishedCallback& on_request_finished) {
119 DCHECK(thread_checker_.CalledOnValidThread());
120
glevinf9f834f2016-10-25 22:52:15121 if (!QuirksEnabled()) {
122 VLOG(1) << "Quirks Client disabled.";
123 on_request_finished.Run(base::FilePath(), false);
124 return;
125 }
126
glevin8a081d12016-09-30 18:26:52127 if (!product_id) {
128 VLOG(1) << "Could not determine display information (product id = 0)";
129 on_request_finished.Run(base::FilePath(), false);
130 return;
131 }
132
glevin5dd01a72016-03-23 23:08:12133 std::string name = IdToFileName(product_id);
134 base::PostTaskAndReplyWithResult(
Daniel Erat661b99f2017-07-06 15:57:59135 task_runner_.get(), FROM_HERE,
glevin5dd01a72016-03-23 23:08:12136 base::Bind(&CheckForIccFile,
glevinf9f834f2016-10-25 22:52:15137 delegate_->GetDisplayProfileDirectory().Append(name)),
glevin5dd01a72016-03-23 23:08:12138 base::Bind(&QuirksManager::OnIccFilePathRequestCompleted,
glevin2aa9dd12017-03-16 21:07:59139 weak_ptr_factory_.GetWeakPtr(), product_id, display_name,
glevin5dd01a72016-03-23 23:08:12140 on_request_finished));
141}
142
143void QuirksManager::ClientFinished(QuirksClient* client) {
144 DCHECK(thread_checker_.CalledOnValidThread());
145 SetLastServerCheck(client->product_id(), base::Time::Now());
146 auto it = std::find_if(clients_.begin(), clients_.end(),
dcheng82beb4f2016-04-26 00:35:02147 [client](const std::unique_ptr<QuirksClient>& c) {
glevin5dd01a72016-03-23 23:08:12148 return c.get() == client;
149 });
150 CHECK(it != clients_.end());
151 clients_.erase(it);
152}
153
dcheng82beb4f2016-04-26 00:35:02154std::unique_ptr<net::URLFetcher> QuirksManager::CreateURLFetcher(
glevin5dd01a72016-03-23 23:08:12155 const GURL& url,
156 net::URLFetcherDelegate* delegate) {
157 if (!fake_quirks_fetcher_creator_.is_null())
158 return fake_quirks_fetcher_creator_.Run(url, delegate);
159
160 return net::URLFetcher::Create(url, net::URLFetcher::GET, delegate);
161}
162
163void QuirksManager::OnIccFilePathRequestCompleted(
164 int64_t product_id,
glevin2aa9dd12017-03-16 21:07:59165 const std::string& display_name,
glevin5dd01a72016-03-23 23:08:12166 const RequestFinishedCallback& on_request_finished,
167 base::FilePath path) {
168 DCHECK(thread_checker_.CalledOnValidThread());
169
glevinf9f834f2016-10-25 22:52:15170 // If we found a file, just inform requester.
171 if (!path.empty()) {
glevin5dd01a72016-03-23 23:08:12172 on_request_finished.Run(path, false);
173 // TODO(glevin): If Quirks files are ever modified on the server, we'll need
174 // to modify this logic to check for updates. See crbug.com/595024.
175 return;
176 }
177
178 double last_check = 0.0;
179 local_state_->GetDictionary(prefs::kQuirksClientLastServerCheck)
180 ->GetDouble(IdToHexString(product_id), &last_check);
181
glevin5dd01a72016-03-23 23:08:12182 const base::TimeDelta time_since =
183 base::Time::Now() - base::Time::FromDoubleT(last_check);
184
185 // Don't need server check if we've checked within last 30 days.
186 if (time_since < base::TimeDelta::FromDays(kDaysBetweenServerChecks)) {
187 VLOG(2) << time_since.InDays()
188 << " days since last Quirks Server check for display "
189 << IdToHexString(product_id);
190 on_request_finished.Run(base::FilePath(), false);
191 return;
192 }
193
glevin0bbe13102016-07-11 01:15:02194 // Create and start a client to download file.
glevin5dd01a72016-03-23 23:08:12195 QuirksClient* client =
glevin2aa9dd12017-03-16 21:07:59196 new QuirksClient(product_id, display_name, on_request_finished, this);
dcheng82beb4f2016-04-26 00:35:02197 clients_.insert(base::WrapUnique(client));
glevin5dd01a72016-03-23 23:08:12198 if (!waiting_for_login_)
199 client->StartDownload();
200 else
201 VLOG(2) << "Quirks Client created; waiting for login to begin download.";
202}
203
glevin5b821b52016-04-04 16:42:47204bool QuirksManager::QuirksEnabled() {
glevin8c8f2d72016-04-20 13:03:28205 if (!delegate_->DevicePolicyEnabled()) {
206 VLOG(2) << "Quirks Client disabled by device policy.";
207 return false;
208 }
209 return true;
glevin5b821b52016-04-04 16:42:47210}
211
glevin5dd01a72016-03-23 23:08:12212void QuirksManager::SetLastServerCheck(int64_t product_id,
213 const base::Time& last_check) {
214 DCHECK(thread_checker_.CalledOnValidThread());
215 DictionaryPrefUpdate dict(local_state_, prefs::kQuirksClientLastServerCheck);
216 dict->SetDouble(IdToHexString(product_id), last_check.ToDoubleT());
217}
218
219} // namespace quirks