Save errno for logging before potentially overwriting it.
[chromium-blink-merge.git] / content / browser / speech / speech_recognition_manager_impl.cc
blob63d4c611c6ac584d86fee2316698866dbefa1328
1 // Copyright (c) 2013 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 "content/browser/speech/speech_recognition_manager_impl.h"
7 #include "base/bind.h"
8 #include "content/browser/browser_main_loop.h"
9 #include "content/browser/renderer_host/media/media_stream_manager.h"
10 #include "content/browser/renderer_host/media/media_stream_ui_proxy.h"
11 #include "content/browser/speech/google_one_shot_remote_engine.h"
12 #include "content/browser/speech/google_streaming_remote_engine.h"
13 #include "content/browser/speech/speech_recognition_engine.h"
14 #include "content/browser/speech/speech_recognizer_impl.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "content/public/browser/content_browser_client.h"
17 #include "content/public/browser/resource_context.h"
18 #include "content/public/browser/speech_recognition_event_listener.h"
19 #include "content/public/browser/speech_recognition_manager_delegate.h"
20 #include "content/public/browser/speech_recognition_session_config.h"
21 #include "content/public/browser/speech_recognition_session_context.h"
22 #include "content/public/common/speech_recognition_error.h"
23 #include "content/public/common/speech_recognition_result.h"
24 #include "media/audio/audio_manager.h"
25 #include "media/audio/audio_manager_base.h"
27 #if defined(OS_ANDROID)
28 #include "content/browser/speech/speech_recognizer_impl_android.h"
29 #endif
31 using base::Callback;
33 namespace content {
35 SpeechRecognitionManager* SpeechRecognitionManager::manager_for_tests_;
37 namespace {
39 SpeechRecognitionManagerImpl* g_speech_recognition_manager_impl;
41 void ShowAudioInputSettingsOnFileThread(media::AudioManager* audio_manager) {
42 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
43 audio_manager->ShowAudioInputSettings();
46 } // namespace
48 SpeechRecognitionManager* SpeechRecognitionManager::GetInstance() {
49 if (manager_for_tests_)
50 return manager_for_tests_;
51 return SpeechRecognitionManagerImpl::GetInstance();
54 void SpeechRecognitionManager::SetManagerForTests(
55 SpeechRecognitionManager* manager) {
56 manager_for_tests_ = manager;
59 SpeechRecognitionManagerImpl* SpeechRecognitionManagerImpl::GetInstance() {
60 return g_speech_recognition_manager_impl;
63 SpeechRecognitionManagerImpl::SpeechRecognitionManagerImpl(
64 media::AudioManager* audio_manager,
65 MediaStreamManager* media_stream_manager)
66 : audio_manager_(audio_manager),
67 media_stream_manager_(media_stream_manager),
68 primary_session_id_(kSessionIDInvalid),
69 last_session_id_(kSessionIDInvalid),
70 is_dispatching_event_(false),
71 delegate_(GetContentClient()->browser()->
72 GetSpeechRecognitionManagerDelegate()),
73 weak_factory_(this) {
74 DCHECK(!g_speech_recognition_manager_impl);
75 g_speech_recognition_manager_impl = this;
78 SpeechRecognitionManagerImpl::~SpeechRecognitionManagerImpl() {
79 DCHECK(g_speech_recognition_manager_impl);
80 g_speech_recognition_manager_impl = NULL;
82 for (SessionsTable::iterator it = sessions_.begin(); it != sessions_.end();
83 ++it) {
84 // MediaStreamUIProxy must be deleted on the IO thread.
85 BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE,
86 it->second->ui.release());
87 delete it->second;
89 sessions_.clear();
92 int SpeechRecognitionManagerImpl::CreateSession(
93 const SpeechRecognitionSessionConfig& config) {
94 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
96 const int session_id = GetNextSessionID();
97 DCHECK(!SessionExists(session_id));
98 // Set-up the new session.
99 Session* session = new Session();
100 sessions_[session_id] = session;
101 session->id = session_id;
102 session->config = config;
103 session->context = config.initial_context;
105 std::string hardware_info;
106 bool can_report_metrics = false;
107 if (delegate_)
108 delegate_->GetDiagnosticInformation(&can_report_metrics, &hardware_info);
110 // The legacy api cannot use continuous mode.
111 DCHECK(!config.is_legacy_api || !config.continuous);
113 #if !defined(OS_ANDROID)
114 // A SpeechRecognitionEngine (and corresponding Config) is required only
115 // when using SpeechRecognizerImpl, which performs the audio capture and
116 // endpointing in the browser. This is not the case of Android where, not
117 // only the speech recognition, but also the audio capture and endpointing
118 // activities performed outside of the browser (delegated via JNI to the
119 // Android API implementation).
121 SpeechRecognitionEngineConfig remote_engine_config;
122 remote_engine_config.language = config.language;
123 remote_engine_config.grammars = config.grammars;
124 remote_engine_config.audio_sample_rate =
125 SpeechRecognizerImpl::kAudioSampleRate;
126 remote_engine_config.audio_num_bits_per_sample =
127 SpeechRecognizerImpl::kNumBitsPerAudioSample;
128 remote_engine_config.filter_profanities = config.filter_profanities;
129 remote_engine_config.continuous = config.continuous;
130 remote_engine_config.interim_results = config.interim_results;
131 remote_engine_config.max_hypotheses = config.max_hypotheses;
132 remote_engine_config.hardware_info = hardware_info;
133 remote_engine_config.origin_url =
134 can_report_metrics ? config.origin_url : std::string();
136 SpeechRecognitionEngine* google_remote_engine;
137 if (config.is_legacy_api) {
138 google_remote_engine =
139 new GoogleOneShotRemoteEngine(config.url_request_context_getter.get());
140 } else {
141 google_remote_engine = new GoogleStreamingRemoteEngine(
142 config.url_request_context_getter.get());
145 google_remote_engine->SetConfig(remote_engine_config);
147 session->recognizer = new SpeechRecognizerImpl(
148 this,
149 session_id,
150 !config.continuous,
151 google_remote_engine);
152 #else
153 session->recognizer = new SpeechRecognizerImplAndroid(this, session_id);
154 #endif
155 return session_id;
158 void SpeechRecognitionManagerImpl::StartSession(int session_id) {
159 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
160 if (!SessionExists(session_id))
161 return;
163 // If there is another active session, abort that.
164 if (primary_session_id_ != kSessionIDInvalid &&
165 primary_session_id_ != session_id) {
166 AbortSession(primary_session_id_);
169 primary_session_id_ = session_id;
171 if (delegate_) {
172 delegate_->CheckRecognitionIsAllowed(
173 session_id,
174 base::Bind(&SpeechRecognitionManagerImpl::RecognitionAllowedCallback,
175 weak_factory_.GetWeakPtr(),
176 session_id));
180 void SpeechRecognitionManagerImpl::RecognitionAllowedCallback(int session_id,
181 bool ask_user,
182 bool is_allowed) {
183 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
184 if (!SessionExists(session_id))
185 return;
187 if (ask_user) {
188 SessionsTable::iterator iter = sessions_.find(session_id);
189 DCHECK(iter != sessions_.end());
190 SpeechRecognitionSessionContext& context = iter->second->context;
191 context.label = media_stream_manager_->MakeMediaAccessRequest(
192 context.render_process_id,
193 context.render_view_id,
194 StreamOptions(MEDIA_DEVICE_AUDIO_CAPTURE, MEDIA_NO_SERVICE),
195 GURL(context.context_name),
196 base::Bind(
197 &SpeechRecognitionManagerImpl::MediaRequestPermissionCallback,
198 weak_factory_.GetWeakPtr(), session_id));
199 return;
202 if (is_allowed) {
203 base::MessageLoop::current()->PostTask(
204 FROM_HERE,
205 base::Bind(&SpeechRecognitionManagerImpl::DispatchEvent,
206 weak_factory_.GetWeakPtr(),
207 session_id,
208 EVENT_START));
209 } else {
210 OnRecognitionError(session_id, SpeechRecognitionError(
211 SPEECH_RECOGNITION_ERROR_NOT_ALLOWED));
212 base::MessageLoop::current()->PostTask(
213 FROM_HERE,
214 base::Bind(&SpeechRecognitionManagerImpl::DispatchEvent,
215 weak_factory_.GetWeakPtr(),
216 session_id,
217 EVENT_ABORT));
221 void SpeechRecognitionManagerImpl::MediaRequestPermissionCallback(
222 int session_id,
223 const MediaStreamDevices& devices,
224 scoped_ptr<MediaStreamUIProxy> stream_ui) {
225 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
227 SessionsTable::iterator iter = sessions_.find(session_id);
228 if (iter == sessions_.end())
229 return;
231 bool is_allowed = !devices.empty();
232 if (is_allowed) {
233 // Copy the approved devices array to the context for UI indication.
234 iter->second->context.devices = devices;
236 // Save the UI object.
237 iter->second->ui = stream_ui.Pass();
240 // Clear the label to indicate the request has been done.
241 iter->second->context.label.clear();
243 // Notify the recognition about the request result.
244 RecognitionAllowedCallback(iter->first, false, is_allowed);
247 void SpeechRecognitionManagerImpl::AbortSession(int session_id) {
248 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
249 if (!SessionExists(session_id))
250 return;
252 SessionsTable::iterator iter = sessions_.find(session_id);
253 iter->second->ui.reset();
255 base::MessageLoop::current()->PostTask(
256 FROM_HERE,
257 base::Bind(&SpeechRecognitionManagerImpl::DispatchEvent,
258 weak_factory_.GetWeakPtr(),
259 session_id,
260 EVENT_ABORT));
263 void SpeechRecognitionManagerImpl::StopAudioCaptureForSession(int session_id) {
264 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
265 if (!SessionExists(session_id))
266 return;
268 SessionsTable::iterator iter = sessions_.find(session_id);
269 iter->second->ui.reset();
271 base::MessageLoop::current()->PostTask(
272 FROM_HERE,
273 base::Bind(&SpeechRecognitionManagerImpl::DispatchEvent,
274 weak_factory_.GetWeakPtr(),
275 session_id,
276 EVENT_STOP_CAPTURE));
279 // Here begins the SpeechRecognitionEventListener interface implementation,
280 // which will simply relay the events to the proper listener registered for the
281 // particular session (most likely InputTagSpeechDispatcherHost) and to the
282 // catch-all listener provided by the delegate (if any).
284 void SpeechRecognitionManagerImpl::OnRecognitionStart(int session_id) {
285 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
286 if (!SessionExists(session_id))
287 return;
289 SessionsTable::iterator iter = sessions_.find(session_id);
290 if (iter->second->ui) {
291 // Notify the UI that the devices are being used.
292 iter->second->ui->OnStarted(base::Closure());
295 DCHECK_EQ(primary_session_id_, session_id);
296 if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener())
297 delegate_listener->OnRecognitionStart(session_id);
298 if (SpeechRecognitionEventListener* listener = GetListener(session_id))
299 listener->OnRecognitionStart(session_id);
302 void SpeechRecognitionManagerImpl::OnAudioStart(int session_id) {
303 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
304 if (!SessionExists(session_id))
305 return;
307 DCHECK_EQ(primary_session_id_, session_id);
308 if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener())
309 delegate_listener->OnAudioStart(session_id);
310 if (SpeechRecognitionEventListener* listener = GetListener(session_id))
311 listener->OnAudioStart(session_id);
314 void SpeechRecognitionManagerImpl::OnEnvironmentEstimationComplete(
315 int session_id) {
316 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
317 if (!SessionExists(session_id))
318 return;
320 DCHECK_EQ(primary_session_id_, session_id);
321 if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener())
322 delegate_listener->OnEnvironmentEstimationComplete(session_id);
323 if (SpeechRecognitionEventListener* listener = GetListener(session_id))
324 listener->OnEnvironmentEstimationComplete(session_id);
327 void SpeechRecognitionManagerImpl::OnSoundStart(int session_id) {
328 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
329 if (!SessionExists(session_id))
330 return;
332 DCHECK_EQ(primary_session_id_, session_id);
333 if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener())
334 delegate_listener->OnSoundStart(session_id);
335 if (SpeechRecognitionEventListener* listener = GetListener(session_id))
336 listener->OnSoundStart(session_id);
339 void SpeechRecognitionManagerImpl::OnSoundEnd(int session_id) {
340 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
341 if (!SessionExists(session_id))
342 return;
344 if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener())
345 delegate_listener->OnSoundEnd(session_id);
346 if (SpeechRecognitionEventListener* listener = GetListener(session_id))
347 listener->OnSoundEnd(session_id);
350 void SpeechRecognitionManagerImpl::OnAudioEnd(int session_id) {
351 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
352 if (!SessionExists(session_id))
353 return;
355 if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener())
356 delegate_listener->OnAudioEnd(session_id);
357 if (SpeechRecognitionEventListener* listener = GetListener(session_id))
358 listener->OnAudioEnd(session_id);
359 base::MessageLoop::current()->PostTask(
360 FROM_HERE,
361 base::Bind(&SpeechRecognitionManagerImpl::DispatchEvent,
362 weak_factory_.GetWeakPtr(),
363 session_id,
364 EVENT_AUDIO_ENDED));
367 void SpeechRecognitionManagerImpl::OnRecognitionResults(
368 int session_id, const SpeechRecognitionResults& results) {
369 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
370 if (!SessionExists(session_id))
371 return;
373 if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener())
374 delegate_listener->OnRecognitionResults(session_id, results);
375 if (SpeechRecognitionEventListener* listener = GetListener(session_id))
376 listener->OnRecognitionResults(session_id, results);
379 void SpeechRecognitionManagerImpl::OnRecognitionError(
380 int session_id, const SpeechRecognitionError& error) {
381 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
382 if (!SessionExists(session_id))
383 return;
385 if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener())
386 delegate_listener->OnRecognitionError(session_id, error);
387 if (SpeechRecognitionEventListener* listener = GetListener(session_id))
388 listener->OnRecognitionError(session_id, error);
391 void SpeechRecognitionManagerImpl::OnAudioLevelsChange(
392 int session_id, float volume, float noise_volume) {
393 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
394 if (!SessionExists(session_id))
395 return;
397 if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener())
398 delegate_listener->OnAudioLevelsChange(session_id, volume, noise_volume);
399 if (SpeechRecognitionEventListener* listener = GetListener(session_id))
400 listener->OnAudioLevelsChange(session_id, volume, noise_volume);
403 void SpeechRecognitionManagerImpl::OnRecognitionEnd(int session_id) {
404 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
405 if (!SessionExists(session_id))
406 return;
408 if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener())
409 delegate_listener->OnRecognitionEnd(session_id);
410 if (SpeechRecognitionEventListener* listener = GetListener(session_id))
411 listener->OnRecognitionEnd(session_id);
412 base::MessageLoop::current()->PostTask(
413 FROM_HERE,
414 base::Bind(&SpeechRecognitionManagerImpl::DispatchEvent,
415 weak_factory_.GetWeakPtr(),
416 session_id,
417 EVENT_RECOGNITION_ENDED));
420 int SpeechRecognitionManagerImpl::GetSession(
421 int render_process_id, int render_view_id, int request_id) const {
422 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
423 SessionsTable::const_iterator iter;
424 for(iter = sessions_.begin(); iter != sessions_.end(); ++iter) {
425 const int session_id = iter->first;
426 const SpeechRecognitionSessionContext& context = iter->second->context;
427 if (context.render_process_id == render_process_id &&
428 context.render_view_id == render_view_id &&
429 context.request_id == request_id) {
430 return session_id;
433 return kSessionIDInvalid;
436 SpeechRecognitionSessionContext
437 SpeechRecognitionManagerImpl::GetSessionContext(int session_id) const {
438 return GetSession(session_id)->context;
441 void SpeechRecognitionManagerImpl::AbortAllSessionsForListener(
442 SpeechRecognitionEventListener* listener) {
443 // This method gracefully destroys sessions for the listener. However, since
444 // the listener itself is likely to be destroyed after this call, we avoid
445 // dispatching further events to it, marking the |listener_is_active| flag.
446 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
447 for (SessionsTable::iterator it = sessions_.begin(); it != sessions_.end();
448 ++it) {
449 Session* session = it->second;
450 if (session->config.event_listener == listener) {
451 AbortSession(session->id);
452 session->listener_is_active = false;
457 void SpeechRecognitionManagerImpl::AbortAllSessionsForRenderView(
458 int render_process_id,
459 int render_view_id) {
460 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
461 for (SessionsTable::iterator it = sessions_.begin(); it != sessions_.end();
462 ++it) {
463 Session* session = it->second;
464 if (session->context.render_process_id == render_process_id &&
465 session->context.render_view_id == render_view_id) {
466 AbortSession(session->id);
471 // ----------------------- Core FSM implementation ---------------------------
472 void SpeechRecognitionManagerImpl::DispatchEvent(int session_id,
473 FSMEvent event) {
474 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
476 // There are some corner cases in which the session might be deleted (due to
477 // an EndRecognition event) between a request (e.g. Abort) and its dispatch.
478 if (!SessionExists(session_id))
479 return;
481 Session* session = GetSession(session_id);
482 FSMState session_state = GetSessionState(session_id);
483 DCHECK_LE(session_state, SESSION_STATE_MAX_VALUE);
484 DCHECK_LE(event, EVENT_MAX_VALUE);
486 // Event dispatching must be sequential, otherwise it will break all the rules
487 // and the assumptions of the finite state automata model.
488 DCHECK(!is_dispatching_event_);
489 is_dispatching_event_ = true;
490 ExecuteTransitionAndGetNextState(session, session_state, event);
491 is_dispatching_event_ = false;
494 // This FSM handles the evolution of each session, from the viewpoint of the
495 // interaction with the user (that may be either the browser end-user which
496 // interacts with UI bubbles, or JS developer intracting with JS methods).
497 // All the events received by the SpeechRecognizer instances (one for each
498 // session) are always routed to the SpeechRecognitionEventListener(s)
499 // regardless the choices taken in this FSM.
500 void SpeechRecognitionManagerImpl::ExecuteTransitionAndGetNextState(
501 Session* session, FSMState session_state, FSMEvent event) {
502 // Note: since we're not tracking the state of the recognizer object, rather
503 // we're directly retrieving it (through GetSessionState), we see its events
504 // (that are AUDIO_ENDED and RECOGNITION_ENDED) after its state evolution
505 // (e.g., when we receive the AUDIO_ENDED event, the recognizer has just
506 // completed the transition from CAPTURING_AUDIO to WAITING_FOR_RESULT, thus
507 // we perceive the AUDIO_ENDED event in WAITING_FOR_RESULT).
508 // This makes the code below a bit tricky but avoids a lot of code for
509 // tracking and reconstructing asynchronously the state of the recognizer.
510 switch (session_state) {
511 case SESSION_STATE_IDLE:
512 switch (event) {
513 case EVENT_START:
514 return SessionStart(*session);
515 case EVENT_ABORT:
516 return SessionAbort(*session);
517 case EVENT_RECOGNITION_ENDED:
518 return SessionDelete(session);
519 case EVENT_STOP_CAPTURE:
520 return SessionStopAudioCapture(*session);
521 case EVENT_AUDIO_ENDED:
522 return;
524 break;
525 case SESSION_STATE_CAPTURING_AUDIO:
526 switch (event) {
527 case EVENT_STOP_CAPTURE:
528 return SessionStopAudioCapture(*session);
529 case EVENT_ABORT:
530 return SessionAbort(*session);
531 case EVENT_START:
532 return;
533 case EVENT_AUDIO_ENDED:
534 case EVENT_RECOGNITION_ENDED:
535 return NotFeasible(*session, event);
537 break;
538 case SESSION_STATE_WAITING_FOR_RESULT:
539 switch (event) {
540 case EVENT_ABORT:
541 return SessionAbort(*session);
542 case EVENT_AUDIO_ENDED:
543 return ResetCapturingSessionId(*session);
544 case EVENT_START:
545 case EVENT_STOP_CAPTURE:
546 return;
547 case EVENT_RECOGNITION_ENDED:
548 return NotFeasible(*session, event);
550 break;
552 return NotFeasible(*session, event);
555 SpeechRecognitionManagerImpl::FSMState
556 SpeechRecognitionManagerImpl::GetSessionState(int session_id) const {
557 Session* session = GetSession(session_id);
558 if (!session->recognizer.get() || !session->recognizer->IsActive())
559 return SESSION_STATE_IDLE;
560 if (session->recognizer->IsCapturingAudio())
561 return SESSION_STATE_CAPTURING_AUDIO;
562 return SESSION_STATE_WAITING_FOR_RESULT;
565 // ----------- Contract for all the FSM evolution functions below -------------
566 // - Are guaranteed to be executed in the IO thread;
567 // - Are guaranteed to be not reentrant (themselves and each other);
569 void SpeechRecognitionManagerImpl::SessionStart(const Session& session) {
570 DCHECK_EQ(primary_session_id_, session.id);
571 const MediaStreamDevices& devices = session.context.devices;
572 std::string device_id;
573 if (devices.empty()) {
574 // From the ask_user=false path, use the default device.
575 // TODO(xians): Abort the session after we do not need to support this path
576 // anymore.
577 device_id = media::AudioManagerBase::kDefaultDeviceId;
578 } else {
579 // From the ask_user=true path, use the selected device.
580 DCHECK_EQ(1u, devices.size());
581 DCHECK_EQ(MEDIA_DEVICE_AUDIO_CAPTURE, devices.front().type);
582 device_id = devices.front().id;
585 session.recognizer->StartRecognition(device_id);
588 void SpeechRecognitionManagerImpl::SessionAbort(const Session& session) {
589 if (primary_session_id_ == session.id)
590 primary_session_id_ = kSessionIDInvalid;
591 DCHECK(session.recognizer.get());
592 session.recognizer->AbortRecognition();
595 void SpeechRecognitionManagerImpl::SessionStopAudioCapture(
596 const Session& session) {
597 DCHECK(session.recognizer.get());
598 session.recognizer->StopAudioCapture();
601 void SpeechRecognitionManagerImpl::ResetCapturingSessionId(
602 const Session& session) {
603 DCHECK_EQ(primary_session_id_, session.id);
604 primary_session_id_ = kSessionIDInvalid;
607 void SpeechRecognitionManagerImpl::SessionDelete(Session* session) {
608 DCHECK(session->recognizer.get() == NULL || !session->recognizer->IsActive());
609 if (primary_session_id_ == session->id)
610 primary_session_id_ = kSessionIDInvalid;
611 sessions_.erase(session->id);
612 delete session;
615 void SpeechRecognitionManagerImpl::NotFeasible(const Session& session,
616 FSMEvent event) {
617 NOTREACHED() << "Unfeasible event " << event
618 << " in state " << GetSessionState(session.id)
619 << " for session " << session.id;
622 int SpeechRecognitionManagerImpl::GetNextSessionID() {
623 ++last_session_id_;
624 // Deal with wrapping of last_session_id_. (How civilized).
625 if (last_session_id_ <= 0)
626 last_session_id_ = 1;
627 return last_session_id_;
630 bool SpeechRecognitionManagerImpl::SessionExists(int session_id) const {
631 return sessions_.find(session_id) != sessions_.end();
634 SpeechRecognitionManagerImpl::Session*
635 SpeechRecognitionManagerImpl::GetSession(int session_id) const {
636 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
637 SessionsTable::const_iterator iter = sessions_.find(session_id);
638 DCHECK(iter != sessions_.end());
639 return iter->second;
642 SpeechRecognitionEventListener* SpeechRecognitionManagerImpl::GetListener(
643 int session_id) const {
644 Session* session = GetSession(session_id);
645 return session->listener_is_active ? session->config.event_listener : NULL;
648 SpeechRecognitionEventListener*
649 SpeechRecognitionManagerImpl::GetDelegateListener() const {
650 return delegate_.get() ? delegate_->GetEventListener() : NULL;
653 const SpeechRecognitionSessionConfig&
654 SpeechRecognitionManagerImpl::GetSessionConfig(int session_id) const {
655 return GetSession(session_id)->config;
658 bool SpeechRecognitionManagerImpl::HasAudioInputDevices() {
659 return audio_manager_->HasAudioInputDevices();
662 string16 SpeechRecognitionManagerImpl::GetAudioInputDeviceModel() {
663 return audio_manager_->GetAudioInputDeviceModel();
666 void SpeechRecognitionManagerImpl::ShowAudioInputSettings() {
667 // Since AudioManager::ShowAudioInputSettings can potentially launch external
668 // processes, do that in the FILE thread to not block the calling threads.
669 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
670 base::Bind(&ShowAudioInputSettingsOnFileThread,
671 audio_manager_));
674 SpeechRecognitionManagerImpl::Session::Session()
675 : id(kSessionIDInvalid),
676 listener_is_active(true) {
679 SpeechRecognitionManagerImpl::Session::~Session() {
682 } // namespace content