hotword_service.cc revision 116680a4aac90f2aa7413d9095a592090648e557
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"
10cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "base/path_service.h"
115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/prefs/pref_service.h"
125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/browser/browser_process.h"
13effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "chrome/browser/chrome_notification_types.h"
145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/browser/extensions/extension_service.h"
156d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)#include "chrome/browser/extensions/pending_extension_manager.h"
166d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)#include "chrome/browser/extensions/updater/extension_updater.h"
176d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)#include "chrome/browser/extensions/webstore_startup_installer.h"
18cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "chrome/browser/plugins/plugin_prefs.h"
198bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "chrome/browser/profiles/profile.h"
20cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "chrome/browser/search/hotword_service_factory.h"
21cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "chrome/common/chrome_paths.h"
225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/common/extensions/extension_constants.h"
235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/common/pref_names.h"
24a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "content/public/browser/browser_thread.h"
25effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "content/public/browser/notification_service.h"
26cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "content/public/browser/plugin_service.h"
27cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "content/public/common/webplugininfo.h"
285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/browser/extension_system.h"
295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/common/extension.h"
306d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)#include "extensions/common/one_shot_event.h"
31cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "grit/generated_resources.h"
325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/base/l10n/l10n_util.h"
338bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
34010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// The whole file relies on the extension systems but this file is built on
35010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// some non-extension supported platforms and including an API header will cause
36010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// a compile error since it depends on header files generated by .idl.
37010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// TODO(mukai): clean up file dependencies and remove this clause.
38010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#if defined(ENABLE_EXTENSIONS)
39010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#include "chrome/browser/extensions/api/hotword_private/hotword_private_api.h"
40010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#endif
41010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
42010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#if defined(ENABLE_EXTENSIONS)
43010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)using extensions::BrowserContextKeyedAPIFactory;
44010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)using extensions::HotwordPrivateEventService;
45010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#endif
46010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace {
485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
49effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch// Allowed languages for hotwording.
50effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochstatic const char* kSupportedLocales[] = {
51effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  "en",
526d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  "de",
536d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  "fr",
546d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  "ru"
55effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch};
56effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Enum describing the state of the hotword preference.
585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// This is used for UMA stats -- do not reorder or delete items; only add to
595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// the end.
605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)enum HotwordEnabled {
615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  UNSET = 0,  // The hotword preference has not been set.
625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  ENABLED,    // The hotword preference is enabled.
635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DISABLED,   // The hotword preference is disabled.
645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  NUM_HOTWORD_ENABLED_METRICS
655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)};
665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Enum describing the availability state of the hotword extension.
685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// This is used for UMA stats -- do not reorder or delete items; only add to
695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// the end.
705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)enum HotwordExtensionAvailability {
715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  UNAVAILABLE = 0,
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  AVAILABLE,
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  PENDING_DOWNLOAD,
745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DISABLED_EXTENSION,
755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  NUM_HOTWORD_EXTENSION_AVAILABILITY_METRICS
765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)};
775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
78cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Enum describing the types of errors that can arise when determining
79cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// if hotwording can be used. NO_ERROR is used so it can be seen how often
80cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// errors arise relative to when they do not.
81cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// This is used for UMA stats -- do not reorder or delete items; only add to
82cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// the end.
83cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)enum HotwordError {
84cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  NO_HOTWORD_ERROR = 0,
85cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  GENERIC_HOTWORD_ERROR,
86cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  NACL_HOTWORD_ERROR,
87cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  MICROPHONE_HOTWORD_ERROR,
88cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  NUM_HOTWORD_ERROR_METRICS
89cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)};
90cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
91cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void RecordExtensionAvailabilityMetrics(
925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    ExtensionService* service,
935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const extensions::Extension* extension) {
945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  HotwordExtensionAvailability availability_state = UNAVAILABLE;
955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (extension) {
965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    availability_state = AVAILABLE;
975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  } else if (service->pending_extension_manager() &&
985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)             service->pending_extension_manager()->IsIdPending(
995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                 extension_misc::kHotwordExtensionId)) {
1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    availability_state = PENDING_DOWNLOAD;
1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  } else if (!service->IsExtensionEnabled(
1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      extension_misc::kHotwordExtensionId)) {
1035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    availability_state = DISABLED_EXTENSION;
1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  UMA_HISTOGRAM_ENUMERATION("Hotword.HotwordExtensionAvailability",
1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            availability_state,
1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            NUM_HOTWORD_EXTENSION_AVAILABILITY_METRICS);
1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
110effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochvoid RecordLoggingMetrics(Profile* profile) {
111effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // If the user is not opted in to hotword voice search, the audio logging
112effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // metric is not valid so it is not recorded.
113effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (!profile->GetPrefs()->GetBoolean(prefs::kHotwordSearchEnabled))
114effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    return;
115effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
116effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  UMA_HISTOGRAM_BOOLEAN(
117effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      "Hotword.HotwordAudioLogging",
118effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      profile->GetPrefs()->GetBoolean(prefs::kHotwordAudioLoggingEnabled));
119effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
120effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
121cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void RecordErrorMetrics(int error_message) {
122cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  HotwordError error = NO_HOTWORD_ERROR;
123cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  switch (error_message) {
124cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    case IDS_HOTWORD_GENERIC_ERROR_MESSAGE:
125cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      error = GENERIC_HOTWORD_ERROR;
126cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      break;
127cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    case IDS_HOTWORD_NACL_DISABLED_ERROR_MESSAGE:
128cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      error = NACL_HOTWORD_ERROR;
129cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      break;
130f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    case IDS_HOTWORD_MICROPHONE_ERROR_MESSAGE:
131f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      error = MICROPHONE_HOTWORD_ERROR;
132f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      break;
133cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    default:
134cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      error = NO_HOTWORD_ERROR;
135cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
136cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
137cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  UMA_HISTOGRAM_ENUMERATION("Hotword.HotwordError",
138cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                            error,
139cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                            NUM_HOTWORD_ERROR_METRICS);
140cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
141cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
14223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)ExtensionService* GetExtensionService(Profile* profile) {
143116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
14423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
14523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  extensions::ExtensionSystem* extension_system =
14623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)      extensions::ExtensionSystem::Get(profile);
147116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  return extension_system ?  extension_system->extension_service() : NULL;
14823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)}
14923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
1506d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)std::string GetCurrentLocale(Profile* profile) {
1516d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  std::string locale =
1526d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)#if defined(OS_CHROMEOS)
1536d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      // On ChromeOS locale is per-profile.
1546d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      profile->GetPrefs()->GetString(prefs::kApplicationLocale);
1556d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)#else
1566d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      g_browser_process->GetApplicationLocale();
1576d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)#endif
1586d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  return locale;
1596d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)}
1606d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
1615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}  // namespace
1625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace hotword_internal {
1645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Constants for the hotword field trial.
1655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const char kHotwordFieldTrialName[] = "VoiceTrigger";
1665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const char kHotwordFieldTrialDisabledGroupName[] = "Disabled";
167c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// Old preference constant.
168c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochconst char kHotwordUnusablePrefName[] = "hotword.search_enabled";
1695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}  // namespace hotword_internal
1705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// static
1725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool HotwordService::DoesHotwordSupportLanguage(Profile* profile) {
1736d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  std::string normalized_locale =
1746d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      l10n_util::NormalizeLocale(GetCurrentLocale(profile));
175effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  StringToLowerASCII(&normalized_locale);
176effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
177effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  for (size_t i = 0; i < arraysize(kSupportedLocales); i++) {
1786d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    if (normalized_locale.compare(0, 2, kSupportedLocales[i]) == 0)
179effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      return true;
180effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  }
181effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  return false;
1825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1838bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
1848bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)HotwordService::HotwordService(Profile* profile)
185010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    : profile_(profile),
1866d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      extension_registry_observer_(this),
187cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      client_(NULL),
1886d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      error_message_(0),
1896d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      reinstall_pending_(false),
1906d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      weak_factory_(this) {
1916d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  extension_registry_observer_.Add(extensions::ExtensionRegistry::Get(profile));
1925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // This will be called during profile initialization which is a good time
1935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // to check the user's hotword state.
1945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  HotwordEnabled enabled_state = UNSET;
1955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (profile_->GetPrefs()->HasPrefPath(prefs::kHotwordSearchEnabled)) {
1965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (profile_->GetPrefs()->GetBoolean(prefs::kHotwordSearchEnabled))
1975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      enabled_state = ENABLED;
1985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    else
1995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      enabled_state = DISABLED;
20023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  } else {
20123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    // If the preference has not been set the hotword extension should
2020529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    // not be running. However, this should only be done if auto-install
2030529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    // is enabled which is gated through the IsHotwordAllowed check.
2040529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    if (IsHotwordAllowed())
2050529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch      DisableHotwordExtension(GetExtensionService(profile_));
2065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
2075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  UMA_HISTOGRAM_ENUMERATION("Hotword.Enabled", enabled_state,
2085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            NUM_HOTWORD_ENABLED_METRICS);
20923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
21023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  pref_registrar_.Init(profile_->GetPrefs());
21123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  pref_registrar_.Add(
21223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)      prefs::kHotwordSearchEnabled,
21323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)      base::Bind(&HotwordService::OnHotwordSearchEnabledChanged,
21423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)                 base::Unretained(this)));
215effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
216effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  registrar_.Add(this,
217f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                 chrome::NOTIFICATION_BROWSER_WINDOW_READY,
218f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                 content::NotificationService::AllSources());
219c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
2206d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  extensions::ExtensionSystem::Get(profile_)->ready().Post(
2216d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      FROM_HERE,
2226d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      base::Bind(base::IgnoreResult(
2236d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)          &HotwordService::MaybeReinstallHotwordExtension),
2246d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)                 weak_factory_.GetWeakPtr()));
2256d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
226c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  // Clear the old user pref because it became unusable.
227c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  // TODO(rlp): Remove this code per crbug.com/358789.
228c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  if (profile_->GetPrefs()->HasPrefPath(
229c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch          hotword_internal::kHotwordUnusablePrefName)) {
230c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    profile_->GetPrefs()->ClearPref(hotword_internal::kHotwordUnusablePrefName);
231c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  }
2328bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
2338bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
2348bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)HotwordService::~HotwordService() {
2358bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
2365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
237effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochvoid HotwordService::Observe(int type,
238effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                             const content::NotificationSource& source,
239effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                             const content::NotificationDetails& details) {
2406d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (type == chrome::NOTIFICATION_BROWSER_WINDOW_READY) {
241f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // The microphone monitor must be initialized as the page is loading
242f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // so that the state of the microphone is available when the page
243f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // loads. The Ok Google Hotword setting will display an error if there
244f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // is no microphone but this information will not be up-to-date unless
245f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // the monitor had already been started. Furthermore, the pop up to
246f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // opt in to hotwording won't be available if it thinks there is no
247f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // microphone. There is no hard guarantee that the monitor will actually
248f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // be up by the time it's needed, but this is the best we can do without
249f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // starting it at start up which slows down start up too much.
250f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // The content/media for microphone uses the same observer design and
251f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // makes use of the same audio device monitor.
252f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    HotwordServiceFactory::GetInstance()->UpdateMicrophoneState();
253effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  }
254effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
255effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
2566d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)void HotwordService::OnExtensionUninstalled(
2576d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    content::BrowserContext* browser_context,
2586d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    const extensions::Extension* extension) {
2596d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
2606d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
2616d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (extension->id() != extension_misc::kHotwordExtensionId ||
2626d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      profile_ != Profile::FromBrowserContext(browser_context) ||
2636d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      !GetExtensionService(profile_))
2646d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    return;
2656d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
2666d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // If the extension wasn't uninstalled due to language change, don't try to
2676d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // reinstall it.
2686d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (!reinstall_pending_)
2696d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    return;
2706d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
2716d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  InstallHotwordExtensionFromWebstore();
2726d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  SetPreviousLanguagePref();
2736d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)}
2746d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
2756d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)void HotwordService::InstallHotwordExtensionFromWebstore() {
2766d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)#if defined(ENABLE_EXTENSIONS)
2776d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  installer_ = new extensions::WebstoreStartupInstaller(
2786d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      extension_misc::kHotwordExtensionId,
2796d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      profile_,
2806d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      false,
2816d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      extensions::WebstoreStandaloneInstaller::Callback());
2826d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  installer_->BeginInstall();
2836d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)#endif
2846d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)}
2856d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
2866d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)void HotwordService::OnExtensionInstalled(
2876d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    content::BrowserContext* browser_context,
288116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    const extensions::Extension* extension,
289116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    bool is_update) {
2906d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
2916d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (extension->id() != extension_misc::kHotwordExtensionId ||
2926d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      profile_ != Profile::FromBrowserContext(browser_context))
2936d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    return;
2946d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
2956d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // If the previous locale pref has never been set, set it now since
2966d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // the extension has been installed.
2976d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (!profile_->GetPrefs()->HasPrefPath(prefs::kHotwordPreviousLanguage))
2986d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    SetPreviousLanguagePref();
2996d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
3006d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // If MaybeReinstallHotwordExtension already triggered an uninstall, we
3016d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // don't want to loop and trigger another uninstall-install cycle.
3026d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // However, if we arrived here via an uninstall-triggered-install (and in
3036d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // that case |reinstall_pending_| will be true) then we know install
3046d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // has completed and we can reset |reinstall_pending_|.
3056d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (!reinstall_pending_)
3066d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    MaybeReinstallHotwordExtension();
3076d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  else
3086d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    reinstall_pending_ = false;
3096d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
3106d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // Now that the extension is installed, if the user has not selected
3116d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // the preference on, make sure it is turned off.
3126d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  //
3136d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // Disabling the extension automatically on install should only occur
3146d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // if the user is in the field trial for auto-install which is gated
3156d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // by the IsHotwordAllowed check. The check for IsHotwordAllowed() here
3166d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // can be removed once it's known that few people have manually
3176d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // installed extension.
3186d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (IsHotwordAllowed() &&
3196d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      !profile_->GetPrefs()->GetBoolean(prefs::kHotwordSearchEnabled)) {
3206d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    DisableHotwordExtension(GetExtensionService(profile_));
3216d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  }
3226d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)}
3236d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
3246d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)bool HotwordService::MaybeReinstallHotwordExtension() {
3256d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
3266d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
3276d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  ExtensionService* extension_service = GetExtensionService(profile_);
3286d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (!extension_service)
3296d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    return false;
3306d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
3316d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  const extensions::Extension* extension = extension_service->GetExtensionById(
3326d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      extension_misc::kHotwordExtensionId, true);
3336d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (!extension)
3346d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    return false;
3356d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
3366d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // If the extension is currently pending, return and we'll check again
3376d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // after the install is finished.
3386d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  extensions::PendingExtensionManager* pending_manager =
3396d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      extension_service->pending_extension_manager();
3406d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (pending_manager->IsIdPending(extension->id()))
3416d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    return false;
3426d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
3436d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // If there is already a pending request from HotwordService, don't try
3446d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // to uninstall either.
3456d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (reinstall_pending_)
3466d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    return false;
3476d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
3486d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // Check if the current locale matches the previous. If they don't match,
3496d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // uninstall the extension.
3506d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (!ShouldReinstallHotwordExtension())
3516d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    return false;
3526d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
3536d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // Ensure the call to OnExtensionUninstalled was triggered by a language
3546d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // change so it's okay to reinstall.
3556d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  reinstall_pending_ = true;
3566d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
3576d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  return UninstallHotwordExtension(extension_service);
3586d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)}
3596d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
3606d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)bool HotwordService::UninstallHotwordExtension(
3616d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    ExtensionService* extension_service) {
3626d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  base::string16 error;
3636d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (!extension_service->UninstallExtension(
364116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          extension_misc::kHotwordExtensionId,
365116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          ExtensionService::UNINSTALL_REASON_INTERNAL_MANAGEMENT,
366116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          &error)) {
3676d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    LOG(WARNING) << "Cannot uninstall extension with id "
3686d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)                 << extension_misc::kHotwordExtensionId
3696d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)                 << ": " << error;
3706d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    reinstall_pending_ = false;
3716d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    return false;
3726d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  }
3736d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  return true;
3746d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)}
3756d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
3765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool HotwordService::IsServiceAvailable() {
377cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  error_message_ = 0;
378cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
379cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // Determine if the extension is available.
3805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  extensions::ExtensionSystem* system =
3815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      extensions::ExtensionSystem::Get(profile_);
3825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  ExtensionService* service = system->extension_service();
38323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  // Include disabled extensions (true parameter) since it may not be enabled
38423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  // if the user opted out.
3855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const extensions::Extension* extension =
38623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)      service->GetExtensionById(extension_misc::kHotwordExtensionId, true);
387cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (!extension)
388cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    error_message_ = IDS_HOTWORD_GENERIC_ERROR_MESSAGE;
3895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
390cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  RecordExtensionAvailabilityMetrics(service, extension);
391effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  RecordLoggingMetrics(profile_);
3925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
393cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // NaCl and its associated functions are not available on most mobile
394cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // platforms. ENABLE_EXTENSIONS covers those platforms and hey would not
395cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // allow Hotwording anyways since it is an extension.
396cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#if defined(ENABLE_EXTENSIONS)
397cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // Determine if NaCl is available.
398cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  bool nacl_enabled = false;
399cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  base::FilePath path;
400cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (PathService::Get(chrome::FILE_NACL_PLUGIN, &path)) {
401cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    content::WebPluginInfo info;
402cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    PluginPrefs* plugin_prefs = PluginPrefs::GetForProfile(profile_).get();
403cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    if (content::PluginService::GetInstance()->GetPluginInfoByPath(path, &info))
404cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      nacl_enabled = plugin_prefs->IsPluginEnabled(info);
405cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
406cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (!nacl_enabled)
407cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    error_message_ = IDS_HOTWORD_NACL_DISABLED_ERROR_MESSAGE;
408cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#endif
409cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
410cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  RecordErrorMetrics(error_message_);
411cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
412f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Determine if the proper audio capabilities exist.
413f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  bool audio_capture_allowed =
414f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      profile_->GetPrefs()->GetBoolean(prefs::kAudioCaptureAllowed);
415f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (!audio_capture_allowed || !HotwordServiceFactory::IsMicrophoneAvailable())
416f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    error_message_ = IDS_HOTWORD_MICROPHONE_ERROR_MESSAGE;
417f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
418cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  return (error_message_ == 0) && IsHotwordAllowed();
4195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
4205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool HotwordService::IsHotwordAllowed() {
4225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  std::string group = base::FieldTrialList::FindFullName(
4235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      hotword_internal::kHotwordFieldTrialName);
4245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return !group.empty() &&
4255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      group != hotword_internal::kHotwordFieldTrialDisabledGroupName &&
4265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      DoesHotwordSupportLanguage(profile_);
4275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
428a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
429effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochbool HotwordService::IsOptedIntoAudioLogging() {
430effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // Do not opt the user in if the preference has not been set.
431effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  return
432effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      profile_->GetPrefs()->HasPrefPath(prefs::kHotwordAudioLoggingEnabled) &&
433effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      profile_->GetPrefs()->GetBoolean(prefs::kHotwordAudioLoggingEnabled);
434effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
435effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
43623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)void HotwordService::EnableHotwordExtension(
43723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    ExtensionService* extension_service) {
43823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  if (extension_service)
43923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    extension_service->EnableExtension(extension_misc::kHotwordExtensionId);
44023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)}
44123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
44223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)void HotwordService::DisableHotwordExtension(
44323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    ExtensionService* extension_service) {
44423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  if (extension_service) {
44523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    extension_service->DisableExtension(
44623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)        extension_misc::kHotwordExtensionId,
44723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)        extensions::Extension::DISABLE_USER_ACTION);
44823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  }
44923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)}
45023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
45123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)void HotwordService::OnHotwordSearchEnabledChanged(
45223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    const std::string& pref_name) {
45323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  DCHECK_EQ(pref_name, std::string(prefs::kHotwordSearchEnabled));
45423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
45523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  ExtensionService* extension_service = GetExtensionService(profile_);
45623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  if (profile_->GetPrefs()->GetBoolean(prefs::kHotwordSearchEnabled))
45723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    EnableHotwordExtension(extension_service);
45823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  else
45923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    DisableHotwordExtension(extension_service);
46023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)}
461010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
462010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)void HotwordService::RequestHotwordSession(HotwordClient* client) {
463010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#if defined(ENABLE_EXTENSIONS)
464010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  if (!IsServiceAvailable() || client_)
465010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    return;
466010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
467010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  client_ = client;
468010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
469010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  HotwordPrivateEventService* event_service =
470010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      BrowserContextKeyedAPIFactory<HotwordPrivateEventService>::Get(profile_);
471010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  if (event_service)
472010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    event_service->OnHotwordSessionRequested();
473010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#endif
474010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
475010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
476010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)void HotwordService::StopHotwordSession(HotwordClient* client) {
477010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#if defined(ENABLE_EXTENSIONS)
478010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  if (!IsServiceAvailable())
479010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    return;
480010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
481010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  DCHECK(client_ == client);
482010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
483010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  client_ = NULL;
484010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  HotwordPrivateEventService* event_service =
485010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      BrowserContextKeyedAPIFactory<HotwordPrivateEventService>::Get(profile_);
486010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  if (event_service)
487010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    event_service->OnHotwordSessionStopped();
488010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#endif
489010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
4906d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
4916d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)void HotwordService::SetPreviousLanguagePref() {
4926d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  profile_->GetPrefs()->SetString(prefs::kHotwordPreviousLanguage,
4936d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)                                  GetCurrentLocale(profile_));
4946d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)}
4956d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
4966d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)bool HotwordService::ShouldReinstallHotwordExtension() {
4976d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // If there is no previous locale pref, then this is the first install
4986d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // so no need to uninstall first.
4996d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (!profile_->GetPrefs()->HasPrefPath(prefs::kHotwordPreviousLanguage))
5006d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    return false;
5016d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
5026d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  std::string previous_locale =
5036d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      profile_->GetPrefs()->GetString(prefs::kHotwordPreviousLanguage);
5046d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  std::string locale = GetCurrentLocale(profile_);
5056d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
5066d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // If it's a new locale, then the old extension should be uninstalled.
5076d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  return locale != previous_locale &&
5086d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      HotwordService::DoesHotwordSupportLanguage(profile_);
5096d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)}
510