| // Copyright 2020 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/updater/browser_updater_client.h" |
| |
| #include <algorithm> |
| #include <optional> |
| #include <string> |
| #include <utility> |
| |
| #include "base/functional/bind.h" |
| #include "base/functional/callback.h" |
| #include "base/logging.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/no_destructor.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/task/bind_post_task.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/task/thread_pool.h" |
| #include "base/time/time.h" |
| #include "base/version.h" |
| #include "chrome/updater/branded_constants.h" |
| #include "chrome/updater/service_proxy_factory.h" |
| #include "chrome/updater/update_service.h" |
| #include "components/version_info/version_info.h" |
| |
| namespace { |
| |
| std::optional<updater::UpdateService::AppState>* |
| GetLastKnownBrowserRegistrationStorage() { |
| static base::NoDestructor<std::optional<updater::UpdateService::AppState>> |
| storage; |
| return storage.get(); |
| } |
| |
| std::optional<updater::UpdateService::AppState>* |
| GetLastKnownUpdaterRegistrationStorage() { |
| static base::NoDestructor<std::optional<updater::UpdateService::AppState>> |
| storage; |
| return storage.get(); |
| } |
| |
| std::optional<updater::UpdateService::UpdateState>* |
| GetLastOnDemandUpdateStateStorage() { |
| static base::NoDestructor<std::optional<updater::UpdateService::UpdateState>> |
| storage; |
| return storage.get(); |
| } |
| |
| } // namespace |
| |
| BrowserUpdaterClient::BrowserUpdaterClient( |
| scoped_refptr<updater::UpdateService> update_service) |
| : update_service_(update_service) {} |
| |
| BrowserUpdaterClient::~BrowserUpdaterClient() { |
| // Weak pointers must be invalidated on the app's main sequence. |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| } |
| |
| void BrowserUpdaterClient::Register(base::OnceClosure complete) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| base::ThreadPool::PostTaskAndReplyWithResult( |
| FROM_HERE, {base::MayBlock()}, |
| base::BindOnce(&BrowserUpdaterClient::GetRegistrationRequest, this), |
| base::BindOnce( |
| [](base::OnceCallback<void(int)> callback, |
| scoped_refptr<updater::UpdateService> update_service, |
| const updater::RegistrationRequest& request) { |
| update_service->RegisterApp(request, std::move(callback)); |
| }, |
| base::BindPostTaskToCurrentDefault( |
| base::BindOnce(&BrowserUpdaterClient::RegistrationCompleted, this, |
| std::move(complete))), |
| update_service_)); |
| } |
| |
| void BrowserUpdaterClient::RegistrationCompleted(base::OnceClosure complete, |
| int result) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (result != updater::kRegistrationSuccess) { |
| VLOG(1) << "Updater registration error: " << result; |
| } |
| std::move(complete).Run(); |
| } |
| |
| void BrowserUpdaterClient::GetUpdaterVersion( |
| base::OnceCallback<void(const base::Version&)> callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| update_service_->GetVersion(base::BindPostTaskToCurrentDefault( |
| base::BindOnce(&BrowserUpdaterClient::GetUpdaterVersionCompleted, this, |
| std::move(callback)))); |
| } |
| |
| void BrowserUpdaterClient::GetUpdaterVersionCompleted( |
| base::OnceCallback<void(const base::Version&)> callback, |
| const base::Version& version) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| VLOG(1) << "Detected updater version: " << version; |
| std::move(callback).Run(version); |
| } |
| |
| void BrowserUpdaterClient::CheckForUpdate( |
| base::RepeatingCallback<void(const updater::UpdateService::UpdateState&)> |
| version_updater_callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| updater::UpdateService::UpdateState update_state; |
| update_state.state = |
| updater::UpdateService::UpdateState::State::kCheckingForUpdates; |
| version_updater_callback.Run(update_state); |
| update_service_->Update( |
| GetAppId(), {}, updater::UpdateService::Priority::kForeground, |
| updater::UpdateService::PolicySameVersionUpdate::kNotAllowed, |
| /*language=*/{}, |
| base::BindPostTaskToCurrentDefault( |
| base::BindRepeating([](const updater::UpdateService::UpdateState& |
| state) { |
| *GetLastOnDemandUpdateStateStorage() = state; |
| return state; |
| }).Then(version_updater_callback)), |
| base::BindPostTaskToCurrentDefault( |
| base::BindOnce(&BrowserUpdaterClient::UpdateCompleted, this, |
| version_updater_callback))); |
| } |
| |
| void BrowserUpdaterClient::UpdateCompleted( |
| base::RepeatingCallback<void(const updater::UpdateService::UpdateState&)> |
| callback, |
| updater::UpdateService::Result result) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| VLOG(1) << "Result of update was: " << result; |
| |
| if (result == updater::UpdateService::Result::kSuccess || |
| result == updater::UpdateService::Result::kUpdateCheckFailed) { |
| // These statuses will have sent more descriptive information in the status |
| // callback, don't overwrite it. |
| return; |
| } |
| updater::UpdateService::UpdateState update_state; |
| update_state.state = updater::UpdateService::UpdateState::State::kUpdateError; |
| update_state.error_category = |
| updater::UpdateService::ErrorCategory::kUpdateCheck; |
| update_state.error_code = static_cast<int>(result); |
| callback.Run(update_state); |
| } |
| |
| void BrowserUpdaterClient::RunPeriodicTasks(base::OnceClosure callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| update_service_->RunPeriodicTasks(base::BindPostTaskToCurrentDefault( |
| base::BindOnce(&BrowserUpdaterClient::RunPeriodicTasksCompleted, this, |
| std::move(callback)))); |
| } |
| |
| void BrowserUpdaterClient::RunPeriodicTasksCompleted( |
| base::OnceClosure callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| std::move(callback).Run(); |
| } |
| |
| void BrowserUpdaterClient::IsBrowserRegistered( |
| base::OnceCallback<void(bool)> callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| update_service_->GetAppStates(base::BindPostTaskToCurrentDefault( |
| base::BindOnce(&BrowserUpdaterClient::IsBrowserRegisteredCompleted, this, |
| std::move(callback)))); |
| } |
| |
| void BrowserUpdaterClient::IsBrowserRegisteredCompleted( |
| base::OnceCallback<void(bool)> callback, |
| const std::vector<updater::UpdateService::AppState>& apps) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| const auto updater = std::ranges::find_if( |
| apps, [](const updater::UpdateService::AppState& state) { |
| return base::EqualsCaseInsensitiveASCII(state.app_id, |
| updater::kUpdaterAppId); |
| }); |
| if (updater != apps.end()) { |
| *GetLastKnownUpdaterRegistrationStorage() = *updater; |
| } |
| const auto app = |
| std::ranges::find_if(apps, &BrowserUpdaterClient::AppMatches); |
| if (app != apps.end()) { |
| *GetLastKnownBrowserRegistrationStorage() = *app; |
| } |
| std::move(callback).Run(app != apps.end()); |
| } |
| |
| // User and System BrowserUpdaterClients must be kept separate - the template |
| // function causes there to be two static variables instead of one. |
| template <updater::UpdaterScope scope> |
| scoped_refptr<BrowserUpdaterClient> BrowserUpdaterClient::GetClient( |
| base::RepeatingCallback<scoped_refptr<updater::UpdateService>()> |
| proxy_provider) { |
| // Multiple UpdateServiceProxies interfere with each other. Reuse a current |
| // BrowserUpdaterClient if possible. BrowserUpdaterClients are refcounted, but |
| // bad to keep around indefinitely since they can hold the RPC server open. |
| // Using a static NoDestruct weak pointer keeps the objects' lifetime |
| // controlled by the refcounting, but allows the function to reuse them when |
| // they're alive. |
| struct WeakPointerHolder { |
| base::WeakPtr<BrowserUpdaterClient> client; |
| }; |
| |
| static base::NoDestructor<WeakPointerHolder> existing; |
| if (existing->client) { |
| return base::WrapRefCounted(existing->client.get()); |
| } |
| |
| // Else, make a new one: |
| auto new_client = |
| base::MakeRefCounted<BrowserUpdaterClient>(proxy_provider.Run()); |
| existing->client = new_client->weak_ptr_factory_.GetWeakPtr(); |
| return new_client; |
| } |
| |
| scoped_refptr<BrowserUpdaterClient> BrowserUpdaterClient::Create( |
| updater::UpdaterScope scope) { |
| return Create(base::BindRepeating(&updater::CreateUpdateServiceProxy, scope, |
| base::Seconds(15)), |
| scope); |
| } |
| |
| scoped_refptr<BrowserUpdaterClient> BrowserUpdaterClient::Create( |
| base::RepeatingCallback<scoped_refptr<updater::UpdateService>()> |
| proxy_provider, |
| updater::UpdaterScope scope) { |
| return scope == updater::UpdaterScope::kSystem |
| ? GetClient<updater::UpdaterScope::kSystem>(proxy_provider) |
| : GetClient<updater::UpdaterScope::kUser>(proxy_provider); |
| } |
| |
| std::optional<updater::UpdateService::UpdateState> |
| BrowserUpdaterClient::GetLastOnDemandUpdateState() { |
| return *GetLastOnDemandUpdateStateStorage(); |
| } |
| |
| std::optional<updater::UpdateService::AppState> |
| BrowserUpdaterClient::GetLastKnownBrowserRegistration() { |
| return *GetLastKnownBrowserRegistrationStorage(); |
| } |
| |
| std::optional<updater::UpdateService::AppState> |
| BrowserUpdaterClient::GetLastKnownUpdaterRegistration() { |
| return *GetLastKnownUpdaterRegistrationStorage(); |
| } |