hotword_service.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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" 165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/browser/extension_system.h" 175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/common/extension.h" 185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/base/l10n/l10n_util.h" 198bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles) 205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace { 215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const int kMaxTimesToShowOptInPopup = 10; 225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Enum describing the state of the hotword preference. 245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// This is used for UMA stats -- do not reorder or delete items; only add to 255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// the end. 265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)enum HotwordEnabled { 275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) UNSET = 0, // The hotword preference has not been set. 285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ENABLED, // The hotword preference is enabled. 295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DISABLED, // The hotword preference is disabled. 305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) NUM_HOTWORD_ENABLED_METRICS 315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}; 325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Enum describing the availability state of the hotword extension. 345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// This is used for UMA stats -- do not reorder or delete items; only add to 355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// the end. 365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)enum HotwordExtensionAvailability { 375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) UNAVAILABLE = 0, 385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) AVAILABLE, 395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) PENDING_DOWNLOAD, 405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DISABLED_EXTENSION, 415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) NUM_HOTWORD_EXTENSION_AVAILABILITY_METRICS 425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}; 435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void RecordAvailabilityMetrics( 455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ExtensionService* service, 465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const extensions::Extension* extension) { 475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) HotwordExtensionAvailability availability_state = UNAVAILABLE; 485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (extension) { 495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) availability_state = AVAILABLE; 505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } else if (service->pending_extension_manager() && 515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) service->pending_extension_manager()->IsIdPending( 525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) extension_misc::kHotwordExtensionId)) { 535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) availability_state = PENDING_DOWNLOAD; 545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } else if (!service->IsExtensionEnabled( 555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) extension_misc::kHotwordExtensionId)) { 565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) availability_state = DISABLED_EXTENSION; 575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) UMA_HISTOGRAM_ENUMERATION("Hotword.HotwordExtensionAvailability", 595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) availability_state, 605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) NUM_HOTWORD_EXTENSION_AVAILABILITY_METRICS); 615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} // namespace 645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace hotword_internal { 665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Constants for the hotword field trial. 675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const char kHotwordFieldTrialName[] = "VoiceTrigger"; 685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const char kHotwordFieldTrialDisabledGroupName[] = "Disabled"; 695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} // namespace hotword_internal 705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// static 725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool HotwordService::DoesHotwordSupportLanguage(Profile* profile) { 735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) std::string locale = 745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#if defined(OS_CHROMEOS) 755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // On ChromeOS locale is per-profile. 765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) profile->GetPrefs()->GetString(prefs::kApplicationLocale); 775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#else 785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) g_browser_process->GetApplicationLocale(); 795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#endif 805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Only available for English now. 815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) std::string normalized_locale = l10n_util::NormalizeLocale(locale); 825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return normalized_locale == "en" || normalized_locale == "en_us" || 835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) normalized_locale =="en_US"; 845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 858bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles) 868bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)HotwordService::HotwordService(Profile* profile) 878bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles) : profile_(profile) { 885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // This will be called during profile initialization which is a good time 895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // to check the user's hotword state. 905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) HotwordEnabled enabled_state = UNSET; 915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (profile_->GetPrefs()->HasPrefPath(prefs::kHotwordSearchEnabled)) { 925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (profile_->GetPrefs()->GetBoolean(prefs::kHotwordSearchEnabled)) 935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) enabled_state = ENABLED; 945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) else 955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) enabled_state = DISABLED; 965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) UMA_HISTOGRAM_ENUMERATION("Hotword.Enabled", enabled_state, 985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) NUM_HOTWORD_ENABLED_METRICS); 998bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)} 1008bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles) 1018bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)HotwordService::~HotwordService() { 1028bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)} 1035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool HotwordService::ShouldShowOptInPopup() { 1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (profile_->IsOffTheRecord()) 1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return false; 1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Profile is not off the record. 1095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (profile_->GetPrefs()->HasPrefPath(prefs::kHotwordSearchEnabled)) 1105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return false; // Already opted in or opted out; 1115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) int number_shown = profile_->GetPrefs()->GetInteger( 1135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) prefs::kHotwordOptInPopupTimesShown); 1145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return number_shown < MaxNumberTimesToShowOptInPopup(); 1155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 1165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)int HotwordService::MaxNumberTimesToShowOptInPopup() { 1185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return kMaxTimesToShowOptInPopup; 1195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 1205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void HotwordService::ShowOptInPopup() { 1225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) int number_shown = profile_->GetPrefs()->GetInteger( 1235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) prefs::kHotwordOptInPopupTimesShown); 1245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) profile_->GetPrefs()->SetInteger(prefs::kHotwordOptInPopupTimesShown, 1255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ++number_shown); 1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // TODO(rlp): actually show opt in popup when linked up to extension. 1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 1285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool HotwordService::IsServiceAvailable() { 1305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) extensions::ExtensionSystem* system = 1315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) extensions::ExtensionSystem::Get(profile_); 1325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ExtensionService* service = system->extension_service(); 1335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Do not include disabled extension (false parameter) because if the 1345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // extension is disabled, it's not available. 1355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const extensions::Extension* extension = 1365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) service->GetExtensionById(extension_misc::kHotwordExtensionId, false); 1375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) RecordAvailabilityMetrics(service, extension); 1395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return extension && IsHotwordAllowed(); 1415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 1425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool HotwordService::IsHotwordAllowed() { 1445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) std::string group = base::FieldTrialList::FindFullName( 1455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) hotword_internal::kHotwordFieldTrialName); 1465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return !group.empty() && 1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) group != hotword_internal::kHotwordFieldTrialDisabledGroupName && 1485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DoesHotwordSupportLanguage(profile_); 1495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 150