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

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: more cleanup 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
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 uninstall_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::MaybeUninstallHotwordExtension),
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_ != static_cast<Profile*>(browser_context) ||
Yoyo Zhou 2014/06/18 00:33:21 Profile::FromBrowserContext
rpetterson 2014/06/18 02:08:44 Done.
266 !GetExtensionService(profile_))
267 return;
268
269 // Verify that the extension was uninstalled because of a locale mismatch.
270 // If that's the case, reinstall and reset the saved locale.
271 if (!ShouldUninstallHotwordExtension())
Yoyo Zhou 2014/06/18 00:33:21 Does uninstall_pending_ serve as well here?
rpetterson 2014/06/18 02:08:44 You mean just replace the check ShouldUninstallHot
272 return;
273
274 InstallHotwordExtensionFromWebstore();
275 SetPreviousLocalePref();
276 }
277
278 void HotwordService::InstallHotwordExtensionFromWebstore() {
279 extensions::WebstoreStandaloneInstaller::Callback callback =
280 base::Bind(&HotwordService::OnHotwordExtensionInstalled,
281 base::Unretained(this));
282 installer_ = new extensions::WebstoreStartupInstaller(
Yoyo Zhou 2014/06/18 00:33:21 I'm not confident that this is the right choice of
rpetterson 2014/06/18 02:08:44 This is the same one used by ChromeOS to install t
283 extension_misc::kHotwordExtensionId,
284 profile_,
285 false,
286 callback);
287 installer_->BeginInstall();
288 }
289
290 void HotwordService::OnExtensionInstalled(
291 content::BrowserContext* browser_context,
292 const extensions::Extension* extension) {
293
294 if (extension->id() != extension_misc::kHotwordExtensionId ||
295 profile_ != static_cast<Profile*>(browser_context))
296 return;
297
298 // If the previous locale pref has never been set, set it now since
299 // the extension has been installed.
300 if (!profile_->GetPrefs()->HasPrefPath(prefs::kHotwordPreviousLanguage))
301 SetPreviousLocalePref();
302
303 // If MaybeUninstallHotwordExtension already triggered an uninstall, we
304 // don't want to loop and trigger another uninstall-install cycle.
305 // However, if we arrived her via an uninstall-triggered-install (and in
306 // that case |uninstall_pending_| will be true) then we know install
307 // has completed and we can reset |uninstall_pending_|.
308 if (!uninstall_pending_)
Yoyo Zhou 2014/06/18 00:33:21 What do you think about calling this reinstall_pen
rpetterson 2014/06/18 02:08:44 sgtm. done.
309 MaybeUninstallHotwordExtension();
310 else
311 uninstall_pending_ = false;
312
313 // Now that the extension is installed, if the user has not selected
314 // the preference on, make sure it is turned off.
315 //
316 // Disabling the extension automatically on install should only occur
317 // if the user is in the field trial for auto-install which is gated
318 // by the IsHotwordAllowed check. The check for IsHotwordAllowed() here
319 // can be removed once it's known that few people have manually
320 // installed extension.
321 if (IsHotwordAllowed() &&
Yoyo Zhou 2014/06/18 00:33:21 This check/disable looks repeated from the constru
rpetterson 2014/06/18 02:08:44 Yes, but we have to repeat it because the HotwordS
322 !profile_->GetPrefs()->GetBoolean(prefs::kHotwordSearchEnabled)) {
323 DisableHotwordExtension(GetExtensionService(profile_));
324 }
325 }
326
327 bool HotwordService::MaybeUninstallHotwordExtension() {
328 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
329
330 ExtensionService* extension_service = GetExtensionService(profile_);
331 if (!extension_service)
332 return false;
333
334 const extensions::Extension* extension = extension_service->GetExtensionById(
335 extension_misc::kHotwordExtensionId, true);
336 if (!extension)
337 return false;
338
339 // If the extension is currently pending, return and we'll check again
340 // after the install is finished.
341 extensions::PendingExtensionManager* pending_manager =
342 extension_service->pending_extension_manager();
343 if (pending_manager->IsIdPending(extension->id()))
344 return false;
345
346 // If there is already a pending request from HotwordService, don't try
347 // to uninstall either.
348 if (uninstall_pending_)
349 return false;
350
351 // Check if the current locale matches the previous. If they don't match,
352 // uninstall the extension.
353 if (!ShouldUninstallHotwordExtension())
354 return false;
355
356 uninstall_pending_ = true;
357 return UninstallHotwordExtension(extension_service);
358 }
359
360 bool HotwordService::UninstallHotwordExtension(
361 ExtensionService* extension_service) {
362 base::string16 error;
363 if (!extension_service->UninstallExtension(
364 extension_misc::kHotwordExtensionId, true, &error)) {
365 LOG(WARNING) << "Cannot uninstall extension with id "
366 << extension_misc::kHotwordExtensionId
367 << ": " << error;
368 return false;
369 }
370 return true;
371 }
372
260 bool HotwordService::IsServiceAvailable() { 373 bool HotwordService::IsServiceAvailable() {
261 error_message_ = 0; 374 error_message_ = 0;
262 375
263 // Determine if the extension is available. 376 // Determine if the extension is available.
264 extensions::ExtensionSystem* system = 377 extensions::ExtensionSystem* system =
265 extensions::ExtensionSystem::Get(profile_); 378 extensions::ExtensionSystem::Get(profile_);
266 ExtensionService* service = system->extension_service(); 379 ExtensionService* service = system->extension_service();
267 // Include disabled extensions (true parameter) since it may not be enabled 380 // Include disabled extensions (true parameter) since it may not be enabled
268 // if the user opted out. 381 // if the user opted out.
269 const extensions::Extension* extension = 382 const extensions::Extension* extension =
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
364 477
365 DCHECK(client_ == client); 478 DCHECK(client_ == client);
366 479
367 client_ = NULL; 480 client_ = NULL;
368 HotwordPrivateEventService* event_service = 481 HotwordPrivateEventService* event_service =
369 BrowserContextKeyedAPIFactory<HotwordPrivateEventService>::Get(profile_); 482 BrowserContextKeyedAPIFactory<HotwordPrivateEventService>::Get(profile_);
370 if (event_service) 483 if (event_service)
371 event_service->OnHotwordSessionStopped(); 484 event_service->OnHotwordSessionStopped();
372 #endif 485 #endif
373 } 486 }
487
488 void HotwordService::SetPreviousLocalePref() {
489 profile_->GetPrefs()->SetString(prefs::kHotwordPreviousLanguage,
490 GetCurrentLocale(profile_));
491 }
492
493 bool HotwordService::ShouldUninstallHotwordExtension() {
494 std::string previous_locale =
495 profile_->GetPrefs()->GetString(prefs::kHotwordPreviousLanguage);
496 std::string locale = GetCurrentLocale(profile_);
497
498 // If it's a new locale, then the old extension should be uninstalled.
499 // If there is no previous locale pref, then this is the first install
500 // so no need to uninstall first.
501 return locale != previous_locale &&
502 profile_->GetPrefs()->HasPrefPath(prefs::kHotwordPreviousLanguage) &&
Yoyo Zhou 2014/06/18 00:33:21 nit: seems like the no-previous-locale check shoul
rpetterson 2014/06/18 02:08:44 Done.
503 HotwordService::DoesHotwordSupportLanguage(profile_);
504 }
505
506 void HotwordService::OnHotwordExtensionInstalled(bool success,
Yoyo Zhou 2014/06/18 00:33:21 This callback doesn't really do anything. Is it ne
rpetterson 2014/06/18 02:08:44 I agree. Removed.
507 const std::string& error) {
508 if (!success)
509 LOG(WARNING) << "Cannot install extension with id "
510 << extension_misc::kHotwordExtensionId
511 << ": " << error;
512 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698