blob: 96baee034a2ba2fe7709bbed81e4778597bf8aab [file] [log] [blame]
// 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/accessibility/soda_installer_impl.h"
#include <algorithm>
#include <map>
#include <string>
#include <vector>
#include "base/check_op.h"
#include "base/containers/contains.h"
#include "base/containers/flat_set.h"
#include "base/feature_list.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/metrics/histogram_functions.h"
#include "base/notreached.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/component_updater/soda_component_installer.h"
#include "chrome/browser/component_updater/soda_language_pack_component_installer.h"
#include "components/prefs/pref_service.h"
#include "components/soda/constants.h"
#include "components/update_client/crx_update_item.h"
#include "media/base/media_switches.h"
#include "ui/base/l10n/l10n_util.h"
namespace speech {
SodaInstallerImpl::SodaInstallerImpl() = default;
SodaInstallerImpl::~SodaInstallerImpl() {
component_updater_observation_.Reset();
}
base::FilePath SodaInstallerImpl::GetSodaBinaryPath() const {
DLOG(FATAL) << "GetSodaBinaryPath not supported on this platform";
return base::FilePath();
}
base::FilePath SodaInstallerImpl::GetLanguagePath(
const std::string& language) const {
DLOG(FATAL) << "GetLanguagePath not supported on this platform";
return base::FilePath();
}
void SodaInstallerImpl::InstallSoda(PrefService* global_prefs) {
if (never_download_soda_for_testing_)
return;
soda_binary_installed_ = false;
is_soda_downloading_ = true;
component_updater::RegisterSodaComponent(
g_browser_process->component_updater(), global_prefs,
base::BindOnce(&SodaInstallerImpl::OnSodaBinaryInstalled,
weak_factory_.GetWeakPtr()),
base::BindOnce(&component_updater::SodaComponentInstallerPolicy::
UpdateSodaComponentOnDemand));
soda_binary_install_start_time_ = base::Time::Now();
if (!component_updater_observation_.IsObservingSource(
g_browser_process->component_updater())) {
component_updater_observation_.Observe(
g_browser_process->component_updater());
}
}
void SodaInstallerImpl::InstallLanguage(const std::string& language,
PrefService* global_prefs) {
if (never_download_soda_for_testing_)
return;
speech::LanguageCode locale = speech::GetLanguageCode(language);
language_pack_progress_.insert({locale, 0.0});
SodaInstaller::RegisterLanguage(language, global_prefs);
component_updater::RegisterSodaLanguageComponent(
g_browser_process->component_updater(), language, global_prefs,
base::BindOnce(&SodaInstallerImpl::OnSodaLanguagePackInstalled,
weak_factory_.GetWeakPtr()));
language_pack_install_start_time_[locale] = base::Time::Now();
if (!component_updater_observation_.IsObservingSource(
g_browser_process->component_updater())) {
component_updater_observation_.Observe(
g_browser_process->component_updater());
}
}
void SodaInstallerImpl::UninstallLanguage(const std::string& language,
PrefService* global_prefs) {
speech::LanguageCode language_code = speech::GetLanguageCode(language);
if (language_code != speech::LanguageCode::kNone) {
// Remove the language from the preference tracking installed language packs
// and unregister the corresponding component from the component updater
// service to remove the files and prevent future updates.
SodaInstaller::UnregisterLanguage(language, global_prefs);
const std::string crx_id = component_updater::
SodaLanguagePackComponentInstallerPolicy::GetExtensionId(language_code);
auto* component_updater_service = g_browser_process->component_updater();
if (component_updater_service) {
component_updater_service->UnregisterComponent(crx_id);
}
std::set<speech::LanguageCode>::iterator it =
installed_languages_.find(language_code);
if (it != installed_languages_.end()) {
installed_languages_.erase(it);
}
}
}
std::vector<std::string> SodaInstallerImpl::GetAvailableLanguages() const {
// TODO(crbug.com/40162502): SODA is only available for English right now.
// Update this to check available languages.
return {kUsEnglishLocale};
}
void SodaInstallerImpl::UninstallSoda(PrefService* global_prefs) {
SodaInstaller::UnregisterLanguages(global_prefs);
base::DeletePathRecursively(speech::GetSodaDirectory());
base::DeletePathRecursively(speech::GetSodaLanguagePacksDirectory());
global_prefs->SetTime(prefs::kSodaScheduledDeletionTime, base::Time());
soda_binary_installed_ = false;
is_soda_downloading_ = false;
soda_installer_initialized_ = false;
installed_languages_.clear();
language_pack_progress_.clear();
}
void SodaInstallerImpl::OnEvent(Events event, const std::string& id) {
if (!component_updater::SodaLanguagePackComponentInstallerPolicy::
GetExtensionIds()
.contains(id) &&
id != component_updater::SodaComponentInstallerPolicy::GetExtensionId()) {
return;
}
LanguageCode language_code = LanguageCode::kNone;
if (id != component_updater::SodaComponentInstallerPolicy::GetExtensionId()) {
language_code = GetLanguageCodeByComponentId(id);
DCHECK_NE(language_code, LanguageCode::kNone);
}
switch (event) {
case Events::COMPONENT_UPDATE_FOUND:
case Events::COMPONENT_UPDATE_READY:
case Events::COMPONENT_WAIT:
case Events::COMPONENT_UPDATE_DOWNLOADING:
case Events::COMPONENT_UPDATE_UPDATING: {
update_client::CrxUpdateItem item;
g_browser_process->component_updater()->GetComponentDetails(id, &item);
downloading_components_[language_code] = item;
if (language_code == LanguageCode::kNone &&
!language_pack_progress_.empty()) {
for (auto language : language_pack_progress_) {
UpdateAndNotifyOnSodaProgress(language.first);
}
} else {
UpdateAndNotifyOnSodaProgress(language_code);
}
} break;
case Events::COMPONENT_UPDATE_ERROR:
is_soda_downloading_ = false;
if (language_code != LanguageCode::kNone) {
language_pack_progress_.erase(language_code);
base::UmaHistogramTimes(
GetInstallationFailureTimeMetricForLanguagePack(language_code),
base::Time::Now() -
language_pack_install_start_time_[language_code]);
base::UmaHistogramBoolean(
GetInstallationResultMetricForLanguagePack(language_code), false);
} else {
base::UmaHistogramTimes(
kSodaBinaryInstallationFailureTimeTaken,
base::Time::Now() - soda_binary_install_start_time_);
base::UmaHistogramBoolean(kSodaBinaryInstallationResult, false);
}
NotifyOnSodaInstallError(
language_code, speech::SodaInstaller::ErrorCode::kUnspecifiedError);
break;
case Events::COMPONENT_CHECKING_FOR_UPDATES:
case Events::COMPONENT_UPDATED:
case Events::COMPONENT_ALREADY_UP_TO_DATE:
// Do nothing.
break;
}
}
void SodaInstallerImpl::OnSodaBinaryInstalled() {
soda_binary_installed_ = true;
is_soda_downloading_ = false;
for (LanguageCode language : installed_languages_) {
NotifyOnSodaInstalled(language);
}
base::UmaHistogramTimes(kSodaBinaryInstallationSuccessTimeTaken,
base::Time::Now() - soda_binary_install_start_time_);
base::UmaHistogramBoolean(kSodaBinaryInstallationResult, true);
}
void SodaInstallerImpl::OnSodaLanguagePackInstalled(
speech::LanguageCode language_code) {
installed_languages_.insert(language_code);
language_pack_progress_.erase(language_code);
if (soda_binary_installed_) {
NotifyOnSodaInstalled(language_code);
}
base::UmaHistogramTimes(
GetInstallationSuccessTimeMetricForLanguagePack(language_code),
base::Time::Now() - language_pack_install_start_time_[language_code]);
base::UmaHistogramBoolean(
GetInstallationResultMetricForLanguagePack(language_code), true);
}
void SodaInstallerImpl::UpdateAndNotifyOnSodaProgress(
speech::LanguageCode language_code) {
int total_bytes = 0;
int downloaded_bytes = 0;
speech::LanguageCode soda_code = speech::LanguageCode::kNone;
if (base::Contains(downloading_components_, soda_code)) {
total_bytes += downloading_components_[soda_code].total_bytes;
downloaded_bytes += downloading_components_[soda_code].downloaded_bytes;
}
if (language_code != soda_code) {
total_bytes += downloading_components_[language_code].total_bytes;
downloaded_bytes += downloading_components_[language_code].downloaded_bytes;
}
if (total_bytes == 0)
return;
DCHECK_LE(downloaded_bytes, total_bytes);
int progress =
100 * std::clamp(static_cast<double>(downloaded_bytes) / total_bytes,
0.0, 1.0);
if (language_code != soda_code)
language_pack_progress_[language_code] = progress;
NotifyOnSodaProgress(language_code, progress);
}
} // namespace speech