blob: 8c2b79f3e2ebd285b806b6c8c7495ad910a33be9 [file] [log] [blame]
// Copyright 2024 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/speech/on_device_speech_recognition_impl.h"
#include "base/strings/string_util.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#include "components/language/core/browser/language_prefs.h"
#include "components/language/core/browser/pref_names.h"
#include "components/language/core/common/locale_util.h"
#include "components/prefs/pref_service.h"
#include "components/soda/constants.h"
#include "components/soda/pref_names.h"
#include "components/soda/soda_installer.h"
#include "content/public/browser/document_user_data.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "media/mojo/mojom/speech_recognizer.mojom.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "net/base/network_change_notifier.h"
#include "ui/base/l10n/l10n_util.h"
#if !BUILDFLAG(IS_ANDROID)
#include "components/soda/soda_util.h"
namespace {
// Returns a boolean indicating whether the language is both enabled and not
// already installed.
bool IsLanguageInstallable(const std::string& language_code,
bool is_soda_binary_installed) {
if (is_soda_binary_installed) {
// Check if the language pack is already installed if the SODA binary was
// already installed. If the SODA binary has not been installed,
// `prefs::kSodaRegisteredLanguagePacks` will contain the default language
// pack which may not exist on the device.
for (const auto& language : g_browser_process->local_state()->GetList(
prefs::kSodaRegisteredLanguagePacks)) {
if (language.GetString() == language_code) {
return false;
}
}
}
return base::Contains(
speech::SodaInstaller::GetInstance()->GetLiveCaptionEnabledLanguages(),
language_code);
}
} // namespace
#endif // !BUILDFLAG(IS_ANDROID)
namespace speech {
OnDeviceSpeechRecognitionImpl::~OnDeviceSpeechRecognitionImpl() = default;
void OnDeviceSpeechRecognitionImpl::Bind(
mojo::PendingReceiver<media::mojom::OnDeviceSpeechRecognition> receiver) {
receiver_.Bind(std::move(receiver));
}
void OnDeviceSpeechRecognitionImpl::OnDeviceWebSpeechAvailable(
const std::string& language,
OnDeviceSpeechRecognitionImpl::OnDeviceWebSpeechAvailableCallback
callback) {
#if BUILDFLAG(IS_ANDROID)
std::move(callback).Run(false);
#else
std::move(callback).Run(
speech::IsOnDeviceSpeechRecognitionAvailable(language));
#endif // BUILDFLAG(IS_ANDROID)
}
void OnDeviceSpeechRecognitionImpl::InstallOnDeviceSpeechRecognition(
const std::string& language,
OnDeviceSpeechRecognitionImpl::InstallOnDeviceSpeechRecognitionCallback
callback) {
#if BUILDFLAG(IS_ANDROID)
std::move(callback).Run(false);
#else
std::optional<speech::SodaLanguagePackComponentConfig> language_config =
speech::GetLanguageComponentConfigMatchingLanguageSubtag(language);
if (!language_config.has_value() ||
!IsLanguageInstallable(
language_config.value().language_name,
speech::SodaInstaller::GetInstance()->IsSodaBinaryInstalled())) {
std::move(callback).Run(false);
return;
}
if (!CanInstallWithoutUserConsent(language_config.value().language_name)) {
std::move(callback).Run(false);
// TODO(crbug.com/40286514): Prompt the user for permission to download
// language pack.
return;
}
// `InstallSoda` will only install the SODA binary if it is not already
// installed.
speech::SodaInstaller::GetInstance()->InstallSoda(
g_browser_process->local_state());
// `InstallLanguage` will only install languages that are not already
// installed.
speech::SodaInstaller::GetInstance()->InstallLanguage(
language_config.value().language_name, g_browser_process->local_state());
std::move(callback).Run(true);
#endif // BUILDFLAG(IS_ANDROID)
}
OnDeviceSpeechRecognitionImpl::OnDeviceSpeechRecognitionImpl(
content::RenderFrameHost* frame_host)
: content::DocumentUserData<OnDeviceSpeechRecognitionImpl>(frame_host),
pref_service_(Profile::FromBrowserContext(
frame_host->GetProcess()->GetBrowserContext())
->GetPrefs()),
language_prefs_(
std::make_unique<language::LanguagePrefs>(pref_service_)) {}
bool OnDeviceSpeechRecognitionImpl::CanInstallWithoutUserConsent(
const std::string& language) {
net::NetworkChangeNotifier::ConnectionType connection_type =
net::NetworkChangeNotifier::GetConnectionType();
if (connection_type != net::NetworkChangeNotifier::CONNECTION_ETHERNET &&
connection_type != net::NetworkChangeNotifier::CONNECTION_WIFI) {
return false;
}
std::vector<std::string> accept_languages;
language_prefs_->GetAcceptLanguagesList(&accept_languages);
for (auto accept_language : accept_languages) {
if (l10n_util::GetLanguage(base::ToLowerASCII(accept_language)) ==
l10n_util::GetLanguage(base::ToLowerASCII(language))) {
return true;
}
}
return false;
}
DOCUMENT_USER_DATA_KEY_IMPL(OnDeviceSpeechRecognitionImpl);
} // namespace speech