blob: 0e427f29297f212d03374acbd2ad07fbdb8f56cb [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 "components/soda/soda_installer_impl_chromeos.h"
#include <string>
#include "base/bind.h"
#include "base/containers/contains.h"
#include "base/feature_list.h"
#include "base/metrics/histogram_functions.h"
#include "base/numerics/safe_conversions.h"
#include "chromeos/ash/components/dbus/dlcservice/dlcservice.pb.h"
#include "chromeos/ash/components/dbus/dlcservice/dlcservice_client.h"
#include "components/live_caption/pref_names.h"
#include "components/prefs/pref_service.h"
#include "components/soda/pref_names.h"
#include "components/soda/soda_installer.h"
#include "media/base/media_switches.h"
#include "ui/base/l10n/l10n_util.h"
namespace speech {
namespace {
constexpr char kSodaDlcName[] = "libsoda";
constexpr char kSodaEnglishUsDlcName[] = "libsoda-model-en-us";
SodaInstaller::ErrorCode DlcCodeToSodaErrorCode(const std::string& code) {
return (code == dlcservice::kErrorNeedReboot)
? SodaInstaller::ErrorCode::kNeedsReboot
: SodaInstaller::ErrorCode::kUnspecifiedError;
}
} // namespace
SodaInstallerImplChromeOS::SodaInstallerImplChromeOS() = default;
SodaInstallerImplChromeOS::~SodaInstallerImplChromeOS() = default;
base::FilePath SodaInstallerImplChromeOS::GetSodaBinaryPath() const {
return soda_lib_path_;
}
base::FilePath SodaInstallerImplChromeOS::GetLanguagePath(
const std::string& language) const {
// TODO(crbug.com/1161569): Support multiple languages. Currently only en-US
// is supported.
DCHECK_EQ(language, kUsEnglishLocale);
return language_path_;
}
void SodaInstallerImplChromeOS::InstallSoda(PrefService* global_prefs) {
if (never_download_soda_for_testing_)
return;
// Clear cached path in case this is a reinstallation (path could
// change).
SetSodaBinaryPath(base::FilePath());
soda_binary_installed_ = false;
is_soda_downloading_ = true;
soda_progress_ = 0.0;
// Install SODA DLC.
dlcservice::InstallRequest install_request;
install_request.set_id(kSodaDlcName);
ash::DlcserviceClient::Get()->Install(
install_request,
base::BindOnce(&SodaInstallerImplChromeOS::OnSodaInstalled,
base::Unretained(this), base::Time::Now()),
base::BindRepeating(&SodaInstallerImplChromeOS::OnSodaProgress,
base::Unretained(this)));
}
void SodaInstallerImplChromeOS::InstallLanguage(const std::string& language,
PrefService* global_prefs) {
if (never_download_soda_for_testing_)
return;
// TODO(crbug.com/1161569): SODA is only available for en-US right now.
DCHECK_EQ(language, kUsEnglishLocale);
SodaInstaller::RegisterLanguage(language, global_prefs);
// Clear cached path in case this is a reinstallation (path could
// change).
SetLanguagePath(base::FilePath());
// TODO(crbug.com/1055150): Compare user's language to a list of
// supported languages and map it to a DLC ID. For now, default to
// installing the SODA English-US DLC.
DCHECK_EQ(language, kUsEnglishLocale);
language_pack_progress_.insert({LanguageCode::kEnUs, 0.0});
dlcservice::InstallRequest install_request;
install_request.set_id(kSodaEnglishUsDlcName);
ash::DlcserviceClient::Get()->Install(
install_request,
base::BindOnce(&SodaInstallerImplChromeOS::OnLanguageInstalled,
base::Unretained(this), LanguageCode::kEnUs,
base::Time::Now()),
base::BindRepeating(&SodaInstallerImplChromeOS::OnLanguageProgress,
base::Unretained(this)));
}
std::vector<std::string> SodaInstallerImplChromeOS::GetAvailableLanguages()
const {
// TODO(crbug.com/1161569): SODA is only available for English right now.
// Update this to check available languages.
return {kUsEnglishLocale};
}
void SodaInstallerImplChromeOS::UninstallSoda(PrefService* global_prefs) {
soda_binary_installed_ = false;
SetSodaBinaryPath(base::FilePath());
ash::DlcserviceClient::Get()->Uninstall(
kSodaDlcName, base::BindOnce(&SodaInstallerImplChromeOS::OnDlcUninstalled,
base::Unretained(this), kSodaDlcName));
installed_languages_.clear();
language_pack_progress_.clear();
SodaInstaller::UnregisterLanguages(global_prefs);
SetLanguagePath(base::FilePath());
ash::DlcserviceClient::Get()->Uninstall(
kSodaEnglishUsDlcName,
base::BindOnce(&SodaInstallerImplChromeOS::OnDlcUninstalled,
base::Unretained(this), kSodaEnglishUsDlcName));
global_prefs->SetTime(prefs::kSodaScheduledDeletionTime, base::Time());
}
void SodaInstallerImplChromeOS::SetSodaBinaryPath(base::FilePath new_path) {
soda_lib_path_ = new_path;
}
void SodaInstallerImplChromeOS::SetLanguagePath(base::FilePath new_path) {
language_path_ = new_path;
}
void SodaInstallerImplChromeOS::OnSodaInstalled(
const base::Time start_time,
const ash::DlcserviceClient::InstallResult& install_result) {
is_soda_downloading_ = false;
if (install_result.error == dlcservice::kErrorNone) {
soda_binary_installed_ = true;
SetSodaBinaryPath(base::FilePath(install_result.root_path));
// TODO(crbug.com/1161569): SODA is only available for English right now.
// Update this to notify on all installed languages.
if (IsLanguageInstalled(LanguageCode::kEnUs))
NotifyOnSodaInstalled(LanguageCode::kEnUs);
base::UmaHistogramTimes(kSodaBinaryInstallationSuccessTimeTaken,
base::Time::Now() - start_time);
} else {
soda_binary_installed_ = false;
soda_progress_ = 0.0;
NotifyOnSodaInstallError(LanguageCode::kNone,
DlcCodeToSodaErrorCode(install_result.error));
base::UmaHistogramTimes(kSodaBinaryInstallationFailureTimeTaken,
base::Time::Now() - start_time);
}
base::UmaHistogramBoolean(kSodaBinaryInstallationResult,
install_result.error == dlcservice::kErrorNone);
}
void SodaInstallerImplChromeOS::OnLanguageInstalled(
const LanguageCode language_code,
const base::Time start_time,
const ash::DlcserviceClient::InstallResult& install_result) {
language_pack_progress_.erase(language_code);
if (install_result.error == dlcservice::kErrorNone) {
installed_languages_.insert(language_code);
SetLanguagePath(base::FilePath(install_result.root_path));
if (soda_binary_installed_) {
NotifyOnSodaInstalled(language_code);
}
base::UmaHistogramTimes(
GetInstallationSuccessTimeMetricForLanguagePack(language_code),
base::Time::Now() - start_time);
} else {
// TODO: Notify the observer of the specific language pack that failed
// to install. ChromeOS currently only supports the en-US language pack.
NotifyOnSodaInstallError(language_code,
DlcCodeToSodaErrorCode(install_result.error));
base::UmaHistogramTimes(
GetInstallationFailureTimeMetricForLanguagePack(language_code),
base::Time::Now() - start_time);
}
base::UmaHistogramBoolean(
GetInstallationResultMetricForLanguagePack(language_code),
install_result.error == dlcservice::kErrorNone);
}
void SodaInstallerImplChromeOS::OnSodaProgress(double progress) {
soda_progress_ = progress;
OnSodaCombinedProgress();
}
void SodaInstallerImplChromeOS::OnLanguageProgress(double progress) {
language_pack_progress_[LanguageCode::kEnUs] = progress;
OnSodaCombinedProgress();
}
void SodaInstallerImplChromeOS::OnSodaCombinedProgress() {
// TODO(crbug.com/1055150): Consider updating this implementation.
// e.g.: (1) starting progress from 0% if we are downloading language
// only (2) weighting download progress proportionally to DLC binary size.
double language_progress = 0.0;
if (base::Contains(language_pack_progress_, LanguageCode::kEnUs))
language_progress = language_pack_progress_[LanguageCode::kEnUs];
const double progress = (soda_progress_ + language_progress) / 2;
// TODO: Notify the observer of the specific language pack that is currently
// being installed. ChromeOS currently only supports the en-US language pack.
NotifyOnSodaProgress(LanguageCode::kEnUs, base::ClampFloor(100 * progress));
}
void SodaInstallerImplChromeOS::OnDlcUninstalled(const std::string& dlc_id,
const std::string& err) {
if (err != dlcservice::kErrorNone) {
LOG(ERROR) << "Failed to uninstall DLC " << dlc_id << ". Error: " << err;
}
}
} // namespace speech