| // Copyright 2021 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "android_webview/nonembedded/component_updater/aw_component_update_service.h" |
| |
| #include <memory> |
| #include <string> |
| #include <vector> |
| |
| #include "android_webview/common/aw_paths.h" |
| #include "android_webview/nonembedded/component_updater/aw_component_updater_configurator.h" |
| #include "android_webview/nonembedded/component_updater/registration.h" |
| #include "android_webview/nonembedded/nonembedded_jni_headers/AwComponentUpdateService_jni.h" |
| #include "android_webview/nonembedded/webview_apk_process.h" |
| #include "base/android/callback_android.h" |
| #include "base/android/scoped_java_ref.h" |
| #include "base/check.h" |
| #include "base/command_line.h" |
| #include "base/containers/contains.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/no_destructor.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "base/time/time.h" |
| #include "components/component_updater/component_installer.h" |
| #include "components/component_updater/component_updater_service.h" |
| #include "components/component_updater/component_updater_utils.h" |
| #include "components/update_client/crx_update_item.h" |
| #include "components/update_client/update_client.h" |
| |
| namespace android_webview { |
| |
| // static |
| AwComponentUpdateService* AwComponentUpdateService::GetInstance() { |
| static base::NoDestructor<AwComponentUpdateService> instance; |
| return instance.get(); |
| } |
| |
| // static |
| void JNI_AwComponentUpdateService_StartComponentUpdateService( |
| JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& j_finished_callback, |
| jboolean j_on_demand_update) { |
| AwComponentUpdateService::GetInstance()->StartComponentUpdateService( |
| base::BindOnce( |
| &base::android::RunIntCallbackAndroid, |
| base::android::ScopedJavaGlobalRef<jobject>(j_finished_callback)), |
| j_on_demand_update); |
| } |
| |
| AwComponentUpdateService::AwComponentUpdateService() |
| : AwComponentUpdateService(MakeAwComponentUpdaterConfigurator( |
| base::CommandLine::ForCurrentProcess(), |
| WebViewApkProcess::GetInstance()->GetPrefService())) {} |
| |
| AwComponentUpdateService::AwComponentUpdateService( |
| scoped_refptr<update_client::Configurator> configurator) |
| : update_client_(update_client::UpdateClientFactory(configurator)) {} |
| |
| AwComponentUpdateService::~AwComponentUpdateService() = default; |
| |
| // Start ComponentUpdateService once. |
| void AwComponentUpdateService::StartComponentUpdateService( |
| UpdateCallback finished_callback, |
| bool on_demand_update) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| RegisterComponents( |
| base::BindRepeating(&AwComponentUpdateService::RegisterComponent, |
| base::Unretained(this)), |
| base::BindOnce( |
| &AwComponentUpdateService::ScheduleUpdatesOfRegisteredComponents, |
| weak_ptr_factory_.GetWeakPtr(), std::move(finished_callback), |
| on_demand_update)); |
| } |
| |
| bool AwComponentUpdateService::RegisterComponent( |
| const component_updater::ComponentRegistration& component) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| // TODO(crbug.com/1180595): Add the histograms being logged in |
| // CrxUpdateService once we support logging metrics from nonembedded WebView. |
| |
| if (component.app_id.empty() || !component.version.IsValid() || |
| !component.installer) { |
| return false; |
| } |
| |
| // Update the registration data if the component has been registered before. |
| auto it = components_.find(component.app_id); |
| if (it != components_.end()) { |
| it->second = component; |
| return true; |
| } |
| |
| components_.insert(std::make_pair(component.app_id, component)); |
| components_order_.push_back(component.app_id); |
| return true; |
| } |
| |
| void AwComponentUpdateService::CheckForUpdates(UpdateCallback on_finished, |
| bool on_demand_update) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| // TODO(crbug.com/1180595): Add the histograms being logged in |
| // CrxUpdateService once we support logging metrics from nonembedded WebView. |
| |
| std::vector<std::string> secure_ids; // Require HTTPS for update checks. |
| std::vector<std::string> unsecure_ids; // Can fallback to HTTP. |
| for (const auto& id : components_order_) { |
| DCHECK(base::Contains(components_, id)); |
| |
| const auto component = component_updater::GetComponent(components_, id); |
| if (!component || component->requires_network_encryption) |
| secure_ids.push_back(id); |
| else |
| unsecure_ids.push_back(id); |
| } |
| |
| if (unsecure_ids.empty() && secure_ids.empty()) { |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(on_finished), 0)); |
| return; |
| } |
| |
| auto on_finished_callback = |
| base::BindOnce(&AwComponentUpdateService::RecordComponentsUpdated, |
| weak_ptr_factory_.GetWeakPtr(), std::move(on_finished)); |
| |
| // Reset updated components counter. |
| components_updated_count_ = 0; |
| |
| if (!unsecure_ids.empty()) { |
| update_client_->Update( |
| unsecure_ids, |
| base::BindOnce(&AwComponentUpdateService::GetCrxComponents, |
| base::Unretained(this)), |
| {}, on_demand_update, |
| base::BindOnce(&AwComponentUpdateService::OnUpdateComplete, |
| weak_ptr_factory_.GetWeakPtr(), |
| secure_ids.empty() ? std::move(on_finished_callback) |
| : update_client::Callback(), |
| base::TimeTicks::Now())); |
| } |
| |
| if (!secure_ids.empty()) { |
| update_client_->Update( |
| secure_ids, |
| base::BindOnce(&AwComponentUpdateService::GetCrxComponents, |
| base::Unretained(this)), |
| {}, on_demand_update, |
| base::BindOnce(&AwComponentUpdateService::OnUpdateComplete, |
| weak_ptr_factory_.GetWeakPtr(), |
| std::move(on_finished_callback), |
| base::TimeTicks::Now())); |
| } |
| |
| return; |
| } |
| |
| void AwComponentUpdateService::OnUpdateComplete( |
| update_client::Callback callback, |
| const base::TimeTicks& start_time, |
| update_client::Error error) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| // TODO(crbug.com/1180595): Add the histograms being logged in |
| // CrxUpdateService once we support logging metrics from nonembedded WebView. |
| |
| if (!callback.is_null()) { |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(callback), error)); |
| } |
| } |
| |
| update_client::CrxComponent AwComponentUpdateService::ToCrxComponent( |
| const component_updater::ComponentRegistration& component) const { |
| update_client::CrxComponent crx; |
| crx.pk_hash = component.public_key_hash; |
| crx.app_id = component.app_id; |
| crx.installer = component.installer; |
| crx.action_handler = component.action_handler; |
| crx.version = component.version; |
| crx.fingerprint = component.fingerprint; |
| crx.name = component.name; |
| crx.installer_attributes = component.installer_attributes; |
| crx.requires_network_encryption = component.requires_network_encryption; |
| |
| crx.crx_format_requirement = |
| crx_file::VerifierFormat::CRX3_WITH_PUBLISHER_PROOF; |
| |
| return crx; |
| } |
| |
| absl::optional<component_updater::ComponentRegistration> |
| AwComponentUpdateService::GetComponent(const std::string& id) const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return component_updater::GetComponent(components_, id); |
| } |
| |
| void AwComponentUpdateService::GetCrxComponents( |
| const std::vector<std::string>& ids, |
| base::OnceCallback< |
| void(const std::vector<absl::optional<update_client::CrxComponent>>&)> |
| callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| std::vector<absl::optional<update_client::CrxComponent>> crxs; |
| for (absl::optional<component_updater::ComponentRegistration> item : |
| component_updater::GetCrxComponents(components_, ids)) { |
| crxs.push_back( |
| item |
| ? absl::optional<update_client::CrxComponent>{ToCrxComponent(*item)} |
| : absl::nullopt); |
| } |
| std::move(callback).Run(crxs); |
| } |
| |
| void AwComponentUpdateService::ScheduleUpdatesOfRegisteredComponents( |
| UpdateCallback on_finished_updates, |
| bool on_demand_update) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| CheckForUpdates(std::move(on_finished_updates), on_demand_update); |
| } |
| |
| void AwComponentUpdateService::RegisterComponents( |
| RegisterComponentsCallback register_callback, |
| base::OnceClosure on_finished) { |
| RegisterComponentsForUpdate(register_callback, std::move(on_finished)); |
| } |
| |
| void AwComponentUpdateService::IncrementComponentsUpdatedCount() { |
| components_updated_count_++; |
| } |
| |
| void AwComponentUpdateService::RecordComponentsUpdated( |
| UpdateCallback on_finished, |
| update_client::Error error) { |
| std::move(on_finished).Run(components_updated_count_); |
| } |
| |
| } // namespace android_webview |