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