glevin | 5dd01a7 | 2016-03-23 23:08:12 | [diff] [blame] | 1 | // 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 | |
glevin | 5dd01a7 | 2016-03-23 23:08:12 | [diff] [blame] | 9 | #include "base/files/file_util.h" |
| 10 | #include "base/format_macros.h" |
dcheng | 82beb4f | 2016-04-26 00:35:02 | [diff] [blame] | 11 | #include "base/memory/ptr_util.h" |
glevin | 5dd01a7 | 2016-03-23 23:08:12 | [diff] [blame] | 12 | #include "base/path_service.h" |
glevin | 5dd01a7 | 2016-03-23 23:08:12 | [diff] [blame] | 13 | #include "base/strings/stringprintf.h" |
Daniel Erat | 661b99f | 2017-07-06 15:57:59 | [diff] [blame] | 14 | #include "base/task_runner.h" |
glevin | 5dd01a7 | 2016-03-23 23:08:12 | [diff] [blame] | 15 | #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" |
glevin | 5dd01a7 | 2016-03-23 23:08:12 | [diff] [blame] | 20 | #include "net/url_request/url_fetcher.h" |
| 21 | #include "net/url_request/url_request_context_getter.h" |
| 22 | #include "url/gurl.h" |
| 23 | |
| 24 | namespace quirks { |
| 25 | |
| 26 | namespace { |
| 27 | |
| 28 | QuirksManager* manager_ = nullptr; |
| 29 | |
| 30 | const char kIccExtension[] = ".icc"; |
| 31 | |
| 32 | // How often we query Quirks Server. |
| 33 | const int kDaysBetweenServerChecks = 30; |
| 34 | |
glevin | f9f834f | 2016-10-25 22:52:15 | [diff] [blame] | 35 | // Check if QuirksClient has already downloaded icc file from server. |
| 36 | base::FilePath CheckForIccFile(const base::FilePath& path) { |
glevin | 5dd01a7 | 2016-03-23 23:08:12 | [diff] [blame] | 37 | 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? |
glevin | f9f834f | 2016-10-25 22:52:15 | [diff] [blame] | 41 | return exists ? path : base::FilePath(); |
glevin | 5dd01a7 | 2016-03-23 23:08:12 | [diff] [blame] | 42 | } |
| 43 | |
| 44 | } // namespace |
| 45 | |
| 46 | std::string IdToHexString(int64_t product_id) { |
| 47 | return base::StringPrintf("%08" PRIx64, product_id); |
| 48 | } |
| 49 | |
| 50 | std::string IdToFileName(int64_t product_id) { |
| 51 | return IdToHexString(product_id).append(kIccExtension); |
| 52 | } |
| 53 | |
| 54 | //////////////////////////////////////////////////////////////////////////////// |
| 55 | // QuirksManager |
| 56 | |
| 57 | QuirksManager::QuirksManager( |
dcheng | 82beb4f | 2016-04-26 00:35:02 | [diff] [blame] | 58 | std::unique_ptr<Delegate> delegate, |
Daniel Erat | 661b99f | 2017-07-06 15:57:59 | [diff] [blame] | 59 | scoped_refptr<base::TaskRunner> task_runner, |
glevin | 5dd01a7 | 2016-03-23 23:08:12 | [diff] [blame] | 60 | PrefService* local_state, |
| 61 | scoped_refptr<net::URLRequestContextGetter> url_context_getter) |
| 62 | : waiting_for_login_(true), |
| 63 | delegate_(std::move(delegate)), |
Daniel Erat | 661b99f | 2017-07-06 15:57:59 | [diff] [blame] | 64 | task_runner_(task_runner), |
glevin | 5dd01a7 | 2016-03-23 23:08:12 | [diff] [blame] | 65 | local_state_(local_state), |
| 66 | url_context_getter_(url_context_getter), |
| 67 | weak_ptr_factory_(this) {} |
| 68 | |
| 69 | QuirksManager::~QuirksManager() { |
| 70 | clients_.clear(); |
| 71 | manager_ = nullptr; |
| 72 | } |
| 73 | |
| 74 | // static |
| 75 | void QuirksManager::Initialize( |
dcheng | 82beb4f | 2016-04-26 00:35:02 | [diff] [blame] | 76 | std::unique_ptr<Delegate> delegate, |
Daniel Erat | 661b99f | 2017-07-06 15:57:59 | [diff] [blame] | 77 | scoped_refptr<base::TaskRunner> task_runner, |
glevin | 5dd01a7 | 2016-03-23 23:08:12 | [diff] [blame] | 78 | PrefService* local_state, |
| 79 | scoped_refptr<net::URLRequestContextGetter> url_context_getter) { |
Daniel Erat | 661b99f | 2017-07-06 15:57:59 | [diff] [blame] | 80 | manager_ = new QuirksManager(std::move(delegate), task_runner, local_state, |
glevin | 5dd01a7 | 2016-03-23 23:08:12 | [diff] [blame] | 81 | url_context_getter); |
| 82 | } |
| 83 | |
| 84 | // static |
| 85 | void QuirksManager::Shutdown() { |
| 86 | delete manager_; |
| 87 | } |
| 88 | |
| 89 | // static |
| 90 | QuirksManager* QuirksManager::Get() { |
| 91 | DCHECK(manager_); |
| 92 | return manager_; |
| 93 | } |
| 94 | |
| 95 | // static |
| 96 | void 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. |
| 101 | void QuirksManager::OnLoginCompleted() { |
| 102 | if (!waiting_for_login_) |
| 103 | return; |
| 104 | |
| 105 | waiting_for_login_ = false; |
glevin | 8c8f2d7 | 2016-04-20 13:03:28 | [diff] [blame] | 106 | if (!clients_.empty() && !QuirksEnabled()) { |
| 107 | VLOG(2) << clients_.size() << " client(s) deleted."; |
| 108 | clients_.clear(); |
glevin | 5b821b5 | 2016-04-04 16:42:47 | [diff] [blame] | 109 | } |
| 110 | |
dcheng | 82beb4f | 2016-04-26 00:35:02 | [diff] [blame] | 111 | for (const std::unique_ptr<QuirksClient>& client : clients_) |
glevin | 5dd01a7 | 2016-03-23 23:08:12 | [diff] [blame] | 112 | client->StartDownload(); |
| 113 | } |
| 114 | |
| 115 | void QuirksManager::RequestIccProfilePath( |
| 116 | int64_t product_id, |
glevin | 2aa9dd1 | 2017-03-16 21:07:59 | [diff] [blame] | 117 | const std::string& display_name, |
glevin | 5dd01a7 | 2016-03-23 23:08:12 | [diff] [blame] | 118 | const RequestFinishedCallback& on_request_finished) { |
| 119 | DCHECK(thread_checker_.CalledOnValidThread()); |
| 120 | |
glevin | f9f834f | 2016-10-25 22:52:15 | [diff] [blame] | 121 | if (!QuirksEnabled()) { |
| 122 | VLOG(1) << "Quirks Client disabled."; |
| 123 | on_request_finished.Run(base::FilePath(), false); |
| 124 | return; |
| 125 | } |
| 126 | |
glevin | 8a081d1 | 2016-09-30 18:26:52 | [diff] [blame] | 127 | 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 | |
glevin | 5dd01a7 | 2016-03-23 23:08:12 | [diff] [blame] | 133 | std::string name = IdToFileName(product_id); |
| 134 | base::PostTaskAndReplyWithResult( |
Daniel Erat | 661b99f | 2017-07-06 15:57:59 | [diff] [blame] | 135 | task_runner_.get(), FROM_HERE, |
glevin | 5dd01a7 | 2016-03-23 23:08:12 | [diff] [blame] | 136 | base::Bind(&CheckForIccFile, |
glevin | f9f834f | 2016-10-25 22:52:15 | [diff] [blame] | 137 | delegate_->GetDisplayProfileDirectory().Append(name)), |
glevin | 5dd01a7 | 2016-03-23 23:08:12 | [diff] [blame] | 138 | base::Bind(&QuirksManager::OnIccFilePathRequestCompleted, |
glevin | 2aa9dd1 | 2017-03-16 21:07:59 | [diff] [blame] | 139 | weak_ptr_factory_.GetWeakPtr(), product_id, display_name, |
glevin | 5dd01a7 | 2016-03-23 23:08:12 | [diff] [blame] | 140 | on_request_finished)); |
| 141 | } |
| 142 | |
| 143 | void 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(), |
dcheng | 82beb4f | 2016-04-26 00:35:02 | [diff] [blame] | 147 | [client](const std::unique_ptr<QuirksClient>& c) { |
glevin | 5dd01a7 | 2016-03-23 23:08:12 | [diff] [blame] | 148 | return c.get() == client; |
| 149 | }); |
| 150 | CHECK(it != clients_.end()); |
| 151 | clients_.erase(it); |
| 152 | } |
| 153 | |
dcheng | 82beb4f | 2016-04-26 00:35:02 | [diff] [blame] | 154 | std::unique_ptr<net::URLFetcher> QuirksManager::CreateURLFetcher( |
glevin | 5dd01a7 | 2016-03-23 23:08:12 | [diff] [blame] | 155 | 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 | |
| 163 | void QuirksManager::OnIccFilePathRequestCompleted( |
| 164 | int64_t product_id, |
glevin | 2aa9dd1 | 2017-03-16 21:07:59 | [diff] [blame] | 165 | const std::string& display_name, |
glevin | 5dd01a7 | 2016-03-23 23:08:12 | [diff] [blame] | 166 | const RequestFinishedCallback& on_request_finished, |
| 167 | base::FilePath path) { |
| 168 | DCHECK(thread_checker_.CalledOnValidThread()); |
| 169 | |
glevin | f9f834f | 2016-10-25 22:52:15 | [diff] [blame] | 170 | // If we found a file, just inform requester. |
| 171 | if (!path.empty()) { |
glevin | 5dd01a7 | 2016-03-23 23:08:12 | [diff] [blame] | 172 | 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 | |
glevin | 5dd01a7 | 2016-03-23 23:08:12 | [diff] [blame] | 182 | 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 | |
glevin | 0bbe1310 | 2016-07-11 01:15:02 | [diff] [blame] | 194 | // Create and start a client to download file. |
glevin | 5dd01a7 | 2016-03-23 23:08:12 | [diff] [blame] | 195 | QuirksClient* client = |
glevin | 2aa9dd1 | 2017-03-16 21:07:59 | [diff] [blame] | 196 | new QuirksClient(product_id, display_name, on_request_finished, this); |
dcheng | 82beb4f | 2016-04-26 00:35:02 | [diff] [blame] | 197 | clients_.insert(base::WrapUnique(client)); |
glevin | 5dd01a7 | 2016-03-23 23:08:12 | [diff] [blame] | 198 | if (!waiting_for_login_) |
| 199 | client->StartDownload(); |
| 200 | else |
| 201 | VLOG(2) << "Quirks Client created; waiting for login to begin download."; |
| 202 | } |
| 203 | |
glevin | 5b821b5 | 2016-04-04 16:42:47 | [diff] [blame] | 204 | bool QuirksManager::QuirksEnabled() { |
glevin | 8c8f2d7 | 2016-04-20 13:03:28 | [diff] [blame] | 205 | if (!delegate_->DevicePolicyEnabled()) { |
| 206 | VLOG(2) << "Quirks Client disabled by device policy."; |
| 207 | return false; |
| 208 | } |
| 209 | return true; |
glevin | 5b821b5 | 2016-04-04 16:42:47 | [diff] [blame] | 210 | } |
| 211 | |
glevin | 5dd01a7 | 2016-03-23 23:08:12 | [diff] [blame] | 212 | void 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 |