hotword_service.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
18bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved. 28bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 38bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)// found in the LICENSE file. 48bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles) 58bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "chrome/browser/search/hotword_service.h" 68bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles) 75d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/i18n/case_conversion.h" 85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/metrics/field_trial.h" 95d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/metrics/histogram.h" 105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/prefs/pref_service.h" 115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/browser/browser_process.h" 125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/browser/extensions/extension_service.h" 138bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "chrome/browser/profiles/profile.h" 145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/common/extensions/extension_constants.h" 155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/common/pref_names.h" 16a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "content/public/browser/browser_thread.h" 175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/browser/extension_system.h" 185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/common/extension.h" 195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/base/l10n/l10n_util.h" 208bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles) 215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace { 225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const int kMaxTimesToShowOptInPopup = 10; 235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Enum describing the state of the hotword preference. 255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// This is used for UMA stats -- do not reorder or delete items; only add to 265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// the end. 275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)enum HotwordEnabled { 285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) UNSET = 0, // The hotword preference has not been set. 295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ENABLED, // The hotword preference is enabled. 305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DISABLED, // The hotword preference is disabled. 315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) NUM_HOTWORD_ENABLED_METRICS 325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}; 335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Enum describing the availability state of the hotword extension. 355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// This is used for UMA stats -- do not reorder or delete items; only add to 365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// the end. 375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)enum HotwordExtensionAvailability { 385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) UNAVAILABLE = 0, 395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) AVAILABLE, 405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) PENDING_DOWNLOAD, 415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DISABLED_EXTENSION, 425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) NUM_HOTWORD_EXTENSION_AVAILABILITY_METRICS 435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}; 445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void RecordAvailabilityMetrics( 465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ExtensionService* service, 475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const extensions::Extension* extension) { 485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) HotwordExtensionAvailability availability_state = UNAVAILABLE; 495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (extension) { 505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) availability_state = AVAILABLE; 515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } else if (service->pending_extension_manager() && 525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) service->pending_extension_manager()->IsIdPending( 535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) extension_misc::kHotwordExtensionId)) { 545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) availability_state = PENDING_DOWNLOAD; 555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } else if (!service->IsExtensionEnabled( 565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) extension_misc::kHotwordExtensionId)) { 575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) availability_state = DISABLED_EXTENSION; 585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) UMA_HISTOGRAM_ENUMERATION("Hotword.HotwordExtensionAvailability", 605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) availability_state, 615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) NUM_HOTWORD_EXTENSION_AVAILABILITY_METRICS); 625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} // namespace 655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace hotword_internal { 675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Constants for the hotword field trial. 685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const char kHotwordFieldTrialName[] = "VoiceTrigger"; 695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const char kHotwordFieldTrialDisabledGroupName[] = "Disabled"; 705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} // namespace hotword_internal 715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// static 735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool HotwordService::DoesHotwordSupportLanguage(Profile* profile) { 745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) std::string locale = 755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#if defined(OS_CHROMEOS) 765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // On ChromeOS locale is per-profile. 775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) profile->GetPrefs()->GetString(prefs::kApplicationLocale); 785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#else 795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) g_browser_process->GetApplicationLocale(); 805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#endif 815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Only available for English now. 825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) std::string normalized_locale = l10n_util::NormalizeLocale(locale); 835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return normalized_locale == "en" || normalized_locale == "en_us" || 845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) normalized_locale =="en_US"; 855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 868bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles) 878bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)HotwordService::HotwordService(Profile* profile) 888bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles) : profile_(profile) { 895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // This will be called during profile initialization which is a good time 905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // to check the user's hotword state. 915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) HotwordEnabled enabled_state = UNSET; 925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (profile_->GetPrefs()->HasPrefPath(prefs::kHotwordSearchEnabled)) { 935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (profile_->GetPrefs()->GetBoolean(prefs::kHotwordSearchEnabled)) 945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) enabled_state = ENABLED; 955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) else 965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) enabled_state = DISABLED; 975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) UMA_HISTOGRAM_ENUMERATION("Hotword.Enabled", enabled_state, 995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) NUM_HOTWORD_ENABLED_METRICS); 1008bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)} 1018bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles) 1028bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)HotwordService::~HotwordService() { 1038bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)} 1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool HotwordService::ShouldShowOptInPopup() { 1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (profile_->IsOffTheRecord()) 1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return false; 1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Profile is not off the record. 1105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (profile_->GetPrefs()->HasPrefPath(prefs::kHotwordSearchEnabled)) 1115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return false; // Already opted in or opted out; 1125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) int number_shown = profile_->GetPrefs()->GetInteger( 1145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) prefs::kHotwordOptInPopupTimesShown); 1155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return number_shown < MaxNumberTimesToShowOptInPopup(); 1165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 1175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)int HotwordService::MaxNumberTimesToShowOptInPopup() { 1195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return kMaxTimesToShowOptInPopup; 1205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 1215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void HotwordService::ShowOptInPopup() { 1235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) int number_shown = profile_->GetPrefs()->GetInteger( 1245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) prefs::kHotwordOptInPopupTimesShown); 1255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) profile_->GetPrefs()->SetInteger(prefs::kHotwordOptInPopupTimesShown, 1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ++number_shown); 1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // TODO(rlp): actually show opt in popup when linked up to extension. 1285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 1295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool HotwordService::IsServiceAvailable() { 1315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) extensions::ExtensionSystem* system = 1325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) extensions::ExtensionSystem::Get(profile_); 1335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ExtensionService* service = system->extension_service(); 1345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Do not include disabled extension (false parameter) because if the 1355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // extension is disabled, it's not available. 1365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const extensions::Extension* extension = 1375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) service->GetExtensionById(extension_misc::kHotwordExtensionId, false); 1385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) RecordAvailabilityMetrics(service, extension); 1405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return extension && IsHotwordAllowed(); 1425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 1435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool HotwordService::IsHotwordAllowed() { 1455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) std::string group = base::FieldTrialList::FindFullName( 1465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) hotword_internal::kHotwordFieldTrialName); 1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return !group.empty() && 1485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) group != hotword_internal::kHotwordFieldTrialDisabledGroupName && 1495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DoesHotwordSupportLanguage(profile_); 1505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 151a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 152a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)bool HotwordService::RetryHotwordExtension() { 153a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 154a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 155a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) extensions::ExtensionSystem* extension_system = 156a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) extensions::ExtensionSystem::Get(profile_); 157a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (!extension_system || !extension_system->extension_service()) 158a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return false; 159a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) ExtensionService* extension_service = extension_system->extension_service(); 160a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 161a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) extension_service->ReloadExtension(extension_misc::kHotwordExtensionId); 162a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return true; 163a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)} 164