| // Copyright 2015 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/extensions/updater/chrome_update_client_config.h" |
| |
| #include <algorithm> |
| #include <set> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/command_line.h" |
| #include "base/containers/flat_map.h" |
| #include "base/files/file_path.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/no_destructor.h" |
| #include "base/path_service.h" |
| #include "base/scoped_observation.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/time/time.h" |
| #include "base/version.h" |
| #include "chrome/browser/component_updater/component_updater_utils.h" |
| #include "chrome/browser/extensions/updater/extension_update_client_command_line_config_policy.h" |
| #include "chrome/browser/extensions/updater/extension_updater_switches.h" |
| #include "chrome/browser/google/google_brand.h" |
| #include "chrome/browser/update_client/chrome_update_query_params_delegate.h" |
| #include "chrome/common/channel_info.h" |
| #include "chrome/common/chrome_paths.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/services/patch/content/patch_service.h" |
| #include "components/services/unzip/content/unzip_service.h" |
| #include "components/update_client/activity_data_service.h" |
| #include "components/update_client/crx_cache.h" |
| #include "components/update_client/crx_downloader_factory.h" |
| #include "components/update_client/net/network_chromium.h" |
| #include "components/update_client/patch/patch_impl.h" |
| #include "components/update_client/patcher.h" |
| #include "components/update_client/persisted_data.h" |
| #include "components/update_client/protocol_handler.h" |
| #include "components/update_client/unzip/unzip_impl.h" |
| #include "components/update_client/unzipper.h" |
| #include "components/update_client/update_query_params.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/storage_partition.h" |
| #include "extensions/browser/extension_prefs.h" |
| #include "extensions/browser/extension_prefs_observer.h" |
| |
| namespace extensions { |
| |
| namespace { |
| |
| using FactoryCallback = ChromeUpdateClientConfig::FactoryCallback; |
| |
| // static |
| static FactoryCallback& GetFactoryCallback() { |
| static base::NoDestructor<FactoryCallback> factory; |
| return *factory; |
| } |
| |
| class ExtensionActivityDataService final |
| : public update_client::ActivityDataService, |
| public ExtensionPrefsObserver { |
| public: |
| explicit ExtensionActivityDataService(ExtensionPrefs* extension_prefs); |
| |
| ExtensionActivityDataService(const ExtensionActivityDataService&) = delete; |
| ExtensionActivityDataService& operator=(const ExtensionActivityDataService&) = |
| delete; |
| |
| ~ExtensionActivityDataService() override = default; |
| |
| // update_client::ActivityDataService: |
| void GetActiveBits(const std::vector<std::string>& ids, |
| base::OnceCallback<void(const std::set<std::string>&)> |
| callback) const override; |
| void GetAndClearActiveBits( |
| const std::vector<std::string>& ids, |
| base::OnceCallback<void(const std::set<std::string>&)> callback) override; |
| int GetDaysSinceLastActive(const std::string& id) const override; |
| int GetDaysSinceLastRollCall(const std::string& id) const override; |
| |
| // ExtensionPrefsObserver: |
| void OnExtensionPrefsWillBeDestroyed(ExtensionPrefs* prefs) override; |
| |
| private: |
| // This member is not owned by this class, it's owned by a profile keyed |
| // service. |
| raw_ptr<ExtensionPrefs> extension_prefs_; |
| |
| base::ScopedObservation<ExtensionPrefs, ExtensionPrefsObserver> |
| prefs_observation_{this}; |
| }; |
| |
| // Calculates the value to use for the ping days parameter. |
| int CalculatePingDays(const base::Time& last_ping_day) { |
| return last_ping_day.is_null() |
| ? update_client::kDaysFirstTime |
| : std::max((base::Time::Now() - last_ping_day).InDays(), 0); |
| } |
| |
| PrefService* GetPrefService(base::WeakPtr<content::BrowserContext> context) { |
| return context ? ExtensionPrefs::Get(context.get())->pref_service() : nullptr; |
| } |
| |
| ExtensionActivityDataService::ExtensionActivityDataService( |
| ExtensionPrefs* extension_prefs) |
| : extension_prefs_(extension_prefs) { |
| DCHECK(extension_prefs_); |
| |
| prefs_observation_.Observe(extension_prefs); |
| } |
| |
| void ExtensionActivityDataService::GetActiveBits( |
| const std::vector<std::string>& ids, |
| base::OnceCallback<void(const std::set<std::string>&)> callback) const { |
| std::set<std::string> actives; |
| if (extension_prefs_) { |
| for (const auto& id : ids) { |
| if (extension_prefs_->GetActiveBit(id)) { |
| actives.insert(id); |
| } |
| } |
| } |
| base::SequencedTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(callback), actives)); |
| } |
| |
| int ExtensionActivityDataService::GetDaysSinceLastActive( |
| const std::string& id) const { |
| if (!extension_prefs_) { |
| return update_client::kDaysUnknown; |
| } |
| return CalculatePingDays(extension_prefs_->LastActivePingDay(id)); |
| } |
| |
| int ExtensionActivityDataService::GetDaysSinceLastRollCall( |
| const std::string& id) const { |
| if (!extension_prefs_) { |
| return update_client::kDaysUnknown; |
| } |
| return CalculatePingDays(extension_prefs_->LastPingDay(id)); |
| } |
| |
| void ExtensionActivityDataService::GetAndClearActiveBits( |
| const std::vector<std::string>& ids, |
| base::OnceCallback<void(const std::set<std::string>&)> callback) { |
| std::set<std::string> actives; |
| if (extension_prefs_) { |
| for (const auto& id : ids) { |
| if (extension_prefs_->GetActiveBit(id)) { |
| actives.insert(id); |
| } |
| extension_prefs_->SetActiveBit(id, false); |
| } |
| } |
| base::SequencedTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(callback), actives)); |
| } |
| |
| void ExtensionActivityDataService::OnExtensionPrefsWillBeDestroyed( |
| ExtensionPrefs* prefs) { |
| DCHECK(prefs_observation_.IsObservingSource(prefs)); |
| prefs_observation_.Reset(); |
| extension_prefs_ = nullptr; |
| } |
| |
| } // namespace |
| |
| // For privacy reasons, requires encryption of the component updater |
| // communication with the update backend. |
| ChromeUpdateClientConfig::ChromeUpdateClientConfig( |
| content::BrowserContext* context, |
| std::optional<GURL> url_override) |
| : context_(context->GetWeakPtr()), |
| impl_(ExtensionUpdateClientCommandLineConfigPolicy( |
| base::CommandLine::ForCurrentProcess()), |
| /*require_encryption=*/true), |
| persisted_data_(update_client::CreatePersistedData( |
| base::BindRepeating(&extensions::GetPrefService, context_), |
| std::make_unique<ExtensionActivityDataService>( |
| ExtensionPrefs::Get(context)))), |
| url_override_(url_override) { |
| base::FilePath path; |
| bool result = base::PathService::Get(chrome::DIR_USER_DATA, &path); |
| crx_cache_ = base::MakeRefCounted<update_client::CrxCache>( |
| result ? std::optional<base::FilePath>( |
| path.AppendASCII("extensions_crx_cache")) |
| : std::nullopt); |
| } |
| |
| ChromeUpdateClientConfig::~ChromeUpdateClientConfig() = default; |
| |
| base::TimeDelta ChromeUpdateClientConfig::InitialDelay() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return impl_.InitialDelay(); |
| } |
| |
| base::TimeDelta ChromeUpdateClientConfig::NextCheckDelay() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return impl_.NextCheckDelay(); |
| } |
| |
| base::TimeDelta ChromeUpdateClientConfig::OnDemandDelay() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return impl_.OnDemandDelay(); |
| } |
| |
| base::TimeDelta ChromeUpdateClientConfig::UpdateDelay() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return impl_.UpdateDelay(); |
| } |
| |
| std::vector<GURL> ChromeUpdateClientConfig::UpdateUrl() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (url_override_.has_value()) { |
| return {*url_override_}; |
| } |
| return impl_.UpdateUrl(); |
| } |
| |
| std::vector<GURL> ChromeUpdateClientConfig::PingUrl() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (url_override_.has_value()) { |
| return {*url_override_}; |
| } |
| return impl_.PingUrl(); |
| } |
| |
| std::string ChromeUpdateClientConfig::GetProdId() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return update_client::UpdateQueryParams::GetProdIdString( |
| update_client::UpdateQueryParams::ProdId::CRX); |
| } |
| |
| base::Version ChromeUpdateClientConfig::GetBrowserVersion() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return impl_.GetBrowserVersion(); |
| } |
| |
| std::string ChromeUpdateClientConfig::GetChannel() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return GetChannelForExtensionUpdates(); |
| } |
| |
| std::string ChromeUpdateClientConfig::GetLang() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return ChromeUpdateQueryParamsDelegate::GetLang(); |
| } |
| |
| std::string ChromeUpdateClientConfig::GetOSLongName() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return impl_.GetOSLongName(); |
| } |
| |
| base::flat_map<std::string, std::string> |
| ChromeUpdateClientConfig::ExtraRequestParams() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return impl_.ExtraRequestParams(); |
| } |
| |
| std::string ChromeUpdateClientConfig::GetDownloadPreference() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return std::string(); |
| } |
| |
| scoped_refptr<update_client::NetworkFetcherFactory> |
| ChromeUpdateClientConfig::GetNetworkFetcherFactory() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (!network_fetcher_factory_) { |
| network_fetcher_factory_ = |
| base::MakeRefCounted<update_client::NetworkFetcherChromiumFactory>( |
| context_->GetDefaultStoragePartition() |
| ->GetURLLoaderFactoryForBrowserProcess(), |
| // Only extension updates that require authentication are served |
| // from chrome.google.com, so send cookies if and only if that is |
| // the download domain. |
| base::BindRepeating([](const GURL& url) { |
| return url.DomainIs("chrome.google.com"); |
| })); |
| } |
| return network_fetcher_factory_; |
| } |
| |
| scoped_refptr<update_client::CrxDownloaderFactory> |
| ChromeUpdateClientConfig::GetCrxDownloaderFactory() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (!crx_downloader_factory_) { |
| crx_downloader_factory_ = |
| update_client::MakeCrxDownloaderFactory(GetNetworkFetcherFactory()); |
| } |
| return crx_downloader_factory_; |
| } |
| |
| scoped_refptr<update_client::UnzipperFactory> |
| ChromeUpdateClientConfig::GetUnzipperFactory() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| if (!unzip_factory_) { |
| unzip_factory_ = base::MakeRefCounted<update_client::UnzipChromiumFactory>( |
| base::BindRepeating(&unzip::LaunchUnzipper)); |
| } |
| return unzip_factory_; |
| } |
| |
| scoped_refptr<update_client::PatcherFactory> |
| ChromeUpdateClientConfig::GetPatcherFactory() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| if (!patch_factory_) { |
| patch_factory_ = base::MakeRefCounted<update_client::PatchChromiumFactory>( |
| base::BindRepeating(&patch::LaunchFilePatcher)); |
| } |
| return patch_factory_; |
| } |
| |
| bool ChromeUpdateClientConfig::EnabledBackgroundDownloader() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| // Historically, Chrome hasn't used background downloaders like BITS for |
| // extension updates. In theory, they should work in most cases. When they |
| // don't (for example because they don't pass the credentials necessary to |
| // download private extensions), the system should fall back to the foreground |
| // downloader. So, returning `true` here is probably safe, but should likely |
| // be done as a Finch experiment that is carefully monitored. |
| return false; |
| } |
| |
| bool ChromeUpdateClientConfig::EnabledCupSigning() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (url_override_.has_value()) { |
| return false; |
| } |
| return impl_.EnabledCupSigning(); |
| } |
| |
| PrefService* ChromeUpdateClientConfig::GetPrefService() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return extensions::GetPrefService(context_); |
| } |
| |
| update_client::PersistedData* ChromeUpdateClientConfig::GetPersistedData() |
| const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return persisted_data_.get(); |
| } |
| |
| bool ChromeUpdateClientConfig::IsPerUserInstall() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return component_updater::IsPerUserInstall(); |
| } |
| |
| std::unique_ptr<update_client::ProtocolHandlerFactory> |
| ChromeUpdateClientConfig::GetProtocolHandlerFactory() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return impl_.GetProtocolHandlerFactory(); |
| } |
| |
| std::optional<bool> ChromeUpdateClientConfig::IsMachineExternallyManaged() |
| const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return impl_.IsMachineExternallyManaged(); |
| } |
| |
| update_client::UpdaterStateProvider |
| ChromeUpdateClientConfig::GetUpdaterStateProvider() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return impl_.GetUpdaterStateProvider(); |
| } |
| |
| // static |
| scoped_refptr<ChromeUpdateClientConfig> ChromeUpdateClientConfig::Create( |
| content::BrowserContext* context, |
| std::optional<GURL> update_url_override) { |
| FactoryCallback& factory = GetFactoryCallback(); |
| return factory.is_null() ? base::MakeRefCounted<ChromeUpdateClientConfig>( |
| context, update_url_override) |
| : factory.Run(context); |
| } |
| |
| // static |
| void ChromeUpdateClientConfig::SetChromeUpdateClientConfigFactoryForTesting( |
| FactoryCallback factory) { |
| DCHECK(!factory.is_null()); |
| GetFactoryCallback() = factory; |
| } |
| |
| scoped_refptr<update_client::CrxCache> ChromeUpdateClientConfig::GetCrxCache() |
| const { |
| return crx_cache_; |
| } |
| |
| bool ChromeUpdateClientConfig::IsConnectionMetered() const { |
| return impl_.IsConnectionMetered(); |
| } |
| |
| } // namespace extensions |