Revert 168224 - Update V8 to version 3.15.4.
[chromium-blink-merge.git] / chrome / browser / speech / speech_input_extension_manager.cc
blob476698dd958cb13b1b76d84c97ecb408688b206d
1 // Copyright (c) 2012 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/speech/speech_input_extension_manager.h"
7 #include "base/bind.h"
8 #include "base/json/json_writer.h"
9 #include "base/utf_string_conversions.h"
10 #include "base/values.h"
11 #include "chrome/browser/extensions/event_router.h"
12 #include "chrome/browser/extensions/extension_host.h"
13 #include "chrome/browser/extensions/extension_process_manager.h"
14 #include "chrome/browser/extensions/extension_service.h"
15 #include "chrome/browser/extensions/extension_system.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/profiles/profile_dependency_manager.h"
18 #include "chrome/browser/profiles/profile_keyed_service.h"
19 #include "chrome/browser/profiles/profile_keyed_service_factory.h"
20 #include "chrome/common/chrome_notification_types.h"
21 #include "chrome/common/extensions/extension.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/notification_registrar.h"
24 #include "content/public/browser/notification_service.h"
25 #include "content/public/browser/render_process_host.h"
26 #include "content/public/browser/speech_recognition_manager.h"
27 #include "content/public/browser/speech_recognition_session_config.h"
28 #include "content/public/browser/speech_recognition_session_context.h"
29 #include "content/public/common/speech_recognition_error.h"
30 #include "content/public/common/speech_recognition_result.h"
31 #include "net/url_request/url_request_context_getter.h"
33 using content::BrowserThread;
34 using content::SpeechRecognitionHypothesis;
35 using content::SpeechRecognitionManager;
37 namespace {
39 const char kErrorNoRecordingDeviceFound[] = "noRecordingDeviceFound";
40 const char kErrorRecordingDeviceInUse[] = "recordingDeviceInUse";
41 const char kErrorUnableToStart[] = "unableToStart";
42 const char kErrorRequestDenied[] = "requestDenied";
43 const char kErrorRequestInProgress[] = "requestInProgress";
44 const char kErrorInvalidOperation[] = "invalidOperation";
46 const char kErrorCodeKey[] = "code";
47 const char kErrorCaptureError[] = "captureError";
48 const char kErrorNetworkError[] = "networkError";
49 const char kErrorNoSpeechHeard[] = "noSpeechHeard";
50 const char kErrorNoResults[] = "noResults";
52 const char kUtteranceKey[] = "utterance";
53 const char kConfidenceKey[] = "confidence";
54 const char kHypothesesKey[] = "hypotheses";
56 const char kOnErrorEvent[] = "experimental.speechInput.onError";
57 const char kOnResultEvent[] = "experimental.speechInput.onResult";
58 const char kOnSoundStartEvent[] = "experimental.speechInput.onSoundStart";
59 const char kOnSoundEndEvent[] = "experimental.speechInput.onSoundEnd";
61 // Wrap an SpeechInputExtensionManager using scoped_refptr to avoid
62 // assertion failures on destruction because of not using release().
63 class SpeechInputExtensionManagerWrapper : public ProfileKeyedService {
64 public:
65 explicit SpeechInputExtensionManagerWrapper(
66 SpeechInputExtensionManager* manager)
67 : manager_(manager) {}
69 virtual ~SpeechInputExtensionManagerWrapper() {}
71 SpeechInputExtensionManager* manager() const { return manager_.get(); }
73 private:
74 // Methods from ProfileKeyedService.
75 virtual void Shutdown() OVERRIDE {
76 manager()->ShutdownOnUIThread();
79 scoped_refptr<SpeechInputExtensionManager> manager_;
83 // Factory for SpeechInputExtensionManagers as profile keyed services.
84 class SpeechInputExtensionManager::Factory : public ProfileKeyedServiceFactory {
85 public:
86 static void Initialize();
87 static Factory* GetInstance();
89 SpeechInputExtensionManagerWrapper* GetForProfile(Profile* profile);
91 private:
92 friend struct DefaultSingletonTraits<Factory>;
94 Factory();
95 virtual ~Factory();
97 // ProfileKeyedServiceFactory methods:
98 virtual ProfileKeyedService* BuildServiceInstanceFor(
99 Profile* profile) const OVERRIDE;
100 virtual bool ServiceRedirectedInIncognito() const OVERRIDE { return false; }
101 virtual bool ServiceIsNULLWhileTesting() const OVERRIDE { return true; }
102 virtual bool ServiceIsCreatedWithProfile() const OVERRIDE { return true; }
104 DISALLOW_COPY_AND_ASSIGN(Factory);
107 void SpeechInputExtensionManager::Factory::Initialize() {
108 GetInstance();
111 SpeechInputExtensionManager::Factory*
112 SpeechInputExtensionManager::Factory::GetInstance() {
113 return Singleton<SpeechInputExtensionManager::Factory>::get();
116 SpeechInputExtensionManagerWrapper*
117 SpeechInputExtensionManager::Factory::GetForProfile(
118 Profile* profile) {
119 return static_cast<SpeechInputExtensionManagerWrapper*>(
120 GetServiceForProfile(profile, true));
123 SpeechInputExtensionManager::Factory::Factory()
124 : ProfileKeyedServiceFactory("SpeechInputExtensionManager",
125 ProfileDependencyManager::GetInstance()) {
128 SpeechInputExtensionManager::Factory::~Factory() {
131 ProfileKeyedService*
132 SpeechInputExtensionManager::Factory::BuildServiceInstanceFor(
133 Profile* profile) const {
134 scoped_refptr<SpeechInputExtensionManager> manager(
135 new SpeechInputExtensionManager(profile));
136 return new SpeechInputExtensionManagerWrapper(manager);
139 SpeechInputExtensionInterface::SpeechInputExtensionInterface() {
142 SpeechInputExtensionInterface::~SpeechInputExtensionInterface() {
145 SpeechInputExtensionManager::SpeechInputExtensionManager(Profile* profile)
146 : profile_(profile),
147 state_(kIdle),
148 registrar_(new content::NotificationRegistrar),
149 speech_interface_(NULL),
150 is_recognition_in_progress_(false),
151 speech_recognition_session_id_(
152 SpeechRecognitionManager::kSessionIDInvalid) {
153 registrar_->Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
154 content::Source<Profile>(profile_));
157 SpeechInputExtensionManager::~SpeechInputExtensionManager() {
160 SpeechInputExtensionManager* SpeechInputExtensionManager::GetForProfile(
161 Profile* profile) {
162 SpeechInputExtensionManagerWrapper* wrapper =
163 Factory::GetInstance()->GetForProfile(profile);
164 if (!wrapper)
165 return NULL;
166 return wrapper->manager();
169 void SpeechInputExtensionManager::InitializeFactory() {
170 Factory::Initialize();
173 void SpeechInputExtensionManager::Observe(int type,
174 const content::NotificationSource& source,
175 const content::NotificationDetails& details) {
176 if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) {
177 ExtensionUnloaded(
178 content::Details<extensions::UnloadedExtensionInfo>(details)->
179 extension->id());
180 } else {
181 NOTREACHED();
185 void SpeechInputExtensionManager::ShutdownOnUIThread() {
186 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
187 VLOG(1) << "Profile shutting down.";
189 // Note: Unretained(this) is safe, also if we are freed in the meanwhile.
190 // It is used by the SR manager just for comparing the raw pointer and remove
191 // the associated sessions.
192 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
193 base::Bind(&SpeechInputExtensionManager::AbortAllSessionsOnIOThread,
194 base::Unretained(this)));
196 base::AutoLock auto_lock(state_lock_);
197 DCHECK(state_ != kShutdown);
198 if (state_ != kIdle) {
199 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
200 base::Bind(&SpeechInputExtensionManager::ForceStopOnIOThread, this));
202 state_ = kShutdown;
203 VLOG(1) << "Entering the shutdown sink state.";
204 registrar_.reset();
205 profile_ = NULL;
208 void SpeechInputExtensionManager::AbortAllSessionsOnIOThread() {
209 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
210 // TODO(primiano): The following check should not be really needed if the
211 // SpeechRecognitionManager and this class are destroyed in the correct order
212 // (this class first), as it is in current chrome implementation.
213 // However, it seems the some ChromiumOS tests violate the destruction order
214 // envisaged by browser_main_loop, so SpeechRecognitionmanager could have been
215 // freed by now.
216 if (SpeechRecognitionManager* mgr = SpeechRecognitionManager::GetInstance())
217 mgr->AbortAllSessionsForListener(this);
220 void SpeechInputExtensionManager::ExtensionUnloaded(
221 const std::string& extension_id) {
222 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
224 base::AutoLock auto_lock(state_lock_);
225 if (state_ == kShutdown)
226 return;
228 VLOG(1) << "Extension unloaded. Requesting to enforce stop...";
229 if (extension_id_in_use_ == extension_id) {
230 if (state_ != kIdle) {
231 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
232 base::Bind(&SpeechInputExtensionManager::ForceStopOnIOThread, this));
237 void SpeechInputExtensionManager::SetSpeechInputExtensionInterface(
238 SpeechInputExtensionInterface* speech_interface) {
239 speech_interface_ = speech_interface;
242 SpeechInputExtensionInterface*
243 SpeechInputExtensionManager::GetSpeechInputExtensionInterface() {
244 return speech_interface_ ? speech_interface_ : this;
247 void SpeechInputExtensionManager::ResetToIdleState() {
248 VLOG(1) << "State changed to idle. Deassociating any extensions.";
249 state_ = kIdle;
250 extension_id_in_use_.clear();
253 int SpeechInputExtensionManager::GetRenderProcessIDForExtension(
254 const std::string& extension_id) const {
255 ExtensionProcessManager* epm =
256 extensions::ExtensionSystem::Get(profile_)->process_manager();
257 DCHECK(epm);
258 extensions::ExtensionHost* eh =
259 epm->GetBackgroundHostForExtension(extension_id);
260 DCHECK(eh);
261 content::RenderProcessHost* rph = eh->render_process_host();
262 DCHECK(rph);
263 return rph->GetID();
266 void SpeechInputExtensionManager::OnRecognitionResult(
267 int session_id,
268 const content::SpeechRecognitionResult& result) {
269 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
270 DCHECK_EQ(session_id, speech_recognition_session_id_);
272 // Stopping will start the disassociation with the extension.
273 // Make a copy to report the results to the proper one.
274 std::string extension_id = extension_id_in_use_;
275 ForceStopOnIOThread();
277 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
278 base::Bind(&SpeechInputExtensionManager::SetRecognitionResultOnUIThread,
279 this, result, extension_id));
282 void SpeechInputExtensionManager::SetRecognitionResultOnUIThread(
283 const content::SpeechRecognitionResult& result,
284 const std::string& extension_id) {
285 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
287 scoped_ptr<ListValue> args(new ListValue());
288 DictionaryValue* js_event = new DictionaryValue();
289 args->Append(js_event);
291 ListValue* js_hypothesis_array = new ListValue();
292 js_event->Set(kHypothesesKey, js_hypothesis_array);
294 for (size_t i = 0; i < result.hypotheses.size(); ++i) {
295 const SpeechRecognitionHypothesis& hypothesis = result.hypotheses[i];
297 DictionaryValue* js_hypothesis_object = new DictionaryValue();
298 js_hypothesis_array->Append(js_hypothesis_object);
300 js_hypothesis_object->SetString(kUtteranceKey,
301 UTF16ToUTF8(hypothesis.utterance));
302 js_hypothesis_object->SetDouble(kConfidenceKey,
303 hypothesis.confidence);
306 DispatchEventToExtension(extension_id, kOnResultEvent, args.Pass());
309 void SpeechInputExtensionManager::OnRecognitionStart(int session_id) {
310 DCHECK_EQ(session_id, speech_recognition_session_id_);
313 void SpeechInputExtensionManager::OnAudioStart(int session_id) {
314 VLOG(1) << "OnAudioStart";
315 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
316 DCHECK_EQ(session_id, speech_recognition_session_id_);
318 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
319 base::Bind(&SpeechInputExtensionManager::DidStartReceivingAudioOnUIThread,
320 this));
323 void SpeechInputExtensionManager::OnAudioEnd(int session_id) {
326 void SpeechInputExtensionManager::OnRecognitionEnd(int session_id) {
327 // In the very exceptional case in which we requested a new recognition before
328 // the previous one ended, don't clobber the speech_recognition_session_id_.
329 if (speech_recognition_session_id_ == session_id) {
330 is_recognition_in_progress_ = false;
331 speech_recognition_session_id_ =
332 SpeechRecognitionManager::kSessionIDInvalid;
336 void SpeechInputExtensionManager::DidStartReceivingAudioOnUIThread() {
337 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
339 base::AutoLock auto_lock(state_lock_);
340 if (state_ == kShutdown)
341 return;
343 DCHECK_EQ(state_, kStarting);
344 VLOG(1) << "State changed to recording";
345 state_ = kRecording;
347 DCHECK(profile_);
349 VLOG(1) << "Sending start notification";
350 content::NotificationService::current()->Notify(
351 chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_RECORDING_STARTED,
352 content::Source<Profile>(profile_),
353 content::Details<std::string>(&extension_id_in_use_));
356 void SpeechInputExtensionManager::OnRecognitionError(
357 int session_id, const content::SpeechRecognitionError& error) {
358 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
359 DCHECK_EQ(session_id, speech_recognition_session_id_);
360 VLOG(1) << "OnRecognitionError: " << error.code;
362 base::AutoLock auto_lock(state_lock_);
363 if (state_ == kShutdown)
364 return;
366 GetSpeechInputExtensionInterface()->StopRecording(true);
368 std::string event_error_code;
369 bool report_to_event = true;
371 switch (error.code) {
372 case content::SPEECH_RECOGNITION_ERROR_NONE:
373 break;
375 case content::SPEECH_RECOGNITION_ERROR_ABORTED:
376 // ERROR_ABORTED is received whenever AbortSession is called on the
377 // manager. However, we want propagate the error only if it is triggered
378 // by an external cause (another recognition started, aborting us), thus
379 // only if it occurs while we are capturing audio.
380 if (state_ == kRecording)
381 event_error_code = kErrorCaptureError;
382 break;
384 case content::SPEECH_RECOGNITION_ERROR_AUDIO:
385 if (state_ == kStarting) {
386 event_error_code = kErrorUnableToStart;
387 report_to_event = false;
388 } else {
389 event_error_code = kErrorCaptureError;
391 break;
393 case content::SPEECH_RECOGNITION_ERROR_NETWORK:
394 event_error_code = kErrorNetworkError;
395 break;
397 case content::SPEECH_RECOGNITION_ERROR_BAD_GRAMMAR:
398 // No error is returned on invalid language, for example.
399 // To avoid confusion about when this is would be fired, the invalid
400 // params error is not being exposed to the onError event.
401 event_error_code = kErrorUnableToStart;
402 break;
404 case content::SPEECH_RECOGNITION_ERROR_NO_SPEECH:
405 event_error_code = kErrorNoSpeechHeard;
406 break;
408 case content::SPEECH_RECOGNITION_ERROR_NO_MATCH:
409 event_error_code = kErrorNoResults;
410 break;
412 // The remaining kErrorAborted case should never be returned by the server.
413 default:
414 NOTREACHED();
417 if (!event_error_code.empty()) {
418 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
419 base::Bind(&SpeechInputExtensionManager::DispatchError,
420 this, event_error_code, report_to_event));
424 void SpeechInputExtensionManager::OnEnvironmentEstimationComplete(
425 int session_id) {
426 DCHECK_EQ(session_id, speech_recognition_session_id_);
429 void SpeechInputExtensionManager::OnSoundStart(int session_id) {
430 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
431 DCHECK_EQ(session_id, speech_recognition_session_id_);
432 VLOG(1) << "OnSoundStart";
434 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
435 base::Bind(&SpeechInputExtensionManager::DispatchEventToExtension,
436 this, extension_id_in_use_, std::string(kOnSoundStartEvent),
437 Passed(scoped_ptr<ListValue>(new ListValue()))));
440 void SpeechInputExtensionManager::OnSoundEnd(int session_id) {
441 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
442 VLOG(1) << "OnSoundEnd";
444 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
445 base::Bind(&SpeechInputExtensionManager::DispatchEventToExtension,
446 this, extension_id_in_use_, std::string(kOnSoundEndEvent),
447 Passed(scoped_ptr<ListValue>(new ListValue()))));
450 void SpeechInputExtensionManager::DispatchEventToExtension(
451 const std::string& extension_id, const std::string& event,
452 scoped_ptr<ListValue> event_args) {
453 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
455 base::AutoLock auto_lock(state_lock_);
456 if (state_ == kShutdown)
457 return;
459 if (profile_ && extensions::ExtensionSystem::Get(profile_)->event_router()) {
460 extensions::ExtensionSystem::Get(profile_)->event_router()->
461 DispatchEventToExtension(extension_id, event, event_args.Pass(),
462 profile_, GURL());
466 void SpeechInputExtensionManager::DispatchError(
467 const std::string& error, bool dispatch_event) {
468 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
470 std::string extension_id;
472 base::AutoLock auto_lock(state_lock_);
473 if (state_ == kShutdown)
474 return;
476 extension_id = extension_id_in_use_;
477 ResetToIdleState();
479 // Will set the error property in the ongoing extension function calls.
480 ExtensionError details(extension_id, error);
481 content::NotificationService::current()->Notify(
482 chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_FAILED,
483 content::Source<Profile>(profile_),
484 content::Details<ExtensionError>(&details));
487 // Used for errors that are also reported via the onError event.
488 if (dispatch_event) {
489 scoped_ptr<ListValue> args(new ListValue());
490 DictionaryValue* js_error = new DictionaryValue();
491 args->Append(js_error);
492 js_error->SetString(kErrorCodeKey, error);
493 DispatchEventToExtension(extension_id, kOnErrorEvent, args.Pass());
497 bool SpeechInputExtensionManager::Start(
498 const std::string& extension_id, const std::string& language,
499 const std::string& grammar, bool filter_profanities, std::string* error) {
500 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
501 DCHECK(error);
502 VLOG(1) << "Requesting start (UI thread)";
504 base::AutoLock auto_lock(state_lock_);
505 if (state_ == kShutdown ||
506 (!extension_id_in_use_.empty() && extension_id_in_use_ != extension_id)) {
507 *error = kErrorRequestDenied;
508 return false;
511 switch (state_) {
512 case kIdle:
513 break;
515 case kStarting:
516 *error = kErrorRequestInProgress;
517 return false;
519 case kRecording:
520 case kStopping:
521 *error = kErrorInvalidOperation;
522 return false;
524 default:
525 NOTREACHED();
528 const extensions::Extension* extension = profile_->GetExtensionService()->
529 GetExtensionById(extension_id, true);
530 DCHECK(extension);
531 const std::string& extension_name = extension->name();
533 extension_id_in_use_ = extension_id;
534 VLOG(1) << "State changed to starting";
535 state_ = kStarting;
537 scoped_refptr<net::URLRequestContextGetter> url_request_context_getter =
538 profile_->GetRequestContext();
540 const int render_process_id = GetRenderProcessIDForExtension(extension_id);
542 BrowserThread::PostTask(
543 BrowserThread::IO, FROM_HERE,
544 base::Bind(&SpeechInputExtensionManager::StartOnIOThread, this,
545 url_request_context_getter, extension_name, language, grammar,
546 filter_profanities, render_process_id));
547 return true;
550 void SpeechInputExtensionManager::StartOnIOThread(
551 scoped_refptr<net::URLRequestContextGetter> context_getter,
552 const std::string& extension_name,
553 const std::string& language,
554 const std::string& grammar,
555 bool filter_profanities,
556 int render_process_id) {
557 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
558 VLOG(1) << "Requesting start (IO thread)";
560 // Everything put inside the lock to ensure the validity of context_getter,
561 // guaranteed while not in the shutdown state. Any ongoing or recognition
562 // request will be requested to be aborted when entering the shutdown state.
563 base::AutoLock auto_lock(state_lock_);
564 if (state_ == kShutdown)
565 return;
567 // TODO(primiano): These two checks below could be avoided, since they are
568 // already handled in the speech recognition classes. However, since the
569 // speech input extensions tests are bypassing the manager, we need them to
570 // pass the tests.
571 if (!GetSpeechInputExtensionInterface()->HasAudioInputDevices()) {
572 BrowserThread::PostTask(
573 BrowserThread::UI, FROM_HERE,
574 base::Bind(&SpeechInputExtensionManager::DispatchError, this,
575 std::string(kErrorNoRecordingDeviceFound), false));
576 return;
579 if (GetSpeechInputExtensionInterface()->IsCapturingAudio()) {
580 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
581 base::Bind(&SpeechInputExtensionManager::DispatchError, this,
582 std::string(kErrorRecordingDeviceInUse), false));
583 return;
586 GetSpeechInputExtensionInterface()->StartRecording(this,
587 context_getter,
588 extension_name,
589 language,
590 grammar,
591 filter_profanities,
592 render_process_id);
595 bool SpeechInputExtensionManager::HasAudioInputDevices() {
596 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
597 return SpeechRecognitionManager::GetInstance()->HasAudioInputDevices();
600 bool SpeechInputExtensionManager::IsCapturingAudio() {
601 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
602 return SpeechRecognitionManager::GetInstance()->IsCapturingAudio();
605 void SpeechInputExtensionManager::IsRecording(
606 const IsRecordingCallback& callback) {
607 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
608 BrowserThread::PostTask(
609 BrowserThread::IO, FROM_HERE,
610 base::Bind(&SpeechInputExtensionManager::IsRecordingOnIOThread,
611 this, callback));
614 void SpeechInputExtensionManager::IsRecordingOnIOThread(
615 const IsRecordingCallback& callback) {
616 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
618 bool result = GetSpeechInputExtensionInterface()->IsCapturingAudio();
620 BrowserThread::PostTask(
621 BrowserThread::UI, FROM_HERE,
622 base::Bind(&SpeechInputExtensionManager::IsRecordingOnUIThread,
623 this, callback, result));
626 void SpeechInputExtensionManager::IsRecordingOnUIThread(
627 const IsRecordingCallback& callback,
628 bool result) {
629 BrowserThread::CurrentlyOn(BrowserThread::UI);
630 callback.Run(result);
633 void SpeechInputExtensionManager::StartRecording(
634 content::SpeechRecognitionEventListener* listener,
635 net::URLRequestContextGetter* context_getter,
636 const std::string& extension_name,
637 const std::string& language,
638 const std::string& grammar,
639 bool filter_profanities,
640 int render_process_id) {
641 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
643 content::SpeechRecognitionSessionContext context;
644 context.requested_by_page_element = false;
645 context.render_process_id = render_process_id;
646 context.context_name = extension_name;
648 content::SpeechRecognitionSessionConfig config;
649 config.language = language;
650 config.grammars.push_back(content::SpeechRecognitionGrammar(grammar));
651 config.initial_context = context;
652 config.url_request_context_getter = context_getter;
653 config.filter_profanities = filter_profanities;
654 config.event_listener = listener;
656 DCHECK(!is_recognition_in_progress_);
657 SpeechRecognitionManager& manager = *SpeechRecognitionManager::GetInstance();
658 speech_recognition_session_id_ =
659 manager.CreateSession(config);
660 DCHECK_NE(speech_recognition_session_id_,
661 SpeechRecognitionManager::kSessionIDInvalid);
662 is_recognition_in_progress_ = true;
663 manager.StartSession(speech_recognition_session_id_);
666 bool SpeechInputExtensionManager::HasValidRecognizer() {
667 if (!is_recognition_in_progress_)
668 return false;
669 return SpeechRecognitionManager::GetInstance()->IsCapturingAudio();
672 bool SpeechInputExtensionManager::Stop(const std::string& extension_id,
673 std::string* error) {
674 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
675 DCHECK(error);
676 VLOG(1) << "Requesting stop (UI thread)";
678 base::AutoLock auto_lock(state_lock_);
679 if (state_ == kShutdown ||
680 (!extension_id_in_use_.empty() && extension_id_in_use_ != extension_id)) {
681 *error = kErrorRequestDenied;
682 return false;
685 switch (state_) {
686 case kRecording:
687 break;
689 case kStopping:
690 *error = kErrorRequestInProgress;
691 return false;
693 case kIdle:
694 case kStarting:
695 *error = kErrorInvalidOperation;
696 return false;
698 default:
699 NOTREACHED();
702 // Guarded by the state lock.
703 DCHECK(GetSpeechInputExtensionInterface()->HasValidRecognizer());
705 VLOG(1) << "State changed to stopping";
706 state_ = kStopping;
708 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
709 base::Bind(&SpeechInputExtensionManager::ForceStopOnIOThread, this));
710 return true;
713 void SpeechInputExtensionManager::ForceStopOnIOThread() {
714 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
715 VLOG(1) << "Requesting forced stop (IO thread)";
717 base::AutoLock auto_lock(state_lock_);
718 DCHECK(state_ != kIdle);
720 GetSpeechInputExtensionInterface()->StopRecording(false);
722 if (state_ == kShutdown)
723 return;
725 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
726 base::Bind(&SpeechInputExtensionManager::StopSucceededOnUIThread, this));
729 void SpeechInputExtensionManager::StopRecording(bool recognition_failed) {
730 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
731 if (!is_recognition_in_progress_)
732 return;
733 DCHECK_NE(speech_recognition_session_id_,
734 SpeechRecognitionManager::kSessionIDInvalid);
735 SpeechRecognitionManager::GetInstance()->AbortSession(
736 speech_recognition_session_id_);
739 void SpeechInputExtensionManager::StopSucceededOnUIThread() {
740 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
741 VLOG(1) << "Stop succeeded (UI thread)";
743 base::AutoLock auto_lock(state_lock_);
744 if (state_ == kShutdown)
745 return;
747 std::string extension_id = extension_id_in_use_;
748 ResetToIdleState();
750 content::NotificationService::current()->Notify(
751 chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_RECORDING_STOPPED,
752 // Guarded by the state_ == kShutdown check.
753 content::Source<Profile>(profile_),
754 content::Details<std::string>(&extension_id));
757 void SpeechInputExtensionManager::OnAudioLevelsChange(int session_id,
758 float volume,
759 float noise_volume) {}