Commit 2c1cb79c authored by rlp@chromium.org's avatar rlp@chromium.org

[Hotword] Uninstall and reinstall the extension upon language change.

There are two places we check to see if we need uninstall-install. The first is on startup when the extension system is ready (in response to the ExtensionSystem::ready() OneShot timer). If the extension is currently pending, then we'll check again when we get the notification that the extension has installed when OnExtensionInstalled is called.

The function MaybeUninstallHotwordExtension does a series of checks to determine if we should uninstall the extension based on pending installs and previous vs. current locale. If an uninstall is necessary, it kicks it off. 

Upon Uninstall, OnExtensionUninstalled is triggered. When OnExtensionUninstalled is triggered for the hotword extension, it will reinstall it and update the previous locale (language). If the user goes in to try to uninstall the hotword extension manually, this change will also force it to be reinstalled.

This will trigger a second call to OnExtensionInstalled. To prevent a loop, we use HotwordService::uninstall_pending_ to prevent another uninstall attempt.

This CL also removes the dependency on the deprecated NOTIFICATION_EXTENSION_INSTALLED as a side effect.

Several unittests were added to the unittest suite to test various parts of the uninstall-install pipeline. In order to have access to the extension system during tests, the HotwordServiceTest now extends ExtensionServiceTestBase.

BUG=345806

Review URL: https://codereview.chromium.org/330193005

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@278644 0039d316-1c4b-4281-b951-d872f2087c98
parent 5d3766bc
...@@ -12,6 +12,9 @@ ...@@ -12,6 +12,9 @@
#include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/pending_extension_manager.h"
#include "chrome/browser/extensions/updater/extension_updater.h"
#include "chrome/browser/extensions/webstore_startup_installer.h"
#include "chrome/browser/plugins/plugin_prefs.h" #include "chrome/browser/plugins/plugin_prefs.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search/hotword_service_factory.h" #include "chrome/browser/search/hotword_service_factory.h"
...@@ -24,6 +27,7 @@ ...@@ -24,6 +27,7 @@
#include "content/public/common/webplugininfo.h" #include "content/public/common/webplugininfo.h"
#include "extensions/browser/extension_system.h" #include "extensions/browser/extension_system.h"
#include "extensions/common/extension.h" #include "extensions/common/extension.h"
#include "extensions/common/one_shot_event.h"
#include "grit/generated_resources.h" #include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util.h"
...@@ -145,6 +149,17 @@ ExtensionService* GetExtensionService(Profile* profile) { ...@@ -145,6 +149,17 @@ ExtensionService* GetExtensionService(Profile* profile) {
return NULL; return NULL;
} }
std::string GetCurrentLocale(Profile* profile) {
std::string locale =
#if defined(OS_CHROMEOS)
// On ChromeOS locale is per-profile.
profile->GetPrefs()->GetString(prefs::kApplicationLocale);
#else
g_browser_process->GetApplicationLocale();
#endif
return locale;
}
} // namespace } // namespace
namespace hotword_internal { namespace hotword_internal {
...@@ -157,14 +172,8 @@ const char kHotwordUnusablePrefName[] = "hotword.search_enabled"; ...@@ -157,14 +172,8 @@ const char kHotwordUnusablePrefName[] = "hotword.search_enabled";
// static // static
bool HotwordService::DoesHotwordSupportLanguage(Profile* profile) { bool HotwordService::DoesHotwordSupportLanguage(Profile* profile) {
std::string locale = std::string normalized_locale =
#if defined(OS_CHROMEOS) l10n_util::NormalizeLocale(GetCurrentLocale(profile));
// On ChromeOS locale is per-profile.
profile->GetPrefs()->GetString(prefs::kApplicationLocale);
#else
g_browser_process->GetApplicationLocale();
#endif
std::string normalized_locale = l10n_util::NormalizeLocale(locale);
StringToLowerASCII(&normalized_locale); StringToLowerASCII(&normalized_locale);
for (size_t i = 0; i < arraysize(kSupportedLocales); i++) { for (size_t i = 0; i < arraysize(kSupportedLocales); i++) {
...@@ -176,8 +185,12 @@ bool HotwordService::DoesHotwordSupportLanguage(Profile* profile) { ...@@ -176,8 +185,12 @@ bool HotwordService::DoesHotwordSupportLanguage(Profile* profile) {
HotwordService::HotwordService(Profile* profile) HotwordService::HotwordService(Profile* profile)
: profile_(profile), : profile_(profile),
extension_registry_observer_(this),
client_(NULL), client_(NULL),
error_message_(0) { error_message_(0),
reinstall_pending_(false),
weak_factory_(this) {
extension_registry_observer_.Add(extensions::ExtensionRegistry::Get(profile));
// This will be called during profile initialization which is a good time // This will be called during profile initialization which is a good time
// to check the user's hotword state. // to check the user's hotword state.
HotwordEnabled enabled_state = UNSET; HotwordEnabled enabled_state = UNSET;
...@@ -202,13 +215,16 @@ HotwordService::HotwordService(Profile* profile) ...@@ -202,13 +215,16 @@ HotwordService::HotwordService(Profile* profile)
base::Bind(&HotwordService::OnHotwordSearchEnabledChanged, base::Bind(&HotwordService::OnHotwordSearchEnabledChanged,
base::Unretained(this))); base::Unretained(this)));
registrar_.Add(this,
chrome::NOTIFICATION_EXTENSION_INSTALLED_DEPRECATED,
content::Source<Profile>(profile_));
registrar_.Add(this, registrar_.Add(this,
chrome::NOTIFICATION_BROWSER_WINDOW_READY, chrome::NOTIFICATION_BROWSER_WINDOW_READY,
content::NotificationService::AllSources()); content::NotificationService::AllSources());
extensions::ExtensionSystem::Get(profile_)->ready().Post(
FROM_HERE,
base::Bind(base::IgnoreResult(
&HotwordService::MaybeReinstallHotwordExtension),
weak_factory_.GetWeakPtr()));
// Clear the old user pref because it became unusable. // Clear the old user pref because it became unusable.
// TODO(rlp): Remove this code per crbug.com/358789. // TODO(rlp): Remove this code per crbug.com/358789.
if (profile_->GetPrefs()->HasPrefPath( if (profile_->GetPrefs()->HasPrefPath(
...@@ -223,25 +239,7 @@ HotwordService::~HotwordService() { ...@@ -223,25 +239,7 @@ HotwordService::~HotwordService() {
void HotwordService::Observe(int type, void HotwordService::Observe(int type,
const content::NotificationSource& source, const content::NotificationSource& source,
const content::NotificationDetails& details) { const content::NotificationDetails& details) {
if (type == chrome::NOTIFICATION_EXTENSION_INSTALLED_DEPRECATED) { if (type == chrome::NOTIFICATION_BROWSER_WINDOW_READY) {
const extensions::Extension* extension =
content::Details<const extensions::InstalledExtensionInfo>(details)
->extension;
// Disabling the extension automatically on install should only occur
// if the user is in the field trial for auto-install which is gated
// by the IsHotwordAllowed check.
if (IsHotwordAllowed() &&
extension->id() == extension_misc::kHotwordExtensionId &&
!profile_->GetPrefs()->GetBoolean(prefs::kHotwordSearchEnabled)) {
DisableHotwordExtension(GetExtensionService(profile_));
// Once the extension is disabled, it will not be enabled until the
// user opts in at which point the pref registrar will take over
// enabling and disabling.
registrar_.Remove(this,
chrome::NOTIFICATION_EXTENSION_INSTALLED_DEPRECATED,
content::Source<Profile>(profile_));
}
} else if (type == chrome::NOTIFICATION_BROWSER_WINDOW_READY) {
// The microphone monitor must be initialized as the page is loading // The microphone monitor must be initialized as the page is loading
// so that the state of the microphone is available when the page // so that the state of the microphone is available when the page
// loads. The Ok Google Hotword setting will display an error if there // loads. The Ok Google Hotword setting will display an error if there
...@@ -257,6 +255,123 @@ void HotwordService::Observe(int type, ...@@ -257,6 +255,123 @@ void HotwordService::Observe(int type,
} }
} }
void HotwordService::OnExtensionUninstalled(
content::BrowserContext* browser_context,
const extensions::Extension* extension) {
CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
if (extension->id() != extension_misc::kHotwordExtensionId ||
profile_ != Profile::FromBrowserContext(browser_context) ||
!GetExtensionService(profile_))
return;
// If the extension wasn't uninstalled due to language change, don't try to
// reinstall it.
if (!reinstall_pending_)
return;
InstallHotwordExtensionFromWebstore();
SetPreviousLanguagePref();
}
void HotwordService::InstallHotwordExtensionFromWebstore() {
#if defined(ENABLE_EXTENSIONS)
installer_ = new extensions::WebstoreStartupInstaller(
extension_misc::kHotwordExtensionId,
profile_,
false,
extensions::WebstoreStandaloneInstaller::Callback());
installer_->BeginInstall();
#endif
}
void HotwordService::OnExtensionInstalled(
content::BrowserContext* browser_context,
const extensions::Extension* extension) {
if (extension->id() != extension_misc::kHotwordExtensionId ||
profile_ != Profile::FromBrowserContext(browser_context))
return;
// If the previous locale pref has never been set, set it now since
// the extension has been installed.
if (!profile_->GetPrefs()->HasPrefPath(prefs::kHotwordPreviousLanguage))
SetPreviousLanguagePref();
// If MaybeReinstallHotwordExtension already triggered an uninstall, we
// don't want to loop and trigger another uninstall-install cycle.
// However, if we arrived here via an uninstall-triggered-install (and in
// that case |reinstall_pending_| will be true) then we know install
// has completed and we can reset |reinstall_pending_|.
if (!reinstall_pending_)
MaybeReinstallHotwordExtension();
else
reinstall_pending_ = false;
// Now that the extension is installed, if the user has not selected
// the preference on, make sure it is turned off.
//
// Disabling the extension automatically on install should only occur
// if the user is in the field trial for auto-install which is gated
// by the IsHotwordAllowed check. The check for IsHotwordAllowed() here
// can be removed once it's known that few people have manually
// installed extension.
if (IsHotwordAllowed() &&
!profile_->GetPrefs()->GetBoolean(prefs::kHotwordSearchEnabled)) {
DisableHotwordExtension(GetExtensionService(profile_));
}
}
bool HotwordService::MaybeReinstallHotwordExtension() {
CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
ExtensionService* extension_service = GetExtensionService(profile_);
if (!extension_service)
return false;
const extensions::Extension* extension = extension_service->GetExtensionById(
extension_misc::kHotwordExtensionId, true);
if (!extension)
return false;
// If the extension is currently pending, return and we'll check again
// after the install is finished.
extensions::PendingExtensionManager* pending_manager =
extension_service->pending_extension_manager();
if (pending_manager->IsIdPending(extension->id()))
return false;
// If there is already a pending request from HotwordService, don't try
// to uninstall either.
if (reinstall_pending_)
return false;
// Check if the current locale matches the previous. If they don't match,
// uninstall the extension.
if (!ShouldReinstallHotwordExtension())
return false;
// Ensure the call to OnExtensionUninstalled was triggered by a language
// change so it's okay to reinstall.
reinstall_pending_ = true;
return UninstallHotwordExtension(extension_service);
}
bool HotwordService::UninstallHotwordExtension(
ExtensionService* extension_service) {
base::string16 error;
if (!extension_service->UninstallExtension(
extension_misc::kHotwordExtensionId, true, &error)) {
LOG(WARNING) << "Cannot uninstall extension with id "
<< extension_misc::kHotwordExtensionId
<< ": " << error;
reinstall_pending_ = false;
return false;
}
return true;
}
bool HotwordService::IsServiceAvailable() { bool HotwordService::IsServiceAvailable() {
error_message_ = 0; error_message_ = 0;
...@@ -371,3 +486,23 @@ void HotwordService::StopHotwordSession(HotwordClient* client) { ...@@ -371,3 +486,23 @@ void HotwordService::StopHotwordSession(HotwordClient* client) {
event_service->OnHotwordSessionStopped(); event_service->OnHotwordSessionStopped();
#endif #endif
} }
void HotwordService::SetPreviousLanguagePref() {
profile_->GetPrefs()->SetString(prefs::kHotwordPreviousLanguage,
GetCurrentLocale(profile_));
}
bool HotwordService::ShouldReinstallHotwordExtension() {
// If there is no previous locale pref, then this is the first install
// so no need to uninstall first.
if (!profile_->GetPrefs()->HasPrefPath(prefs::kHotwordPreviousLanguage))
return false;
std::string previous_locale =
profile_->GetPrefs()->GetString(prefs::kHotwordPreviousLanguage);
std::string locale = GetCurrentLocale(profile_);
// If it's a new locale, then the old extension should be uninstalled.
return locale != previous_locale &&
HotwordService::DoesHotwordSupportLanguage(profile_);
}
...@@ -6,15 +6,24 @@ ...@@ -6,15 +6,24 @@
#define CHROME_BROWSER_SEARCH_HOTWORD_SERVICE_H_ #define CHROME_BROWSER_SEARCH_HOTWORD_SERVICE_H_
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/memory/weak_ptr.h"
#include "base/prefs/pref_change_registrar.h" #include "base/prefs/pref_change_registrar.h"
#include "base/scoped_observer.h"
#include "components/keyed_service/core/keyed_service.h" #include "components/keyed_service/core/keyed_service.h"
#include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h" #include "content/public/browser/notification_registrar.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_registry_observer.h"
class ExtensionService; class ExtensionService;
class HotwordClient; class HotwordClient;
class Profile; class Profile;
namespace extensions {
class Extension;
class WebstoreStandaloneInstaller;
} // namespace extensions
namespace hotword_internal { namespace hotword_internal {
// Constants for the hotword field trial. // Constants for the hotword field trial.
extern const char kHotwordFieldTrialName[]; extern const char kHotwordFieldTrialName[];
...@@ -24,6 +33,7 @@ extern const char kHotwordFieldTrialDisabledGroupName[]; ...@@ -24,6 +33,7 @@ extern const char kHotwordFieldTrialDisabledGroupName[];
// Provides an interface for the Hotword component that does voice triggered // Provides an interface for the Hotword component that does voice triggered
// search. // search.
class HotwordService : public content::NotificationObserver, class HotwordService : public content::NotificationObserver,
public extensions::ExtensionRegistryObserver,
public KeyedService { public KeyedService {
public: public:
// Returns true if the hotword supports the current system language. // Returns true if the hotword supports the current system language.
...@@ -37,6 +47,14 @@ class HotwordService : public content::NotificationObserver, ...@@ -37,6 +47,14 @@ class HotwordService : public content::NotificationObserver,
const content::NotificationSource& source, const content::NotificationSource& source,
const content::NotificationDetails& details) OVERRIDE; const content::NotificationDetails& details) OVERRIDE;
// Overridden from ExtensionRegisterObserver:
virtual void OnExtensionInstalled(
content::BrowserContext* browser_context,
const extensions::Extension* extension) OVERRIDE;
virtual void OnExtensionUninstalled(
content::BrowserContext* browser_context,
const extensions::Extension* extension) OVERRIDE;
// Checks for whether all the necessary files have downloaded to allow for // Checks for whether all the necessary files have downloaded to allow for
// using the extension. // using the extension.
virtual bool IsServiceAvailable(); virtual bool IsServiceAvailable();
...@@ -62,6 +80,23 @@ class HotwordService : public content::NotificationObserver, ...@@ -62,6 +80,23 @@ class HotwordService : public content::NotificationObserver,
void StopHotwordSession(HotwordClient* client); void StopHotwordSession(HotwordClient* client);
HotwordClient* client() { return client_; } HotwordClient* client() { return client_; }
// Checks if the current version of the hotword extension should be
// uninstalled in order to update to a different language version.
// Returns true if the extension was uninstalled.
bool MaybeReinstallHotwordExtension();
// Checks based on locale if the current version should be uninstalled so that
// a version with a different language can be installed.
bool ShouldReinstallHotwordExtension();
// Helper functions pulled out for testing purposes.
// UninstallHotwordExtension returns true if the extension was uninstalled.
virtual bool UninstallHotwordExtension(ExtensionService* extension_service);
virtual void InstallHotwordExtensionFromWebstore();
// Sets the pref value of the previous language.
void SetPreviousLanguagePref();
// Returns the current error message id. A value of 0 indicates // Returns the current error message id. A value of 0 indicates
// no error. // no error.
int error_message() { return error_message_; } int error_message() { return error_message_; }
...@@ -73,8 +108,18 @@ class HotwordService : public content::NotificationObserver, ...@@ -73,8 +108,18 @@ class HotwordService : public content::NotificationObserver,
content::NotificationRegistrar registrar_; content::NotificationRegistrar registrar_;
// For observing the ExtensionRegistry.
ScopedObserver<extensions::ExtensionRegistry,
extensions::ExtensionRegistryObserver>
extension_registry_observer_;
scoped_refptr<extensions::WebstoreStandaloneInstaller> installer_;
HotwordClient* client_; HotwordClient* client_;
int error_message_; int error_message_;
bool reinstall_pending_;
base::WeakPtrFactory<HotwordService> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(HotwordService); DISALLOW_COPY_AND_ASSIGN(HotwordService);
}; };
......
...@@ -103,6 +103,9 @@ void HotwordServiceFactory::RegisterProfilePrefs( ...@@ -103,6 +103,9 @@ void HotwordServiceFactory::RegisterProfilePrefs(
prefs->RegisterBooleanPref(prefs::kHotwordAudioLoggingEnabled, prefs->RegisterBooleanPref(prefs::kHotwordAudioLoggingEnabled,
true, true,
user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
prefs->RegisterStringPref(prefs::kHotwordPreviousLanguage,
std::string(),
user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
} }
KeyedService* HotwordServiceFactory::BuildServiceInstanceFor( KeyedService* HotwordServiceFactory::BuildServiceInstanceFor(
......
...@@ -2,17 +2,77 @@ ...@@ -2,17 +2,77 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "base/memory/scoped_ptr.h"
#include "base/metrics/field_trial.h" #include "base/metrics/field_trial.h"
#include "base/prefs/pref_service.h" #include "base/prefs/pref_service.h"
#include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process.h"
#include "chrome/browser/extensions/extension_service_test_base.h"
#include "chrome/browser/extensions/test_extension_service.h"
#include "chrome/browser/search/hotword_service.h" #include "chrome/browser/search/hotword_service.h"
#include "chrome/browser/search/hotword_service_factory.h" #include "chrome/browser/search/hotword_service_factory.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/common/pref_names.h" #include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_profile.h" #include "chrome/test/base/testing_profile.h"
#include "content/public/test/test_browser_thread_bundle.h" #include "content/public/test/test_browser_thread_bundle.h"
#include "extensions/browser/extension_system.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_builder.h"
#include "extensions/common/manifest.h"
#include "extensions/common/one_shot_event.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
class HotwordServiceTest : public testing::Test { namespace {
class MockHotwordService : public HotwordService {
public:
explicit MockHotwordService(Profile* profile)
: HotwordService(profile),
uninstall_count_(0) {
}
virtual bool UninstallHotwordExtension(
ExtensionService* extension_service) OVERRIDE {
uninstall_count_++;
return HotwordService::UninstallHotwordExtension(extension_service);
}
virtual void InstallHotwordExtensionFromWebstore() OVERRIDE{
scoped_ptr<base::DictionaryValue> manifest =
extensions::DictionaryBuilder()
.Set("name", "Hotword Test Extension")
.Set("version", "1.0")
.Set("manifest_version", 2)
.Build();
scoped_refptr<extensions::Extension> extension =
extensions::ExtensionBuilder().SetManifest(manifest.Pass())
.AddFlags(extensions::Extension::FROM_WEBSTORE
| extensions::Extension::WAS_INSTALLED_BY_DEFAULT)
.SetID(extension_misc::kHotwordExtensionId)
.SetLocation(extensions::Manifest::EXTERNAL_COMPONENT)
.Build();
ASSERT_TRUE(extension.get());
service_->OnExtensionInstalled(extension, syncer::StringOrdinal());
}
int uninstall_count() { return uninstall_count_; }
void SetExtensionService(ExtensionService* service) { service_ = service; }
ExtensionService* extension_service() { return service_; }
private:
ExtensionService* service_;
int uninstall_count_;
};
KeyedService* BuildMockHotwordService(content::BrowserContext* context) {
return new MockHotwordService(static_cast<Profile*>(context));
}
} // namespace
class HotwordServiceTest : public extensions::ExtensionServiceTestBase {
protected: protected:
HotwordServiceTest() : field_trial_list_(NULL) {} HotwordServiceTest() : field_trial_list_(NULL) {}
virtual ~HotwordServiceTest() {} virtual ~HotwordServiceTest() {}
...@@ -28,7 +88,6 @@ class HotwordServiceTest : public testing::Test { ...@@ -28,7 +88,6 @@ class HotwordServiceTest : public testing::Test {
private: private:
base::FieldTrialList field_trial_list_; base::FieldTrialList field_trial_list_;
content::TestBrowserThreadBundle thread_bundle_;
}; };
TEST_F(HotwordServiceTest, IsHotwordAllowedBadFieldTrial) { TEST_F(HotwordServiceTest, IsHotwordAllowedBadFieldTrial) {
...@@ -126,3 +185,129 @@ TEST_F(HotwordServiceTest, AudioLoggingPrefSetCorrectly) { ...@@ -126,3 +185,129 @@ TEST_F(HotwordServiceTest, AudioLoggingPrefSetCorrectly) {
// it should return false if the preference has never been set. // it should return false if the preference has never been set.
EXPECT_FALSE(hotword_service->IsOptedIntoAudioLogging()); EXPECT_FALSE(hotword_service->IsOptedIntoAudioLogging());
} }
TEST_F(HotwordServiceTest, ShouldReinstallExtension) {
// Set the field trial to a valid one.
ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
hotword_internal::kHotwordFieldTrialName, "Install"));
InitializeEmptyExtensionService();
HotwordServiceFactory* hotword_service_factory =
HotwordServiceFactory::GetInstance();
MockHotwordService* hotword_service = static_cast<MockHotwordService*>(
hotword_service_factory->SetTestingFactoryAndUse(
profile(), BuildMockHotwordService));
EXPECT_TRUE(hotword_service != NULL);
// If no locale has been set, no reason to uninstall.
EXPECT_FALSE(hotword_service->ShouldReinstallHotwordExtension());
SetApplicationLocale(profile(), "en");
hotword_service->SetPreviousLanguagePref();
// Now a locale is set, but it hasn't changed.
EXPECT_FALSE(hotword_service->ShouldReinstallHotwordExtension());
SetApplicationLocale(profile(), "fr_fr");
// Now it's a different locale so it should uninstall.
EXPECT_TRUE(hotword_service->ShouldReinstallHotwordExtension());
}
TEST_F(HotwordServiceTest, PreviousLanguageSetOnInstall) {
// Set the field trial to a valid one.
ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
hotword_internal::kHotwordFieldTrialName, "Install"));
InitializeEmptyExtensionService();
service_->Init();
HotwordServiceFactory* hotword_service_factory =
HotwordServiceFactory::GetInstance();
MockHotwordService* hotword_service = static_cast<MockHotwordService*>(
hotword_service_factory->SetTestingFactoryAndUse(
profile(), BuildMockHotwordService));
EXPECT_TRUE(hotword_service != NULL);
hotword_service->SetExtensionService(service());
// If no locale has been set, no reason to uninstall.
EXPECT_FALSE(hotword_service->ShouldReinstallHotwordExtension());
SetApplicationLocale(profile(), "test_locale");
hotword_service->InstallHotwordExtensionFromWebstore();
base::MessageLoop::current()->RunUntilIdle();
EXPECT_EQ("test_locale",
profile()->GetPrefs()->GetString(prefs::kHotwordPreviousLanguage));
}
TEST_F(HotwordServiceTest, UninstallReinstallTriggeredCorrectly) {
// Set the field trial to a valid one.
ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
hotword_internal::kHotwordFieldTrialName, "Install"));
InitializeEmptyExtensionService();
service_->Init();
HotwordServiceFactory* hotword_service_factory =
HotwordServiceFactory::GetInstance();
MockHotwordService* hotword_service = static_cast<MockHotwordService*>(
hotword_service_factory->SetTestingFactoryAndUse(
profile(), BuildMockHotwordService));
EXPECT_TRUE(hotword_service != NULL);
hotword_service->SetExtensionService(service());
// Initialize the locale to "en".
SetApplicationLocale(profile(), "en");
// The previous locale should not be set. No reason to uninstall.
EXPECT_FALSE(hotword_service->MaybeReinstallHotwordExtension());
// Do an initial installation.
hotword_service->InstallHotwordExtensionFromWebstore();
base::MessageLoop::current()->RunUntilIdle();
EXPECT_EQ("en",
profile()->GetPrefs()->GetString(prefs::kHotwordPreviousLanguage));
// Verify the extension is installed but disabled.
EXPECT_EQ(1U, registry()->disabled_extensions().size());
EXPECT_TRUE(registry()->disabled_extensions().Contains(
extension_misc::kHotwordExtensionId));
// The previous locale should be set but should match the current
// locale. No reason to uninstall.
EXPECT_FALSE(hotword_service->MaybeReinstallHotwordExtension());
// Switch the locale to a valid but different one.
SetApplicationLocale(profile(), "fr_fr");
EXPECT_TRUE(HotwordServiceFactory::IsHotwordAllowed(profile()));
// Different but valid locale so expect uninstall.
EXPECT_TRUE(hotword_service->MaybeReinstallHotwordExtension());
EXPECT_EQ(1, hotword_service->uninstall_count());
EXPECT_EQ("fr_fr",
profile()->GetPrefs()->GetString(prefs::kHotwordPreviousLanguage));
// Verify the extension is installed. It's still disabled.
EXPECT_TRUE(registry()->disabled_extensions().Contains(
extension_misc::kHotwordExtensionId));
// Switch the locale to an invalid one.
SetApplicationLocale(profile(), "invalid");
EXPECT_FALSE(HotwordServiceFactory::IsHotwordAllowed(profile()));
EXPECT_FALSE(hotword_service->MaybeReinstallHotwordExtension());
EXPECT_EQ("fr_fr",
profile()->GetPrefs()->GetString(prefs::kHotwordPreviousLanguage));
// If the locale is set back to the last valid one, then an uninstall-install
// shouldn't be needed.
SetApplicationLocale(profile(), "fr_fr");
EXPECT_TRUE(HotwordServiceFactory::IsHotwordAllowed(profile()));
EXPECT_FALSE(hotword_service->MaybeReinstallHotwordExtension());
EXPECT_EQ(1, hotword_service->uninstall_count()); // no change
}
...@@ -1866,6 +1866,11 @@ const char kHotwordSearchEnabled[] = "hotword.search_enabled_2"; ...@@ -1866,6 +1866,11 @@ const char kHotwordSearchEnabled[] = "hotword.search_enabled_2";
// seconds of audio data before is sent back to improve voice search. // seconds of audio data before is sent back to improve voice search.
const char kHotwordAudioLoggingEnabled[] = "hotword.audio_logging_enabled"; const char kHotwordAudioLoggingEnabled[] = "hotword.audio_logging_enabled";
// A string holding the locale information under which Hotword was installed.
// It is used for comparison since the hotword voice search trigger must be
// reinstalled to handle a new language.
const char kHotwordPreviousLanguage[] = "hotword.previous_language";
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
// Boolean that controls the global enabled-state of protected media identifier. // Boolean that controls the global enabled-state of protected media identifier.
const char kProtectedMediaIdentifierEnabled[] = const char kProtectedMediaIdentifierEnabled[] =
......
...@@ -658,6 +658,7 @@ extern const char kVideoCaptureAllowedUrls[]; ...@@ -658,6 +658,7 @@ extern const char kVideoCaptureAllowedUrls[];
extern const char kHotwordSearchEnabled[]; extern const char kHotwordSearchEnabled[];
extern const char kHotwordAudioLoggingEnabled[]; extern const char kHotwordAudioLoggingEnabled[];
extern const char kHotwordPreviousLanguage[];
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
extern const char kProtectedMediaIdentifierEnabled[]; extern const char kProtectedMediaIdentifierEnabled[];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment