1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/extensions/api/hotword_private/hotword_private_api.h"
9 #include "base/lazy_instance.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "chrome/browser/browser_process.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/browser/search/hotword_audio_history_handler.h"
15 #include "chrome/browser/search/hotword_client.h"
16 #include "chrome/browser/search/hotword_service.h"
17 #include "chrome/browser/search/hotword_service_factory.h"
18 #include "chrome/browser/ui/app_list/app_list_service.h"
19 #include "chrome/browser/ui/browser.h"
20 #include "chrome/common/pref_names.h"
21 #include "chrome/grit/chromium_strings.h"
22 #include "chrome/grit/generated_resources.h"
23 #include "content/public/browser/speech_recognition_session_preamble.h"
24 #include "extensions/browser/event_router.h"
25 #include "ui/base/l10n/l10n_util.h"
26 #include "ui/base/webui/web_ui_util.h"
28 #if defined(OS_CHROMEOS)
29 #include "ash/system/chromeos/devicetype_utils.h"
32 namespace extensions
{
34 namespace hotword_private_constants
{
35 const char kHotwordServiceUnavailable
[] = "Hotword Service is unavailable.";
36 const char kHotwordEventServiceUnavailable
[] =
37 "Hotword Private Event Service is unavailable.";
38 } // hotword_private_constants
40 namespace OnEnabledChanged
=
41 api::hotword_private::OnEnabledChanged
;
43 static base::LazyInstance
<
44 BrowserContextKeyedAPIFactory
<HotwordPrivateEventService
> > g_factory
=
45 LAZY_INSTANCE_INITIALIZER
;
47 HotwordPrivateEventService::HotwordPrivateEventService(
48 content::BrowserContext
* context
)
49 : profile_(Profile::FromBrowserContext(context
)) {
50 pref_change_registrar_
.Init(profile_
->GetPrefs());
51 pref_change_registrar_
.Add(
52 prefs::kHotwordSearchEnabled
,
53 base::Bind(&HotwordPrivateEventService::OnEnabledChanged
,
54 base::Unretained(this)));
55 pref_change_registrar_
.Add(
56 prefs::kHotwordAlwaysOnSearchEnabled
,
57 base::Bind(&HotwordPrivateEventService::OnEnabledChanged
,
58 base::Unretained(this)));
61 HotwordPrivateEventService::~HotwordPrivateEventService() {
64 void HotwordPrivateEventService::Shutdown() {
68 BrowserContextKeyedAPIFactory
<HotwordPrivateEventService
>*
69 HotwordPrivateEventService::GetFactoryInstance() {
70 return g_factory
.Pointer();
74 const char* HotwordPrivateEventService::service_name() {
75 return "HotwordPrivateEventService";
78 void HotwordPrivateEventService::OnEnabledChanged(
79 const std::string
& pref_name
) {
80 DCHECK(pref_name
== std::string(prefs::kHotwordSearchEnabled
) ||
81 pref_name
== std::string(prefs::kHotwordAlwaysOnSearchEnabled
) ||
82 pref_name
== std::string(
83 hotword_internal::kHotwordTrainingEnabled
));
84 SignalEvent(events::HOTWORD_PRIVATE_ON_ENABLED_CHANGED
,
85 OnEnabledChanged::kEventName
);
88 void HotwordPrivateEventService::OnHotwordSessionRequested() {
89 SignalEvent(events::HOTWORD_PRIVATE_ON_HOTWORD_SESSION_REQUESTED
,
90 api::hotword_private::OnHotwordSessionRequested::kEventName
);
93 void HotwordPrivateEventService::OnHotwordSessionStopped() {
94 SignalEvent(events::HOTWORD_PRIVATE_ON_HOTWORD_SESSION_STOPPED
,
95 api::hotword_private::OnHotwordSessionStopped::kEventName
);
98 void HotwordPrivateEventService::OnFinalizeSpeakerModel() {
99 SignalEvent(events::HOTWORD_PRIVATE_ON_FINALIZE_SPEAKER_MODEL
,
100 api::hotword_private::OnFinalizeSpeakerModel::kEventName
);
103 void HotwordPrivateEventService::OnSpeakerModelSaved() {
104 SignalEvent(events::HOTWORD_PRIVATE_ON_SPEAKER_MODEL_SAVED
,
105 api::hotword_private::OnSpeakerModelSaved::kEventName
);
108 void HotwordPrivateEventService::OnHotwordTriggered() {
109 SignalEvent(events::HOTWORD_PRIVATE_ON_HOTWORD_TRIGGERED
,
110 api::hotword_private::OnHotwordTriggered::kEventName
);
113 void HotwordPrivateEventService::OnDeleteSpeakerModel() {
114 SignalEvent(events::HOTWORD_PRIVATE_ON_DELETE_SPEAKER_MODEL
,
115 api::hotword_private::OnDeleteSpeakerModel::kEventName
);
118 void HotwordPrivateEventService::OnSpeakerModelExists() {
119 SignalEvent(events::HOTWORD_PRIVATE_ON_SPEAKER_MODEL_EXISTS
,
120 api::hotword_private::OnSpeakerModelExists::kEventName
);
123 void HotwordPrivateEventService::OnMicrophoneStateChanged(bool enabled
) {
124 SignalEvent(events::HOTWORD_PRIVATE_ON_MICROPHONE_STATE_CHANGED
,
125 api::hotword_private::OnMicrophoneStateChanged::kEventName
,
126 api::hotword_private::OnMicrophoneStateChanged::Create(enabled
));
129 void HotwordPrivateEventService::SignalEvent(
130 events::HistogramValue histogram_value
,
131 const std::string
& event_name
) {
132 SignalEvent(histogram_value
, event_name
,
133 make_scoped_ptr(new base::ListValue()));
136 void HotwordPrivateEventService::SignalEvent(
137 events::HistogramValue histogram_value
,
138 const std::string
& event_name
,
139 scoped_ptr
<base::ListValue
> args
) {
140 EventRouter
* router
= EventRouter::Get(profile_
);
141 if (!router
|| !router
->HasEventListener(event_name
))
144 scoped_ptr
<Event
> event(new Event(histogram_value
, event_name
, args
.Pass()));
145 router
->BroadcastEvent(event
.Pass());
148 bool HotwordPrivateSetEnabledFunction::RunSync() {
149 scoped_ptr
<api::hotword_private::SetEnabled::Params
> params(
150 api::hotword_private::SetEnabled::Params::Create(*args_
));
151 EXTENSION_FUNCTION_VALIDATE(params
.get());
153 PrefService
* prefs
= GetProfile()->GetPrefs();
154 prefs
->SetBoolean(prefs::kHotwordSearchEnabled
, params
->state
);
158 bool HotwordPrivateSetAudioLoggingEnabledFunction::RunSync() {
159 scoped_ptr
<api::hotword_private::SetAudioLoggingEnabled::Params
> params(
160 api::hotword_private::SetAudioLoggingEnabled::Params::Create(*args_
));
161 EXTENSION_FUNCTION_VALIDATE(params
.get());
163 // TODO(kcarattini): Sync the chrome pref with the account-level
164 // Audio History setting.
165 PrefService
* prefs
= GetProfile()->GetPrefs();
166 prefs
->SetBoolean(prefs::kHotwordAudioLoggingEnabled
, params
->state
);
170 bool HotwordPrivateSetHotwordAlwaysOnSearchEnabledFunction::RunSync() {
171 scoped_ptr
<api::hotword_private::SetHotwordAlwaysOnSearchEnabled::Params
>
172 params(api::hotword_private::SetHotwordAlwaysOnSearchEnabled::Params::
174 EXTENSION_FUNCTION_VALIDATE(params
.get());
176 PrefService
* prefs
= GetProfile()->GetPrefs();
177 prefs
->SetBoolean(prefs::kHotwordAlwaysOnSearchEnabled
, params
->state
);
181 bool HotwordPrivateGetStatusFunction::RunSync() {
182 scoped_ptr
<api::hotword_private::GetStatus::Params
> params(
183 api::hotword_private::GetStatus::Params::Create(*args_
));
184 EXTENSION_FUNCTION_VALIDATE(params
.get());
186 api::hotword_private::StatusDetails result
;
188 HotwordService
* hotword_service
=
189 HotwordServiceFactory::GetForProfile(GetProfile());
190 if (!hotword_service
) {
191 result
.available
= false;
192 result
.always_on_available
= false;
193 result
.enabled
= false;
194 result
.audio_logging_enabled
= false;
195 result
.always_on_enabled
= false;
196 result
.user_is_active
= false;
197 result
.hotword_hardware_available
= false;
199 result
.available
= false;
200 result
.always_on_available
= false;
201 if (params
->get_optional_fields
&& *params
->get_optional_fields
) {
202 result
.available
= hotword_service
->IsServiceAvailable();
203 result
.always_on_available
=
204 HotwordServiceFactory::IsAlwaysOnAvailable();
206 result
.enabled
= hotword_service
->IsSometimesOnEnabled();
207 result
.audio_logging_enabled
= hotword_service
->IsOptedIntoAudioLogging();
208 result
.training_enabled
= hotword_service
->IsTraining();
209 result
.always_on_enabled
= hotword_service
->IsAlwaysOnEnabled();
210 result
.user_is_active
= hotword_service
->UserIsActive();
211 result
.hotword_hardware_available
=
212 HotwordService::IsHotwordHardwareAvailable();
215 PrefService
* prefs
= GetProfile()->GetPrefs();
216 result
.enabled_set
= prefs
->HasPrefPath(prefs::kHotwordSearchEnabled
);
218 SetResult(result
.ToValue().release());
222 bool HotwordPrivateSetHotwordSessionStateFunction::RunSync() {
223 scoped_ptr
<api::hotword_private::SetHotwordSessionState::Params
> params(
224 api::hotword_private::SetHotwordSessionState::Params::Create(*args_
));
225 EXTENSION_FUNCTION_VALIDATE(params
.get());
227 HotwordService
* hotword_service
=
228 HotwordServiceFactory::GetForProfile(GetProfile());
229 if (hotword_service
&&
230 hotword_service
->client() &&
231 !hotword_service
->IsTraining())
232 hotword_service
->client()->OnHotwordStateChanged(params
->started
);
236 bool HotwordPrivateNotifyHotwordRecognitionFunction::RunSync() {
237 scoped_ptr
<api::hotword_private::NotifyHotwordRecognition::Params
> params(
238 api::hotword_private::NotifyHotwordRecognition::Params::Create(*args_
));
239 EXTENSION_FUNCTION_VALIDATE(params
.get());
241 scoped_refptr
<content::SpeechRecognitionSessionPreamble
> preamble
;
242 if (params
->log
.get() &&
243 !params
->log
->buffer
.empty() &&
244 params
->log
->channels
== 1) {
245 // TODO(amistry): Convert multi-channel preamble log into mono.
246 preamble
= new content::SpeechRecognitionSessionPreamble();
247 preamble
->sample_rate
= params
->log
->sample_rate
;
248 preamble
->sample_depth
= params
->log
->bytes_per_sample
;
249 preamble
->sample_data
.swap(params
->log
->buffer
);
252 HotwordService
* hotword_service
=
253 HotwordServiceFactory::GetForProfile(GetProfile());
254 if (hotword_service
) {
255 if (hotword_service
->IsTraining()) {
256 hotword_service
->NotifyHotwordTriggered();
257 } else if (hotword_service
->client()) {
258 hotword_service
->client()->OnHotwordRecognized(preamble
);
259 } else if (hotword_service
->IsAlwaysOnEnabled()) {
260 Browser
* browser
= GetCurrentBrowser();
261 // If a Browser does not exist, fall back to the universally available,
262 // but not recommended, way.
263 AppListService
* app_list_service
= AppListService::Get(
264 browser
? browser
->host_desktop_type() : chrome::GetActiveDesktop());
265 CHECK(app_list_service
);
266 app_list_service
->ShowForVoiceSearch(GetProfile(), preamble
);
272 bool HotwordPrivateGetLaunchStateFunction::RunSync() {
273 HotwordService
* hotword_service
=
274 HotwordServiceFactory::GetForProfile(GetProfile());
275 if (!hotword_service
) {
276 error_
= hotword_private_constants::kHotwordServiceUnavailable
;
280 api::hotword_private::LaunchState result
;
282 hotword_service
->GetHotwordAudioVerificationLaunchMode();
283 SetResult(result
.ToValue().release());
287 bool HotwordPrivateStartTrainingFunction::RunSync() {
288 HotwordService
* hotword_service
=
289 HotwordServiceFactory::GetForProfile(GetProfile());
290 if (!hotword_service
) {
291 error_
= hotword_private_constants::kHotwordServiceUnavailable
;
295 hotword_service
->StartTraining();
299 bool HotwordPrivateFinalizeSpeakerModelFunction::RunSync() {
300 HotwordService
* hotword_service
=
301 HotwordServiceFactory::GetForProfile(GetProfile());
302 if (!hotword_service
) {
303 error_
= hotword_private_constants::kHotwordServiceUnavailable
;
307 hotword_service
->FinalizeSpeakerModel();
311 bool HotwordPrivateNotifySpeakerModelSavedFunction::RunSync() {
312 HotwordPrivateEventService
* event_service
=
313 BrowserContextKeyedAPIFactory
<HotwordPrivateEventService
>::Get(
315 if (!event_service
) {
316 error_
= hotword_private_constants::kHotwordEventServiceUnavailable
;
320 event_service
->OnSpeakerModelSaved();
324 bool HotwordPrivateStopTrainingFunction::RunSync() {
325 HotwordService
* hotword_service
=
326 HotwordServiceFactory::GetForProfile(GetProfile());
327 if (!hotword_service
) {
328 error_
= hotword_private_constants::kHotwordServiceUnavailable
;
332 hotword_service
->StopTraining();
336 bool HotwordPrivateGetLocalizedStringsFunction::RunSync() {
337 #if defined(OS_CHROMEOS)
338 base::string16 device_type
= ash::GetChromeOSDeviceName();
340 base::string16 product_name
=
341 l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME
);
342 base::string16 device_type
=
343 l10n_util::GetStringFUTF16(IDS_HOTWORD_BROWSER_NAME
, product_name
);
346 base::DictionaryValue
* localized_strings
= new base::DictionaryValue();
348 localized_strings
->SetString(
350 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_CLOSE
));
351 localized_strings
->SetString(
353 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_CANCEL
));
354 localized_strings
->SetString(
356 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_INTRO_TITLE
));
357 localized_strings
->SetString(
359 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_INTRO_SUBTITLE
));
360 localized_strings
->SetString(
362 l10n_util::GetStringFUTF16(IDS_HOTWORD_OPT_IN_INTRO_DESCRIPTION
,
364 localized_strings
->SetString(
365 "introDescriptionAudioHistoryEnabled",
366 l10n_util::GetStringFUTF16(
367 IDS_HOTWORD_OPT_IN_INTRO_DESCRIPTION_AUDIO_HISTORY_ENABLED
,
369 localized_strings
->SetString(
371 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_INTRO_START
));
372 localized_strings
->SetString(
374 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_AUDIO_HISTORY_TITLE
));
375 localized_strings
->SetString(
376 "audioHistoryDescription1",
377 l10n_util::GetStringUTF16(
378 IDS_HOTWORD_OPT_IN_AUDIO_HISTORY_DESCRIPTION_1
));
379 localized_strings
->SetString(
380 "audioHistoryDescription2",
381 l10n_util::GetStringUTF16(
382 IDS_HOTWORD_OPT_IN_AUDIO_HISTORY_DESCRIPTION_2
));
383 localized_strings
->SetString(
384 "audioHistoryDescription3",
385 l10n_util::GetStringUTF16(
386 IDS_HOTWORD_OPT_IN_AUDIO_HISTORY_DESCRIPTION_3
));
387 localized_strings
->SetString(
389 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_AUDIO_HISTORY_AGREE
));
390 localized_strings
->SetString(
392 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_AUDIO_HISTORY_WAIT
));
393 localized_strings
->SetString(
395 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_ERROR
));
396 localized_strings
->SetString(
397 "trainingTitle", l10n_util::GetStringFUTF16(
398 IDS_HOTWORD_OPT_IN_TRAINING_TITLE
, device_type
));
399 localized_strings
->SetString(
400 "trainingDescription",
401 l10n_util::GetStringFUTF16(IDS_HOTWORD_OPT_IN_TRAINING_DESCRIPTION
,
403 localized_strings
->SetString(
405 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_TRAINING_SPEAK
));
406 localized_strings
->SetString(
407 "trainingFirstPrompt",
408 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_TRAINING_FIRST_PROMPT
));
409 localized_strings
->SetString(
410 "trainingMiddlePrompt",
411 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_TRAINING_MIDDLE_PROMPT
));
412 localized_strings
->SetString(
413 "trainingLastPrompt",
414 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_TRAINING_LAST_PROMPT
));
415 localized_strings
->SetString(
417 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_TRAINING_RECORDED
));
418 localized_strings
->SetString(
420 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_TRAINING_TIMEOUT
));
421 localized_strings
->SetString(
423 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_TRAINING_RETRY
));
424 localized_strings
->SetString(
426 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_FINISHED_TITLE
));
427 localized_strings
->SetString(
429 l10n_util::GetStringFUTF16(IDS_HOTWORD_OPT_IN_FINISHED_LIST_INTRO
,
431 localized_strings
->SetString(
433 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_FINISHED_LIST_ITEM_1
));
434 localized_strings
->SetString(
436 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_FINISHED_LIST_ITEM_2
));
437 localized_strings
->SetString(
439 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_FINISHED_SETTINGS
));
440 localized_strings
->SetString(
441 "finishedAudioHistory",
442 l10n_util::GetStringUTF16(
443 IDS_HOTWORD_OPT_IN_FINISHED_AUDIO_HISTORY
));
444 localized_strings
->SetString(
446 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_FINISH
));
447 localized_strings
->SetString(
449 l10n_util::GetStringUTF16(IDS_HOTWORD_OPT_IN_FINISHED_WAIT
));
451 const std::string
& app_locale
= g_browser_process
->GetApplicationLocale();
452 webui::SetLoadTimeDataDefaults(app_locale
, localized_strings
);
454 SetResult(localized_strings
);
458 bool HotwordPrivateSetAudioHistoryEnabledFunction::RunAsync() {
459 scoped_ptr
<api::hotword_private::SetAudioHistoryEnabled::Params
> params(
460 api::hotword_private::SetAudioHistoryEnabled::Params::Create(*args_
));
461 EXTENSION_FUNCTION_VALIDATE(params
.get());
463 HotwordService
* hotword_service
=
464 HotwordServiceFactory::GetForProfile(GetProfile());
465 if (!hotword_service
|| !hotword_service
->GetAudioHistoryHandler()) {
466 error_
= hotword_private_constants::kHotwordServiceUnavailable
;
470 hotword_service
->GetAudioHistoryHandler()->SetAudioHistoryEnabled(
473 &HotwordPrivateSetAudioHistoryEnabledFunction::SetResultAndSendResponse
,
478 void HotwordPrivateSetAudioHistoryEnabledFunction::SetResultAndSendResponse(
479 bool success
, bool new_enabled_value
) {
480 api::hotword_private::AudioHistoryState result
;
481 result
.success
= success
;
482 result
.enabled
= new_enabled_value
;
483 SetResult(result
.ToValue().release());
487 bool HotwordPrivateGetAudioHistoryEnabledFunction::RunAsync() {
488 HotwordService
* hotword_service
=
489 HotwordServiceFactory::GetForProfile(GetProfile());
490 if (!hotword_service
|| !hotword_service
->GetAudioHistoryHandler()) {
491 error_
= hotword_private_constants::kHotwordServiceUnavailable
;
495 hotword_service
->GetAudioHistoryHandler()->GetAudioHistoryEnabled(base::Bind(
496 &HotwordPrivateGetAudioHistoryEnabledFunction::SetResultAndSendResponse
,
502 void HotwordPrivateGetAudioHistoryEnabledFunction::SetResultAndSendResponse(
503 bool success
, bool new_enabled_value
) {
504 api::hotword_private::AudioHistoryState result
;
505 result
.success
= success
;
506 result
.enabled
= new_enabled_value
;
507 SetResult(result
.ToValue().release());
511 bool HotwordPrivateSpeakerModelExistsResultFunction::RunSync() {
512 scoped_ptr
<api::hotword_private::SpeakerModelExistsResult::Params
> params(
513 api::hotword_private::SpeakerModelExistsResult::Params::Create(*args_
));
514 EXTENSION_FUNCTION_VALIDATE(params
.get());
516 HotwordService
* hotword_service
=
517 HotwordServiceFactory::GetForProfile(GetProfile());
518 if (!hotword_service
)
521 hotword_service
->SpeakerModelExistsComplete(params
->exists
);
525 } // namespace extensions