// Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "components/quirks/quirks_manager.h" #include #include "base/files/file_util.h" #include "base/format_macros.h" #include "base/memory/ptr_util.h" #include "base/path_service.h" #include "base/strings/stringprintf.h" #include "base/task_runner_util.h" #include "base/threading/sequenced_worker_pool.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/scoped_user_pref_update.h" #include "components/quirks/pref_names.h" #include "components/quirks/quirks_client.h" #include "net/url_request/url_fetcher.h" #include "net/url_request/url_request_context_getter.h" #include "url/gurl.h" namespace quirks { namespace { QuirksManager* manager_ = nullptr; const char kIccExtension[] = ".icc"; // How often we query Quirks Server. const int kDaysBetweenServerChecks = 30; // Check if QuirksClient has already downloaded icc file from server. base::FilePath CheckForIccFile(const base::FilePath& path) { const bool exists = base::PathExists(path); VLOG(1) << (exists ? "File" : "No File") << " found at " << path.value(); // TODO(glevin): If file exists, do we want to implement a hash to verify that // the file hasn't been corrupted or tampered with? return exists ? path : base::FilePath(); } } // namespace std::string IdToHexString(int64_t product_id) { return base::StringPrintf("%08" PRIx64, product_id); } std::string IdToFileName(int64_t product_id) { return IdToHexString(product_id).append(kIccExtension); } //////////////////////////////////////////////////////////////////////////////// // QuirksManager QuirksManager::QuirksManager( std::unique_ptr delegate, scoped_refptr blocking_pool, PrefService* local_state, scoped_refptr url_context_getter) : waiting_for_login_(true), delegate_(std::move(delegate)), blocking_pool_(blocking_pool), local_state_(local_state), url_context_getter_(url_context_getter), weak_ptr_factory_(this) {} QuirksManager::~QuirksManager() { clients_.clear(); manager_ = nullptr; } // static void QuirksManager::Initialize( std::unique_ptr delegate, scoped_refptr blocking_pool, PrefService* local_state, scoped_refptr url_context_getter) { manager_ = new QuirksManager(std::move(delegate), blocking_pool, local_state, url_context_getter); } // static void QuirksManager::Shutdown() { delete manager_; } // static QuirksManager* QuirksManager::Get() { DCHECK(manager_); return manager_; } // static void QuirksManager::RegisterPrefs(PrefRegistrySimple* registry) { registry->RegisterDictionaryPref(prefs::kQuirksClientLastServerCheck); } // Delay downloads until after login, to ensure that device policy has been set. void QuirksManager::OnLoginCompleted() { if (!waiting_for_login_) return; waiting_for_login_ = false; if (!clients_.empty() && !QuirksEnabled()) { VLOG(2) << clients_.size() << " client(s) deleted."; clients_.clear(); } for (const std::unique_ptr& client : clients_) client->StartDownload(); } void QuirksManager::RequestIccProfilePath( int64_t product_id, const std::string& display_name, const RequestFinishedCallback& on_request_finished) { DCHECK(thread_checker_.CalledOnValidThread()); if (!QuirksEnabled()) { VLOG(1) << "Quirks Client disabled."; on_request_finished.Run(base::FilePath(), false); return; } if (!product_id) { VLOG(1) << "Could not determine display information (product id = 0)"; on_request_finished.Run(base::FilePath(), false); return; } std::string name = IdToFileName(product_id); base::PostTaskAndReplyWithResult( blocking_pool_.get(), FROM_HERE, base::Bind(&CheckForIccFile, delegate_->GetDisplayProfileDirectory().Append(name)), base::Bind(&QuirksManager::OnIccFilePathRequestCompleted, weak_ptr_factory_.GetWeakPtr(), product_id, display_name, on_request_finished)); } void QuirksManager::ClientFinished(QuirksClient* client) { DCHECK(thread_checker_.CalledOnValidThread()); SetLastServerCheck(client->product_id(), base::Time::Now()); auto it = std::find_if(clients_.begin(), clients_.end(), [client](const std::unique_ptr& c) { return c.get() == client; }); CHECK(it != clients_.end()); clients_.erase(it); } std::unique_ptr QuirksManager::CreateURLFetcher( const GURL& url, net::URLFetcherDelegate* delegate) { if (!fake_quirks_fetcher_creator_.is_null()) return fake_quirks_fetcher_creator_.Run(url, delegate); return net::URLFetcher::Create(url, net::URLFetcher::GET, delegate); } void QuirksManager::OnIccFilePathRequestCompleted( int64_t product_id, const std::string& display_name, const RequestFinishedCallback& on_request_finished, base::FilePath path) { DCHECK(thread_checker_.CalledOnValidThread()); // If we found a file, just inform requester. if (!path.empty()) { on_request_finished.Run(path, false); // TODO(glevin): If Quirks files are ever modified on the server, we'll need // to modify this logic to check for updates. See crbug.com/595024. return; } double last_check = 0.0; local_state_->GetDictionary(prefs::kQuirksClientLastServerCheck) ->GetDouble(IdToHexString(product_id), &last_check); const base::TimeDelta time_since = base::Time::Now() - base::Time::FromDoubleT(last_check); // Don't need server check if we've checked within last 30 days. if (time_since < base::TimeDelta::FromDays(kDaysBetweenServerChecks)) { VLOG(2) << time_since.InDays() << " days since last Quirks Server check for display " << IdToHexString(product_id); on_request_finished.Run(base::FilePath(), false); return; } // Create and start a client to download file. QuirksClient* client = new QuirksClient(product_id, display_name, on_request_finished, this); clients_.insert(base::WrapUnique(client)); if (!waiting_for_login_) client->StartDownload(); else VLOG(2) << "Quirks Client created; waiting for login to begin download."; } bool QuirksManager::QuirksEnabled() { if (!delegate_->DevicePolicyEnabled()) { VLOG(2) << "Quirks Client disabled by device policy."; return false; } return true; } void QuirksManager::SetLastServerCheck(int64_t product_id, const base::Time& last_check) { DCHECK(thread_checker_.CalledOnValidThread()); DictionaryPrefUpdate dict(local_state_, prefs::kQuirksClientLastServerCheck); dict->SetDouble(IdToHexString(product_id), last_check.ToDoubleT()); } } // namespace quirks