soda_installer.cc 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. // Copyright 2020 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/soda/soda_installer.h"
  5. #include "base/containers/contains.h"
  6. #include "base/feature_list.h"
  7. #include "base/observer_list.h"
  8. #include "base/values.h"
  9. #include "build/chromeos_buildflags.h"
  10. #include "components/live_caption/pref_names.h"
  11. #include "components/prefs/pref_service.h"
  12. #include "components/prefs/scoped_user_pref_update.h"
  13. #include "components/soda/constants.h"
  14. #include "components/soda/pref_names.h"
  15. #include "media/base/media_switches.h"
  16. #if BUILDFLAG(IS_CHROMEOS_ASH)
  17. #include "ash/constants/ash_features.h"
  18. #include "ash/constants/ash_pref_names.h"
  19. #endif // BUILDFLAG(IS_CHROMEOS_ASH)
  20. namespace {
  21. constexpr int kSodaCleanUpDelayInDays = 30;
  22. #if BUILDFLAG(IS_CHROMEOS_ASH)
  23. inline std::string GetProjectorLanguageCode(PrefService* pref_service) {
  24. return pref_service->GetString(ash::prefs::kProjectorCreationFlowLanguage);
  25. }
  26. #endif // IS_CHROMEOS_ASH
  27. } // namespace
  28. namespace speech {
  29. SodaInstaller* g_instance = nullptr;
  30. // static
  31. SodaInstaller* SodaInstaller::GetInstance() {
  32. #if BUILDFLAG(IS_CHROMEOS_ASH)
  33. DCHECK(
  34. base::FeatureList::IsEnabled(ash::features::kOnDeviceSpeechRecognition));
  35. #endif // BUILDFLAG(IS_CHROMEOS_ASH)
  36. return g_instance;
  37. }
  38. SodaInstaller::SodaInstaller() {
  39. DCHECK_EQ(nullptr, g_instance);
  40. g_instance = this;
  41. }
  42. SodaInstaller::~SodaInstaller() {
  43. DCHECK_EQ(this, g_instance);
  44. g_instance = nullptr;
  45. }
  46. // static
  47. void SodaInstaller::RegisterLocalStatePrefs(PrefRegistrySimple* registry) {
  48. registry->RegisterTimePref(prefs::kSodaScheduledDeletionTime, base::Time());
  49. SodaInstaller::RegisterRegisteredLanguagePackPref(registry);
  50. #if !BUILDFLAG(IS_CHROMEOS_ASH)
  51. // Handle non-Chrome-OS logic here. We need to keep the implementation of this
  52. // method in the parent class to avoid duplicate declaration build errors
  53. // (specifically on Windows).
  54. registry->RegisterFilePathPref(prefs::kSodaBinaryPath, base::FilePath());
  55. // Register language pack config path preferences.
  56. for (const SodaLanguagePackComponentConfig& config :
  57. kLanguageComponentConfigs) {
  58. registry->RegisterFilePathPref(config.config_path_pref, base::FilePath());
  59. }
  60. #endif // !BUILDFLAG(IS_CHROMEOS_ASH)
  61. }
  62. void SodaInstaller::Init(PrefService* profile_prefs,
  63. PrefService* global_prefs) {
  64. #if BUILDFLAG(IS_CHROMEOS_ASH)
  65. if (!base::FeatureList::IsEnabled(
  66. ash::features::kOnDeviceSpeechRecognition) ||
  67. soda_installer_initialized_) {
  68. #else // !BUILDFLAG(IS_CHROMEOS_ASH)
  69. if (soda_installer_initialized_) {
  70. #endif
  71. return;
  72. }
  73. if (IsAnyFeatureUsingSodaEnabled(profile_prefs)) {
  74. soda_installer_initialized_ = true;
  75. // Set the SODA uninstaller time to NULL time so that it doesn't get
  76. // uninstalled when features are using it.
  77. global_prefs->SetTime(prefs::kSodaScheduledDeletionTime, base::Time());
  78. SodaInstaller::GetInstance()->InstallSoda(global_prefs);
  79. if (global_prefs->GetValueList(prefs::kSodaRegisteredLanguagePacks)
  80. .empty()) {
  81. // TODO(crbug.com/1200667): Register the default language used by
  82. // Dictation on ChromeOS.
  83. #if BUILDFLAG(IS_CHROMEOS_ASH)
  84. RegisterLanguage(GetProjectorLanguageCode(profile_prefs), global_prefs);
  85. #endif // BUILDFLAG(IS_CHROMEOS_ASH)
  86. RegisterLanguage(prefs::GetLiveCaptionLanguageCode(profile_prefs),
  87. global_prefs);
  88. }
  89. for (const auto& language :
  90. global_prefs->GetValueList(prefs::kSodaRegisteredLanguagePacks)) {
  91. SodaInstaller::GetInstance()->InstallLanguage(language.GetString(),
  92. global_prefs);
  93. }
  94. } else {
  95. base::Time deletion_time =
  96. global_prefs->GetTime(prefs::kSodaScheduledDeletionTime);
  97. if (!deletion_time.is_null() && deletion_time <= base::Time::Now()) {
  98. UninstallSoda(global_prefs);
  99. soda_installer_initialized_ = false;
  100. }
  101. }
  102. }
  103. void SodaInstaller::SetUninstallTimer(PrefService* profile_prefs,
  104. PrefService* global_prefs) {
  105. // Do not schedule uninstallation if any SODA client features are still
  106. // enabled.
  107. if (IsAnyFeatureUsingSodaEnabled(profile_prefs))
  108. return;
  109. // Schedule deletion.
  110. global_prefs->SetTime(
  111. prefs::kSodaScheduledDeletionTime,
  112. base::Time::Now() + base::Days(kSodaCleanUpDelayInDays));
  113. }
  114. bool SodaInstaller::IsSodaInstalled(LanguageCode language_code) const {
  115. return (soda_binary_installed_ && IsLanguageInstalled(language_code));
  116. }
  117. bool SodaInstaller::IsLanguageInstalled(LanguageCode language_code) const {
  118. return base::Contains(installed_languages_, language_code);
  119. }
  120. void SodaInstaller::AddObserver(Observer* observer) {
  121. observers_.AddObserver(observer);
  122. }
  123. void SodaInstaller::RemoveObserver(Observer* observer) {
  124. observers_.RemoveObserver(observer);
  125. }
  126. void SodaInstaller::NotifySodaInstalledForTesting(LanguageCode language_code) {
  127. // TODO: Call the actual functions in SodaInstallerImpl and
  128. // SodaInstallerImpleChromeOS that do this logic
  129. // (e.g. SodaInstallerImpl::OnSodaBinaryInstalled) rather than faking it.
  130. // If language code is none, this signifies that the SODA binary installed.
  131. if (language_code == LanguageCode::kNone) {
  132. soda_binary_installed_ = true;
  133. is_soda_downloading_ = false;
  134. for (LanguageCode installed_language : installed_languages_) {
  135. NotifyOnSodaInstalled(installed_language);
  136. }
  137. return;
  138. }
  139. // Otherwise, this means a language pack installed.
  140. installed_languages_.insert(language_code);
  141. if (base::Contains(language_pack_progress_, language_code))
  142. language_pack_progress_.erase(language_code);
  143. if (soda_binary_installed_)
  144. NotifyOnSodaInstalled(language_code);
  145. }
  146. void SodaInstaller::NotifySodaErrorForTesting(LanguageCode language_code,
  147. ErrorCode error_code) {
  148. // TODO: Call the actual functions in SodaInstallerImpl and
  149. // SodaInstallerImpleChromeOS that do this logic rather than faking it.
  150. if (language_code == LanguageCode::kNone) {
  151. // Error with the SODA binary download.
  152. soda_binary_installed_ = false;
  153. is_soda_downloading_ = false;
  154. language_pack_progress_.clear();
  155. } else {
  156. // Error with the language pack download.
  157. if (base::Contains(language_pack_progress_, language_code))
  158. language_pack_progress_.erase(language_code);
  159. }
  160. NotifyOnSodaInstallError(language_code, error_code);
  161. }
  162. void SodaInstaller::UninstallSodaForTesting() {
  163. soda_binary_installed_ = false;
  164. is_soda_downloading_ = false;
  165. soda_installer_initialized_ = false;
  166. installed_languages_.clear();
  167. language_pack_progress_.clear();
  168. }
  169. void SodaInstaller::NotifySodaProgressForTesting(int progress,
  170. LanguageCode language_code) {
  171. // TODO: Call the actual functions in SodaInstallerImpl and
  172. // SodaInstallerImpleChromeOS that do this logic rather than faking it.
  173. if (language_code == LanguageCode::kNone) {
  174. // SODA binary download progress.
  175. soda_binary_installed_ = false;
  176. is_soda_downloading_ = true;
  177. } else {
  178. // Language pack download progress.
  179. if (base::Contains(language_pack_progress_, language_code))
  180. language_pack_progress_.insert({language_code, progress});
  181. else
  182. language_pack_progress_[language_code] = progress;
  183. }
  184. NotifyOnSodaProgress(language_code, progress);
  185. }
  186. bool SodaInstaller::IsAnyLanguagePackInstalledForTesting() const {
  187. return !installed_languages_.empty();
  188. }
  189. void SodaInstaller::RegisterRegisteredLanguagePackPref(
  190. PrefRegistrySimple* registry) {
  191. // TODO: Default to one of the user's languages.
  192. base::Value::List default_languages;
  193. default_languages.Append(base::Value(kUsEnglishLocale));
  194. registry->RegisterListPref(prefs::kSodaRegisteredLanguagePacks,
  195. std::move(default_languages));
  196. }
  197. void SodaInstaller::NotifyOnSodaInstalled(LanguageCode language_code) {
  198. for (Observer& observer : observers_)
  199. observer.OnSodaInstalled(language_code);
  200. }
  201. void SodaInstaller::NotifyOnSodaInstallError(LanguageCode language_code,
  202. ErrorCode error_code) {
  203. for (Observer& observer : observers_)
  204. observer.OnSodaInstallError(language_code, error_code);
  205. }
  206. void SodaInstaller::NotifyOnSodaProgress(LanguageCode language_code,
  207. int progress) {
  208. for (Observer& observer : observers_)
  209. observer.OnSodaProgress(language_code, progress);
  210. }
  211. void SodaInstaller::RegisterLanguage(const std::string& language,
  212. PrefService* global_prefs) {
  213. ListPrefUpdate update(global_prefs, prefs::kSodaRegisteredLanguagePacks);
  214. if (!base::Contains(update->GetList(), base::Value(language))) {
  215. update->GetList().Append(language);
  216. }
  217. }
  218. void SodaInstaller::UnregisterLanguages(PrefService* global_prefs) {
  219. ListPrefUpdate update(global_prefs, prefs::kSodaRegisteredLanguagePacks);
  220. update->GetList().clear();
  221. }
  222. bool SodaInstaller::IsSodaDownloading(LanguageCode language_code) const {
  223. return is_soda_downloading_ ||
  224. base::Contains(language_pack_progress_, language_code);
  225. }
  226. bool SodaInstaller::IsAnyFeatureUsingSodaEnabled(PrefService* prefs) {
  227. #if BUILDFLAG(IS_CHROMEOS_ASH)
  228. return prefs->GetBoolean(prefs::kLiveCaptionEnabled) ||
  229. prefs->GetBoolean(ash::prefs::kAccessibilityDictationEnabled) ||
  230. prefs->GetBoolean(ash::prefs::kProjectorCreationFlowEnabled);
  231. #else // !BUILDFLAG(IS_CHROMEOS_ASH)
  232. return prefs->GetBoolean(prefs::kLiveCaptionEnabled);
  233. #endif
  234. }
  235. } // namespace speech