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)
703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#include "base/command_line.h"
85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/i18n/case_conversion.h"
95d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/metrics/field_trial.h"
105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/metrics/histogram.h"
11cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "base/path_service.h"
125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/prefs/pref_service.h"
135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/browser/browser_process.h"
14effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "chrome/browser/chrome_notification_types.h"
156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "chrome/browser/extensions/api/hotword_private/hotword_private_api.h"
165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/browser/extensions/extension_service.h"
176d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)#include "chrome/browser/extensions/pending_extension_manager.h"
186d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)#include "chrome/browser/extensions/updater/extension_updater.h"
196d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)#include "chrome/browser/extensions/webstore_startup_installer.h"
20cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "chrome/browser/plugins/plugin_prefs.h"
218bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "chrome/browser/profiles/profile.h"
22cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "chrome/browser/search/hotword_service_factory.h"
231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "chrome/browser/ui/extensions/application_launch.h"
24cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "chrome/common/chrome_paths.h"
2503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#include "chrome/common/chrome_switches.h"
265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/common/extensions/extension_constants.h"
275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/common/pref_names.h"
2803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#include "chrome/grit/generated_resources.h"
29a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "content/public/browser/browser_thread.h"
30effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "content/public/browser/notification_service.h"
31cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "content/public/browser/plugin_service.h"
32cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "content/public/common/webplugininfo.h"
335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/browser/extension_system.h"
345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "extensions/browser/uninstall_reason.h"
355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/common/extension.h"
366d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)#include "extensions/common/one_shot_event.h"
375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/base/l10n/l10n_util.h"
388bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
39010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)using extensions::BrowserContextKeyedAPIFactory;
40010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)using extensions::HotwordPrivateEventService;
41010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace {
435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
44effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch// Allowed languages for hotwording.
45effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochstatic const char* kSupportedLocales[] = {
46effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  "en",
476d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  "de",
486d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  "fr",
496d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  "ru"
50effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch};
51effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Enum describing the state of the hotword preference.
535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// This is used for UMA stats -- do not reorder or delete items; only add to
545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// the end.
555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)enum HotwordEnabled {
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  UNSET = 0,  // The hotword preference has not been set.
575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  ENABLED,    // The hotword preference is enabled.
585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DISABLED,   // The hotword preference is disabled.
595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  NUM_HOTWORD_ENABLED_METRICS
605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)};
615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Enum describing the availability state of the hotword extension.
635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// This is used for UMA stats -- do not reorder or delete items; only add to
645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// the end.
655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)enum HotwordExtensionAvailability {
665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  UNAVAILABLE = 0,
675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  AVAILABLE,
685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  PENDING_DOWNLOAD,
695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DISABLED_EXTENSION,
705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  NUM_HOTWORD_EXTENSION_AVAILABILITY_METRICS
715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)};
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
73cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Enum describing the types of errors that can arise when determining
74cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// if hotwording can be used. NO_ERROR is used so it can be seen how often
75cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// errors arise relative to when they do not.
76cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// This is used for UMA stats -- do not reorder or delete items; only add to
77cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// the end.
78cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)enum HotwordError {
79cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  NO_HOTWORD_ERROR = 0,
80cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  GENERIC_HOTWORD_ERROR,
81cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  NACL_HOTWORD_ERROR,
82cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  MICROPHONE_HOTWORD_ERROR,
83cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  NUM_HOTWORD_ERROR_METRICS
84cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)};
85cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
86cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void RecordExtensionAvailabilityMetrics(
875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    ExtensionService* service,
885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const extensions::Extension* extension) {
895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  HotwordExtensionAvailability availability_state = UNAVAILABLE;
905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (extension) {
915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    availability_state = AVAILABLE;
925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  } else if (service->pending_extension_manager() &&
935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)             service->pending_extension_manager()->IsIdPending(
945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                 extension_misc::kHotwordExtensionId)) {
955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    availability_state = PENDING_DOWNLOAD;
965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  } else if (!service->IsExtensionEnabled(
975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      extension_misc::kHotwordExtensionId)) {
985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    availability_state = DISABLED_EXTENSION;
995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  UMA_HISTOGRAM_ENUMERATION("Hotword.HotwordExtensionAvailability",
1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            availability_state,
1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            NUM_HOTWORD_EXTENSION_AVAILABILITY_METRICS);
1035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
105effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochvoid RecordLoggingMetrics(Profile* profile) {
106effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // If the user is not opted in to hotword voice search, the audio logging
107effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // metric is not valid so it is not recorded.
108effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (!profile->GetPrefs()->GetBoolean(prefs::kHotwordSearchEnabled))
109effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    return;
110effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
111effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  UMA_HISTOGRAM_BOOLEAN(
112effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      "Hotword.HotwordAudioLogging",
113effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      profile->GetPrefs()->GetBoolean(prefs::kHotwordAudioLoggingEnabled));
114effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
115effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
116cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void RecordErrorMetrics(int error_message) {
117cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  HotwordError error = NO_HOTWORD_ERROR;
118cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  switch (error_message) {
119cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    case IDS_HOTWORD_GENERIC_ERROR_MESSAGE:
120cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      error = GENERIC_HOTWORD_ERROR;
121cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      break;
122cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    case IDS_HOTWORD_NACL_DISABLED_ERROR_MESSAGE:
123cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      error = NACL_HOTWORD_ERROR;
124cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      break;
125f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    case IDS_HOTWORD_MICROPHONE_ERROR_MESSAGE:
126f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      error = MICROPHONE_HOTWORD_ERROR;
127f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      break;
128cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    default:
129cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      error = NO_HOTWORD_ERROR;
130cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
131cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
132cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  UMA_HISTOGRAM_ENUMERATION("Hotword.HotwordError",
133cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                            error,
134cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                            NUM_HOTWORD_ERROR_METRICS);
135cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
136cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
13723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)ExtensionService* GetExtensionService(Profile* profile) {
138116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
13923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
14023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  extensions::ExtensionSystem* extension_system =
14123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)      extensions::ExtensionSystem::Get(profile);
142116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  return extension_system ?  extension_system->extension_service() : NULL;
14323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)}
14423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
1456d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)std::string GetCurrentLocale(Profile* profile) {
1466d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)#if defined(OS_CHROMEOS)
14703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  std::string profile_locale =
1486d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      profile->GetPrefs()->GetString(prefs::kApplicationLocale);
14903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if (!profile_locale.empty()) {
15003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    // On ChromeOS locale is per-profile, but only if set.
15103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    return profile_locale;
15203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  }
1536d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)#endif
15403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  return g_browser_process->GetApplicationLocale();
1556d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)}
1566d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
1575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}  // namespace
1585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace hotword_internal {
1605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Constants for the hotword field trial.
1615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const char kHotwordFieldTrialName[] = "VoiceTrigger";
1625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const char kHotwordFieldTrialDisabledGroupName[] = "Disabled";
163c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// Old preference constant.
164c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochconst char kHotwordUnusablePrefName[] = "hotword.search_enabled";
1655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}  // namespace hotword_internal
1665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// static
1685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool HotwordService::DoesHotwordSupportLanguage(Profile* profile) {
1696d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  std::string normalized_locale =
1706d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      l10n_util::NormalizeLocale(GetCurrentLocale(profile));
1716e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  base::StringToLowerASCII(&normalized_locale);
172effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
173effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  for (size_t i = 0; i < arraysize(kSupportedLocales); i++) {
1746d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    if (normalized_locale.compare(0, 2, kSupportedLocales[i]) == 0)
175effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      return true;
176effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  }
177effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  return false;
1785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1798bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
18003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)// static
18103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)bool HotwordService::IsExperimentalHotwordingEnabled() {
18203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  CommandLine* command_line = CommandLine::ForCurrentProcess();
18303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  return command_line->HasSwitch(switches::kEnableExperimentalHotwording);
18403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)}
18503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
1868bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)HotwordService::HotwordService(Profile* profile)
187010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    : profile_(profile),
1886d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      extension_registry_observer_(this),
189cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      client_(NULL),
1906d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      error_message_(0),
1916d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      reinstall_pending_(false),
1926d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      weak_factory_(this) {
1936d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  extension_registry_observer_.Add(extensions::ExtensionRegistry::Get(profile));
1945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // This will be called during profile initialization which is a good time
1955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // to check the user's hotword state.
1965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  HotwordEnabled enabled_state = UNSET;
1975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (profile_->GetPrefs()->HasPrefPath(prefs::kHotwordSearchEnabled)) {
1985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (profile_->GetPrefs()->GetBoolean(prefs::kHotwordSearchEnabled))
1995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      enabled_state = ENABLED;
2005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    else
2015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      enabled_state = DISABLED;
20223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  } else {
20323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    // If the preference has not been set the hotword extension should
2040529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    // not be running. However, this should only be done if auto-install
2050529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    // is enabled which is gated through the IsHotwordAllowed check.
2060529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    if (IsHotwordAllowed())
2070529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch      DisableHotwordExtension(GetExtensionService(profile_));
2085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
2095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  UMA_HISTOGRAM_ENUMERATION("Hotword.Enabled", enabled_state,
2105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            NUM_HOTWORD_ENABLED_METRICS);
21123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
21223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  pref_registrar_.Init(profile_->GetPrefs());
21323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  pref_registrar_.Add(
21423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)      prefs::kHotwordSearchEnabled,
21523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)      base::Bind(&HotwordService::OnHotwordSearchEnabledChanged,
21623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)                 base::Unretained(this)));
217effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
218effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  registrar_.Add(this,
219f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                 chrome::NOTIFICATION_BROWSER_WINDOW_READY,
220f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                 content::NotificationService::AllSources());
221c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
2226d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  extensions::ExtensionSystem::Get(profile_)->ready().Post(
2236d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      FROM_HERE,
2246d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      base::Bind(base::IgnoreResult(
2256d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)          &HotwordService::MaybeReinstallHotwordExtension),
2266d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)                 weak_factory_.GetWeakPtr()));
2276d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
228c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  // Clear the old user pref because it became unusable.
229c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  // TODO(rlp): Remove this code per crbug.com/358789.
230c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  if (profile_->GetPrefs()->HasPrefPath(
231c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch          hotword_internal::kHotwordUnusablePrefName)) {
232c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    profile_->GetPrefs()->ClearPref(hotword_internal::kHotwordUnusablePrefName);
233c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  }
2348bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
2358bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
2368bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)HotwordService::~HotwordService() {
2378bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
2385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
239effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochvoid HotwordService::Observe(int type,
240effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                             const content::NotificationSource& source,
241effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                             const content::NotificationDetails& details) {
2426d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (type == chrome::NOTIFICATION_BROWSER_WINDOW_READY) {
243f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // The microphone monitor must be initialized as the page is loading
244f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // so that the state of the microphone is available when the page
245f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // loads. The Ok Google Hotword setting will display an error if there
246f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // is no microphone but this information will not be up-to-date unless
247f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // the monitor had already been started. Furthermore, the pop up to
248f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // opt in to hotwording won't be available if it thinks there is no
249f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // microphone. There is no hard guarantee that the monitor will actually
250f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // be up by the time it's needed, but this is the best we can do without
251f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // starting it at start up which slows down start up too much.
252f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // The content/media for microphone uses the same observer design and
253f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // makes use of the same audio device monitor.
254f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    HotwordServiceFactory::GetInstance()->UpdateMicrophoneState();
255effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  }
256effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
257effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
2586d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)void HotwordService::OnExtensionUninstalled(
2596d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    content::BrowserContext* browser_context,
2605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    const extensions::Extension* extension,
2615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    extensions::UninstallReason reason) {
2626d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
2636d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
2646d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (extension->id() != extension_misc::kHotwordExtensionId ||
2656d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      profile_ != Profile::FromBrowserContext(browser_context) ||
2666d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      !GetExtensionService(profile_))
2676d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    return;
2686d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
2696d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // If the extension wasn't uninstalled due to language change, don't try to
2706d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // reinstall it.
2716d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (!reinstall_pending_)
2726d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    return;
2736d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
2746d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  InstallHotwordExtensionFromWebstore();
2756d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  SetPreviousLanguagePref();
2766d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)}
2776d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
2786d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)void HotwordService::InstallHotwordExtensionFromWebstore() {
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)}
2866d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
2876d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)void HotwordService::OnExtensionInstalled(
2886d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    content::BrowserContext* browser_context,
289116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    const extensions::Extension* extension,
290116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    bool is_update) {
2916d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
2926d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (extension->id() != extension_misc::kHotwordExtensionId ||
2936d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      profile_ != Profile::FromBrowserContext(browser_context))
2946d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    return;
2956d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
2966d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // If the previous locale pref has never been set, set it now since
2976d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // the extension has been installed.
2986d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (!profile_->GetPrefs()->HasPrefPath(prefs::kHotwordPreviousLanguage))
2996d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    SetPreviousLanguagePref();
3006d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
3016d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // If MaybeReinstallHotwordExtension already triggered an uninstall, we
3026d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // don't want to loop and trigger another uninstall-install cycle.
3036d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // However, if we arrived here via an uninstall-triggered-install (and in
3046d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // that case |reinstall_pending_| will be true) then we know install
3056d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // has completed and we can reset |reinstall_pending_|.
3066d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (!reinstall_pending_)
3076d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    MaybeReinstallHotwordExtension();
3086d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  else
3096d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    reinstall_pending_ = false;
3106d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
3116d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // Now that the extension is installed, if the user has not selected
3126d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // the preference on, make sure it is turned off.
3136d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  //
3146d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // Disabling the extension automatically on install should only occur
3156d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // if the user is in the field trial for auto-install which is gated
3166d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // by the IsHotwordAllowed check. The check for IsHotwordAllowed() here
3176d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // can be removed once it's known that few people have manually
3186d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // installed extension.
3196d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (IsHotwordAllowed() &&
3206d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      !profile_->GetPrefs()->GetBoolean(prefs::kHotwordSearchEnabled)) {
3216d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    DisableHotwordExtension(GetExtensionService(profile_));
3226d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  }
3236d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)}
3246d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
3256d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)bool HotwordService::MaybeReinstallHotwordExtension() {
3266d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
3276d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
3286d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  ExtensionService* extension_service = GetExtensionService(profile_);
3296d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (!extension_service)
3306d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    return false;
3316d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
3326d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  const extensions::Extension* extension = extension_service->GetExtensionById(
3336d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      extension_misc::kHotwordExtensionId, true);
3346d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (!extension)
3356d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    return false;
3366d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
3376d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // If the extension is currently pending, return and we'll check again
3386d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // after the install is finished.
3396d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  extensions::PendingExtensionManager* pending_manager =
3406d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      extension_service->pending_extension_manager();
3416d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (pending_manager->IsIdPending(extension->id()))
3426d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    return false;
3436d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
3446d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // If there is already a pending request from HotwordService, don't try
3456d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // to uninstall either.
3466d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (reinstall_pending_)
3476d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    return false;
3486d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
3496d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // Check if the current locale matches the previous. If they don't match,
3506d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // uninstall the extension.
3516d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (!ShouldReinstallHotwordExtension())
3526d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    return false;
3536d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
3546d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // Ensure the call to OnExtensionUninstalled was triggered by a language
3556d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // change so it's okay to reinstall.
3566d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  reinstall_pending_ = true;
3576d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
3586d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  return UninstallHotwordExtension(extension_service);
3596d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)}
3606d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
3616d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)bool HotwordService::UninstallHotwordExtension(
3626d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    ExtensionService* extension_service) {
3636d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  base::string16 error;
3646d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (!extension_service->UninstallExtension(
365116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          extension_misc::kHotwordExtensionId,
3665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          extensions::UNINSTALL_REASON_INTERNAL_MANAGEMENT,
3675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          base::Bind(&base::DoNothing),
368116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          &error)) {
3696d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    LOG(WARNING) << "Cannot uninstall extension with id "
3706d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)                 << extension_misc::kHotwordExtensionId
3716d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)                 << ": " << error;
3726d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    reinstall_pending_ = false;
3736d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    return false;
3746d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  }
3756d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  return true;
3766d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)}
3776d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
3785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool HotwordService::IsServiceAvailable() {
379cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  error_message_ = 0;
380cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
381cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // Determine if the extension is available.
3825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  extensions::ExtensionSystem* system =
3835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      extensions::ExtensionSystem::Get(profile_);
3845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  ExtensionService* service = system->extension_service();
38523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  // Include disabled extensions (true parameter) since it may not be enabled
38623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  // if the user opted out.
3871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  std::string extensionId;
3881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (IsExperimentalHotwordingEnabled()) {
3891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // TODO(amistry): Handle reloading on language change as the old extension
3901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // does.
3911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    extensionId = extension_misc::kHotwordSharedModuleId;
3921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  } else {
3931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    extensionId = extension_misc::kHotwordExtensionId;
3941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
3955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const extensions::Extension* extension =
3961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      service->GetExtensionById(extensionId, true);
397cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (!extension)
398cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    error_message_ = IDS_HOTWORD_GENERIC_ERROR_MESSAGE;
3995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
400cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  RecordExtensionAvailabilityMetrics(service, extension);
401effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  RecordLoggingMetrics(profile_);
4025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
403cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // Determine if NaCl is available.
404cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  bool nacl_enabled = false;
405cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  base::FilePath path;
406cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (PathService::Get(chrome::FILE_NACL_PLUGIN, &path)) {
407cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    content::WebPluginInfo info;
408cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    PluginPrefs* plugin_prefs = PluginPrefs::GetForProfile(profile_).get();
409cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    if (content::PluginService::GetInstance()->GetPluginInfoByPath(path, &info))
410cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      nacl_enabled = plugin_prefs->IsPluginEnabled(info);
411cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
412cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (!nacl_enabled)
413cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    error_message_ = IDS_HOTWORD_NACL_DISABLED_ERROR_MESSAGE;
414cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
415cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  RecordErrorMetrics(error_message_);
416cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
417f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Determine if the proper audio capabilities exist.
418f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  bool audio_capture_allowed =
419f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      profile_->GetPrefs()->GetBoolean(prefs::kAudioCaptureAllowed);
420f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (!audio_capture_allowed || !HotwordServiceFactory::IsMicrophoneAvailable())
421f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    error_message_ = IDS_HOTWORD_MICROPHONE_ERROR_MESSAGE;
422f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
423cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  return (error_message_ == 0) && IsHotwordAllowed();
4245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
4255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool HotwordService::IsHotwordAllowed() {
4275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  std::string group = base::FieldTrialList::FindFullName(
4285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      hotword_internal::kHotwordFieldTrialName);
4295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return !group.empty() &&
4305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      group != hotword_internal::kHotwordFieldTrialDisabledGroupName &&
4315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      DoesHotwordSupportLanguage(profile_);
4325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
433a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
434effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochbool HotwordService::IsOptedIntoAudioLogging() {
435effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // Do not opt the user in if the preference has not been set.
436effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  return
437effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      profile_->GetPrefs()->HasPrefPath(prefs::kHotwordAudioLoggingEnabled) &&
438effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      profile_->GetPrefs()->GetBoolean(prefs::kHotwordAudioLoggingEnabled);
439effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
440effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
44123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)void HotwordService::EnableHotwordExtension(
44223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    ExtensionService* extension_service) {
44323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  if (extension_service)
44423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    extension_service->EnableExtension(extension_misc::kHotwordExtensionId);
44523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)}
44623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
44723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)void HotwordService::DisableHotwordExtension(
44823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    ExtensionService* extension_service) {
44923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  if (extension_service) {
45023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    extension_service->DisableExtension(
45123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)        extension_misc::kHotwordExtensionId,
45223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)        extensions::Extension::DISABLE_USER_ACTION);
45323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  }
45423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)}
45523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
4561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid HotwordService::LaunchHotwordAudioVerificationApp(
4571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    const LaunchMode& launch_mode) {
4581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  hotword_audio_verification_launch_mode_ = launch_mode;
4591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
4601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  ExtensionService* extension_service = GetExtensionService(profile_);
4611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (!extension_service)
4621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return;
4631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  const extensions::Extension* extension = extension_service->GetExtensionById(
4641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      extension_misc::kHotwordAudioVerificationAppId, true);
4651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (!extension)
4661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return;
4671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
4681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  OpenApplication(AppLaunchParams(
4691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      profile_, extension, extensions::LAUNCH_CONTAINER_WINDOW, NEW_WINDOW));
4701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
4711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
4721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciHotwordService::LaunchMode
4731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciHotwordService::GetHotwordAudioVerificationLaunchMode() {
4741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return hotword_audio_verification_launch_mode_;
4751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
4761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
47723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)void HotwordService::OnHotwordSearchEnabledChanged(
47823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    const std::string& pref_name) {
47923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  DCHECK_EQ(pref_name, std::string(prefs::kHotwordSearchEnabled));
48023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
48123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  ExtensionService* extension_service = GetExtensionService(profile_);
48223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  if (profile_->GetPrefs()->GetBoolean(prefs::kHotwordSearchEnabled))
48323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    EnableHotwordExtension(extension_service);
48423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  else
48523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    DisableHotwordExtension(extension_service);
48623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)}
487010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
488010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)void HotwordService::RequestHotwordSession(HotwordClient* client) {
48903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if (!IsServiceAvailable() || (client_ && client_ != client))
490010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    return;
491010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
492010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  client_ = client;
493010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
494010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  HotwordPrivateEventService* event_service =
495010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      BrowserContextKeyedAPIFactory<HotwordPrivateEventService>::Get(profile_);
496010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  if (event_service)
497010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    event_service->OnHotwordSessionRequested();
498010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
499010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
500010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)void HotwordService::StopHotwordSession(HotwordClient* client) {
501010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  if (!IsServiceAvailable())
502010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    return;
503010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
504010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  DCHECK(client_ == client);
505010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
506010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  client_ = NULL;
507010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  HotwordPrivateEventService* event_service =
508010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      BrowserContextKeyedAPIFactory<HotwordPrivateEventService>::Get(profile_);
509010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  if (event_service)
510010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    event_service->OnHotwordSessionStopped();
511010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
5126d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
5136d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)void HotwordService::SetPreviousLanguagePref() {
5146d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  profile_->GetPrefs()->SetString(prefs::kHotwordPreviousLanguage,
5156d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)                                  GetCurrentLocale(profile_));
5166d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)}
5176d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
5186d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)bool HotwordService::ShouldReinstallHotwordExtension() {
5196d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // If there is no previous locale pref, then this is the first install
5206d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // so no need to uninstall first.
5216d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (!profile_->GetPrefs()->HasPrefPath(prefs::kHotwordPreviousLanguage))
5226d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    return false;
5236d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
5246d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  std::string previous_locale =
5256d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      profile_->GetPrefs()->GetString(prefs::kHotwordPreviousLanguage);
5266d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  std::string locale = GetCurrentLocale(profile_);
5276d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
5286d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // If it's a new locale, then the old extension should be uninstalled.
5296d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  return locale != previous_locale &&
5306d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      HotwordService::DoesHotwordSupportLanguage(profile_);
5316d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)}
532