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