Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(221)

Side by Side Diff: chrome/browser/search/hotword_service.cc

Issue 330193005: [Hotword] Uninstall and reinstall the extension upon language change. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fix comment Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/search/hotword_service.h" 5 #include "chrome/browser/search/hotword_service.h"
6 6
7 #include "base/i18n/case_conversion.h" 7 #include "base/i18n/case_conversion.h"
8 #include "base/metrics/field_trial.h" 8 #include "base/metrics/field_trial.h"
9 #include "base/metrics/histogram.h" 9 #include "base/metrics/histogram.h"
10 #include "base/path_service.h" 10 #include "base/path_service.h"
11 #include "base/prefs/pref_service.h" 11 #include "base/prefs/pref_service.h"
12 #include "chrome/browser/browser_process.h" 12 #include "chrome/browser/browser_process.h"
13 #include "chrome/browser/chrome_notification_types.h" 13 #include "chrome/browser/chrome_notification_types.h"
14 #include "chrome/browser/extensions/extension_service.h" 14 #include "chrome/browser/extensions/extension_service.h"
15 #include "chrome/browser/extensions/pending_extension_manager.h"
16 #include "chrome/browser/extensions/updater/extension_updater.h"
17 #include "chrome/browser/extensions/webstore_startup_installer.h"
15 #include "chrome/browser/plugins/plugin_prefs.h" 18 #include "chrome/browser/plugins/plugin_prefs.h"
16 #include "chrome/browser/profiles/profile.h" 19 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/search/hotword_service_factory.h" 20 #include "chrome/browser/search/hotword_service_factory.h"
18 #include "chrome/common/chrome_paths.h" 21 #include "chrome/common/chrome_paths.h"
19 #include "chrome/common/extensions/extension_constants.h" 22 #include "chrome/common/extensions/extension_constants.h"
20 #include "chrome/common/pref_names.h" 23 #include "chrome/common/pref_names.h"
21 #include "content/public/browser/browser_thread.h" 24 #include "content/public/browser/browser_thread.h"
22 #include "content/public/browser/notification_service.h" 25 #include "content/public/browser/notification_service.h"
23 #include "content/public/browser/plugin_service.h" 26 #include "content/public/browser/plugin_service.h"
24 #include "content/public/common/webplugininfo.h" 27 #include "content/public/common/webplugininfo.h"
25 #include "extensions/browser/extension_system.h" 28 #include "extensions/browser/extension_system.h"
26 #include "extensions/common/extension.h" 29 #include "extensions/common/extension.h"
30 #include "extensions/common/one_shot_event.h"
27 #include "grit/generated_resources.h" 31 #include "grit/generated_resources.h"
28 #include "ui/base/l10n/l10n_util.h" 32 #include "ui/base/l10n/l10n_util.h"
29 33
30 // The whole file relies on the extension systems but this file is built on 34 // The whole file relies on the extension systems but this file is built on
31 // some non-extension supported platforms and including an API header will cause 35 // some non-extension supported platforms and including an API header will cause
32 // a compile error since it depends on header files generated by .idl. 36 // a compile error since it depends on header files generated by .idl.
33 // TODO(mukai): clean up file dependencies and remove this clause. 37 // TODO(mukai): clean up file dependencies and remove this clause.
34 #if defined(ENABLE_EXTENSIONS) 38 #if defined(ENABLE_EXTENSIONS)
35 #include "chrome/browser/extensions/api/hotword_private/hotword_private_api.h" 39 #include "chrome/browser/extensions/api/hotword_private/hotword_private_api.h"
36 #endif 40 #endif
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
138 ExtensionService* GetExtensionService(Profile* profile) { 142 ExtensionService* GetExtensionService(Profile* profile) {
139 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 143 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
140 144
141 extensions::ExtensionSystem* extension_system = 145 extensions::ExtensionSystem* extension_system =
142 extensions::ExtensionSystem::Get(profile); 146 extensions::ExtensionSystem::Get(profile);
143 if (extension_system) 147 if (extension_system)
144 return extension_system->extension_service(); 148 return extension_system->extension_service();
145 return NULL; 149 return NULL;
146 } 150 }
147 151
152 // static
Jered 2014/06/18 15:04:19 delete this comment
rpetterson 2014/06/18 20:48:33 Done.
153 std::string GetCurrentLocale(Profile* profile) {
154 std::string locale =
155 #if defined(OS_CHROMEOS)
156 // On ChromeOS locale is per-profile.
157 profile->GetPrefs()->GetString(prefs::kApplicationLocale);
158 #else
159 g_browser_process->GetApplicationLocale();
160 #endif
161 return locale;
162 }
163
148 } // namespace 164 } // namespace
149 165
150 namespace hotword_internal { 166 namespace hotword_internal {
151 // Constants for the hotword field trial. 167 // Constants for the hotword field trial.
152 const char kHotwordFieldTrialName[] = "VoiceTrigger"; 168 const char kHotwordFieldTrialName[] = "VoiceTrigger";
153 const char kHotwordFieldTrialDisabledGroupName[] = "Disabled"; 169 const char kHotwordFieldTrialDisabledGroupName[] = "Disabled";
154 // Old preference constant. 170 // Old preference constant.
155 const char kHotwordUnusablePrefName[] = "hotword.search_enabled"; 171 const char kHotwordUnusablePrefName[] = "hotword.search_enabled";
156 } // namespace hotword_internal 172 } // namespace hotword_internal
157 173
158 // static 174 // static
159 bool HotwordService::DoesHotwordSupportLanguage(Profile* profile) { 175 bool HotwordService::DoesHotwordSupportLanguage(Profile* profile) {
160 std::string locale = 176 std::string normalized_locale =
161 #if defined(OS_CHROMEOS) 177 l10n_util::NormalizeLocale(GetCurrentLocale(profile));
162 // On ChromeOS locale is per-profile.
163 profile->GetPrefs()->GetString(prefs::kApplicationLocale);
164 #else
165 g_browser_process->GetApplicationLocale();
166 #endif
167 std::string normalized_locale = l10n_util::NormalizeLocale(locale);
168 StringToLowerASCII(&normalized_locale); 178 StringToLowerASCII(&normalized_locale);
169 179
170 for (size_t i = 0; i < arraysize(kSupportedLocales); i++) { 180 for (size_t i = 0; i < arraysize(kSupportedLocales); i++) {
171 if (normalized_locale.compare(0, 2, kSupportedLocales[i]) == 0) 181 if (normalized_locale.compare(0, 2, kSupportedLocales[i]) == 0)
172 return true; 182 return true;
173 } 183 }
174 return false; 184 return false;
175 } 185 }
176 186
177 HotwordService::HotwordService(Profile* profile) 187 HotwordService::HotwordService(Profile* profile)
178 : profile_(profile), 188 : profile_(profile),
189 extension_registry_observer_(this),
179 client_(NULL), 190 client_(NULL),
180 error_message_(0) { 191 error_message_(0),
192 reinstall_pending_(false),
193 weak_factory_(this) {
194 extension_registry_observer_.Add(extensions::ExtensionRegistry::Get(profile));
181 // This will be called during profile initialization which is a good time 195 // This will be called during profile initialization which is a good time
182 // to check the user's hotword state. 196 // to check the user's hotword state.
183 HotwordEnabled enabled_state = UNSET; 197 HotwordEnabled enabled_state = UNSET;
184 if (profile_->GetPrefs()->HasPrefPath(prefs::kHotwordSearchEnabled)) { 198 if (profile_->GetPrefs()->HasPrefPath(prefs::kHotwordSearchEnabled)) {
185 if (profile_->GetPrefs()->GetBoolean(prefs::kHotwordSearchEnabled)) 199 if (profile_->GetPrefs()->GetBoolean(prefs::kHotwordSearchEnabled))
186 enabled_state = ENABLED; 200 enabled_state = ENABLED;
187 else 201 else
188 enabled_state = DISABLED; 202 enabled_state = DISABLED;
189 } else { 203 } else {
190 // If the preference has not been set the hotword extension should 204 // If the preference has not been set the hotword extension should
191 // not be running. However, this should only be done if auto-install 205 // not be running. However, this should only be done if auto-install
192 // is enabled which is gated through the IsHotwordAllowed check. 206 // is enabled which is gated through the IsHotwordAllowed check.
193 if (IsHotwordAllowed()) 207 if (IsHotwordAllowed())
194 DisableHotwordExtension(GetExtensionService(profile_)); 208 DisableHotwordExtension(GetExtensionService(profile_));
195 } 209 }
196 UMA_HISTOGRAM_ENUMERATION("Hotword.Enabled", enabled_state, 210 UMA_HISTOGRAM_ENUMERATION("Hotword.Enabled", enabled_state,
197 NUM_HOTWORD_ENABLED_METRICS); 211 NUM_HOTWORD_ENABLED_METRICS);
198 212
199 pref_registrar_.Init(profile_->GetPrefs()); 213 pref_registrar_.Init(profile_->GetPrefs());
200 pref_registrar_.Add( 214 pref_registrar_.Add(
201 prefs::kHotwordSearchEnabled, 215 prefs::kHotwordSearchEnabled,
202 base::Bind(&HotwordService::OnHotwordSearchEnabledChanged, 216 base::Bind(&HotwordService::OnHotwordSearchEnabledChanged,
203 base::Unretained(this))); 217 base::Unretained(this)));
204 218
205 registrar_.Add(this, 219 registrar_.Add(this,
206 chrome::NOTIFICATION_EXTENSION_INSTALLED_DEPRECATED,
207 content::Source<Profile>(profile_));
208 registrar_.Add(this,
209 chrome::NOTIFICATION_BROWSER_WINDOW_READY, 220 chrome::NOTIFICATION_BROWSER_WINDOW_READY,
210 content::NotificationService::AllSources()); 221 content::NotificationService::AllSources());
211 222
223 extensions::ExtensionSystem::Get(profile_)->ready().Post(
224 FROM_HERE,
225 base::Bind(base::IgnoreResult(
226 &HotwordService::MaybeReinstallHotwordExtension),
227 weak_factory_.GetWeakPtr()));
228
212 // Clear the old user pref because it became unusable. 229 // Clear the old user pref because it became unusable.
213 // TODO(rlp): Remove this code per crbug.com/358789. 230 // TODO(rlp): Remove this code per crbug.com/358789.
214 if (profile_->GetPrefs()->HasPrefPath( 231 if (profile_->GetPrefs()->HasPrefPath(
215 hotword_internal::kHotwordUnusablePrefName)) { 232 hotword_internal::kHotwordUnusablePrefName)) {
216 profile_->GetPrefs()->ClearPref(hotword_internal::kHotwordUnusablePrefName); 233 profile_->GetPrefs()->ClearPref(hotword_internal::kHotwordUnusablePrefName);
217 } 234 }
218 } 235 }
219 236
220 HotwordService::~HotwordService() { 237 HotwordService::~HotwordService() {
221 } 238 }
222 239
223 void HotwordService::Observe(int type, 240 void HotwordService::Observe(int type,
224 const content::NotificationSource& source, 241 const content::NotificationSource& source,
225 const content::NotificationDetails& details) { 242 const content::NotificationDetails& details) {
226 if (type == chrome::NOTIFICATION_EXTENSION_INSTALLED_DEPRECATED) { 243 if (type == chrome::NOTIFICATION_BROWSER_WINDOW_READY) {
227 const extensions::Extension* extension =
228 content::Details<const extensions::InstalledExtensionInfo>(details)
229 ->extension;
230 // Disabling the extension automatically on install should only occur
231 // if the user is in the field trial for auto-install which is gated
232 // by the IsHotwordAllowed check.
233 if (IsHotwordAllowed() &&
234 extension->id() == extension_misc::kHotwordExtensionId &&
235 !profile_->GetPrefs()->GetBoolean(prefs::kHotwordSearchEnabled)) {
236 DisableHotwordExtension(GetExtensionService(profile_));
237 // Once the extension is disabled, it will not be enabled until the
238 // user opts in at which point the pref registrar will take over
239 // enabling and disabling.
240 registrar_.Remove(this,
241 chrome::NOTIFICATION_EXTENSION_INSTALLED_DEPRECATED,
242 content::Source<Profile>(profile_));
243 }
244 } else if (type == chrome::NOTIFICATION_BROWSER_WINDOW_READY) {
245 // The microphone monitor must be initialized as the page is loading 244 // The microphone monitor must be initialized as the page is loading
246 // so that the state of the microphone is available when the page 245 // so that the state of the microphone is available when the page
247 // loads. The Ok Google Hotword setting will display an error if there 246 // loads. The Ok Google Hotword setting will display an error if there
248 // is no microphone but this information will not be up-to-date unless 247 // is no microphone but this information will not be up-to-date unless
249 // the monitor had already been started. Furthermore, the pop up to 248 // the monitor had already been started. Furthermore, the pop up to
250 // opt in to hotwording won't be available if it thinks there is no 249 // opt in to hotwording won't be available if it thinks there is no
251 // microphone. There is no hard guarantee that the monitor will actually 250 // microphone. There is no hard guarantee that the monitor will actually
252 // be up by the time it's needed, but this is the best we can do without 251 // be up by the time it's needed, but this is the best we can do without
253 // starting it at start up which slows down start up too much. 252 // starting it at start up which slows down start up too much.
254 // The content/media for microphone uses the same observer design and 253 // The content/media for microphone uses the same observer design and
255 // makes use of the same audio device monitor. 254 // makes use of the same audio device monitor.
256 HotwordServiceFactory::GetInstance()->UpdateMicrophoneState(); 255 HotwordServiceFactory::GetInstance()->UpdateMicrophoneState();
257 } 256 }
258 } 257 }
259 258
259 void HotwordService::OnExtensionUninstalled(
260 content::BrowserContext* browser_context,
261 const extensions::Extension* extension) {
262 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
263
264 if (extension->id() != extension_misc::kHotwordExtensionId ||
265 profile_ != Profile::FromBrowserContext(browser_context) ||
266 !GetExtensionService(profile_))
267 return;
268
269 // If the extension wasn't uninstalled due to language change, don't try to
270 // reinstall it.
271 if (!reinstall_pending_)
272 return;
273
274 InstallHotwordExtensionFromWebstore();
275 SetPreviousLocalePref();
276 }
277
278 void HotwordService::InstallHotwordExtensionFromWebstore() {
279 installer_ = new extensions::WebstoreStartupInstaller(
280 extension_misc::kHotwordExtensionId,
281 profile_,
282 false,
283 extensions::WebstoreStandaloneInstaller::Callback());
284 installer_->BeginInstall();
285 }
286
287 void HotwordService::OnExtensionInstalled(
288 content::BrowserContext* browser_context,
289 const extensions::Extension* extension) {
290
291 if (extension->id() != extension_misc::kHotwordExtensionId ||
292 profile_ != Profile::FromBrowserContext(browser_context))
293 return;
294
295 // If the previous locale pref has never been set, set it now since
296 // the extension has been installed.
297 if (!profile_->GetPrefs()->HasPrefPath(prefs::kHotwordPreviousLanguage))
298 SetPreviousLocalePref();
299
300 // If MaybeReinstallHotwordExtension already triggered an uninstall, we
301 // don't want to loop and trigger another uninstall-install cycle.
302 // However, if we arrived her via an uninstall-triggered-install (and in
Jered 2014/06/18 15:04:19 her -> here
rpetterson 2014/06/18 20:48:33 Done.
303 // that case |reinstall_pending_| will be true) then we know install
304 // has completed and we can reset |reinstall_pending_|.
305 if (!reinstall_pending_)
306 MaybeReinstallHotwordExtension();
Jered 2014/06/18 15:04:19 Huh? Why would we ever want to reinstall the hotwo
Yoyo Zhou 2014/06/18 19:10:31 I believe this is for the scenario where the local
rpetterson 2014/06/18 20:48:33 That's correct. Typically, no, we would not. If we
Jered 2014/06/18 21:06:04 Ok, thanks for explaining.
307 else
308 reinstall_pending_ = false;
Jered 2014/06/18 15:04:19 Just unconditionally set this to false.
Yoyo Zhou 2014/06/18 19:10:31 I don't think that works with the previous.
rpetterson 2014/06/18 20:48:33 I thought about this, but it needs to be in an els
Jered 2014/06/18 21:06:04 sgtm.
309
310 // Now that the extension is installed, if the user has not selected
311 // the preference on, make sure it is turned off.
312 //
313 // Disabling the extension automatically on install should only occur
314 // if the user is in the field trial for auto-install which is gated
315 // by the IsHotwordAllowed check. The check for IsHotwordAllowed() here
316 // can be removed once it's known that few people have manually
317 // installed extension.
318 if (IsHotwordAllowed() &&
319 !profile_->GetPrefs()->GetBoolean(prefs::kHotwordSearchEnabled)) {
320 DisableHotwordExtension(GetExtensionService(profile_));
321 }
322 }
323
324 bool HotwordService::MaybeReinstallHotwordExtension() {
325 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
326
327 ExtensionService* extension_service = GetExtensionService(profile_);
328 if (!extension_service)
329 return false;
330
331 const extensions::Extension* extension = extension_service->GetExtensionById(
332 extension_misc::kHotwordExtensionId, true);
333 if (!extension)
334 return false;
335
336 // If the extension is currently pending, return and we'll check again
337 // after the install is finished.
338 extensions::PendingExtensionManager* pending_manager =
339 extension_service->pending_extension_manager();
340 if (pending_manager->IsIdPending(extension->id()))
341 return false;
342
343 // If there is already a pending request from HotwordService, don't try
344 // to uninstall either.
345 if (reinstall_pending_)
346 return false;
347
348 // Check if the current locale matches the previous. If they don't match,
349 // uninstall the extension.
350 if (!ShouldReinstallHotwordExtension())
351 return false;
352
353 reinstall_pending_ = true;
Jered 2014/06/18 15:04:19 Should this be set to the return value of Uninstal
Yoyo Zhou 2014/06/18 19:10:31 That seems right.
rpetterson 2014/06/18 20:48:33 Sorta. I still need to set reinstall_pending_ to t
354 return UninstallHotwordExtension(extension_service);
355 }
356
357 bool HotwordService::UninstallHotwordExtension(
358 ExtensionService* extension_service) {
359 base::string16 error;
360 if (!extension_service->UninstallExtension(
361 extension_misc::kHotwordExtensionId, true, &error)) {
362 LOG(WARNING) << "Cannot uninstall extension with id "
363 << extension_misc::kHotwordExtensionId
364 << ": " << error;
365 return false;
366 }
367 return true;
368 }
369
260 bool HotwordService::IsServiceAvailable() { 370 bool HotwordService::IsServiceAvailable() {
261 error_message_ = 0; 371 error_message_ = 0;
262 372
263 // Determine if the extension is available. 373 // Determine if the extension is available.
264 extensions::ExtensionSystem* system = 374 extensions::ExtensionSystem* system =
265 extensions::ExtensionSystem::Get(profile_); 375 extensions::ExtensionSystem::Get(profile_);
266 ExtensionService* service = system->extension_service(); 376 ExtensionService* service = system->extension_service();
267 // Include disabled extensions (true parameter) since it may not be enabled 377 // Include disabled extensions (true parameter) since it may not be enabled
268 // if the user opted out. 378 // if the user opted out.
269 const extensions::Extension* extension = 379 const extensions::Extension* extension =
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
364 474
365 DCHECK(client_ == client); 475 DCHECK(client_ == client);
366 476
367 client_ = NULL; 477 client_ = NULL;
368 HotwordPrivateEventService* event_service = 478 HotwordPrivateEventService* event_service =
369 BrowserContextKeyedAPIFactory<HotwordPrivateEventService>::Get(profile_); 479 BrowserContextKeyedAPIFactory<HotwordPrivateEventService>::Get(profile_);
370 if (event_service) 480 if (event_service)
371 event_service->OnHotwordSessionStopped(); 481 event_service->OnHotwordSessionStopped();
372 #endif 482 #endif
373 } 483 }
484
485 void HotwordService::SetPreviousLocalePref() {
Jered 2014/06/18 15:04:19 nit: The pref is called previous language, so shou
rpetterson 2014/06/18 20:48:33 Done.
486 profile_->GetPrefs()->SetString(prefs::kHotwordPreviousLanguage,
487 GetCurrentLocale(profile_));
488 }
489
490 bool HotwordService::ShouldReinstallHotwordExtension() {
491 // If there is no previous locale pref, then this is the first install
492 // so no need to uninstall first.
493 if (!profile_->GetPrefs()->HasPrefPath(prefs::kHotwordPreviousLanguage))
494 return false;
495
496 std::string previous_locale =
497 profile_->GetPrefs()->GetString(prefs::kHotwordPreviousLanguage);
498 std::string locale = GetCurrentLocale(profile_);
499
500 // If it's a new locale, then the old extension should be uninstalled.
501 return locale != previous_locale &&
502 HotwordService::DoesHotwordSupportLanguage(profile_);
503 }
OLDNEW
« no previous file with comments | « chrome/browser/search/hotword_service.h ('k') | chrome/browser/search/hotword_service_factory.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698