- add sources.
[platform/framework/web/crosswalk.git] / src / content / browser / speech / speech_recognition_manager_impl.cc
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.
4
5 #include "content/browser/speech/speech_recognition_manager_impl.h"
6
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"
26
27 #if defined(OS_ANDROID)
28 #include "content/browser/speech/speech_recognizer_impl_android.h"
29 #endif
30
31 using base::Callback;
32
33 namespace content {
34
35 SpeechRecognitionManager* SpeechRecognitionManager::manager_for_tests_;
36
37 namespace {
38
39 SpeechRecognitionManagerImpl* g_speech_recognition_manager_impl;
40
41 void ShowAudioInputSettingsOnFileThread(media::AudioManager* audio_manager) {
42   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
43   audio_manager->ShowAudioInputSettings();
44 }
45
46 }  // namespace
47
48 SpeechRecognitionManager* SpeechRecognitionManager::GetInstance() {
49   if (manager_for_tests_)
50     return manager_for_tests_;
51   return SpeechRecognitionManagerImpl::GetInstance();
52 }
53
54 void SpeechRecognitionManager::SetManagerForTesting(
55     SpeechRecognitionManager* manager) {
56   manager_for_tests_ = manager;
57 }
58
59 SpeechRecognitionManagerImpl* SpeechRecognitionManagerImpl::GetInstance() {
60   return g_speech_recognition_manager_impl;
61 }
62
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;
76 }
77
78 SpeechRecognitionManagerImpl::~SpeechRecognitionManagerImpl() {
79   DCHECK(g_speech_recognition_manager_impl);
80   g_speech_recognition_manager_impl = NULL;
81
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;
88   }
89   sessions_.clear();
90 }
91
92 int SpeechRecognitionManagerImpl::CreateSession(
93     const SpeechRecognitionSessionConfig& config) {
94   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
95
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;
104
105   std::string hardware_info;
106   bool can_report_metrics = false;
107   if (delegate_)
108     delegate_->GetDiagnosticInformation(&can_report_metrics, &hardware_info);
109
110   // The legacy api cannot use continuous mode.
111   DCHECK(!config.is_legacy_api || !config.continuous);
112
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).
120
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();
135
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());
143   }
144
145   google_remote_engine->SetConfig(remote_engine_config);
146
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;
156 }
157
158 void SpeechRecognitionManagerImpl::StartSession(int session_id) {
159   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
160   if (!SessionExists(session_id))
161     return;
162
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_);
167   }
168
169   primary_session_id_ = session_id;
170
171   if (delegate_) {
172     delegate_->CheckRecognitionIsAllowed(
173         session_id,
174         base::Bind(&SpeechRecognitionManagerImpl::RecognitionAllowedCallback,
175                    weak_factory_.GetWeakPtr(),
176                    session_id));
177   }
178 }
179
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;
186
187   SessionsTable::iterator iter = sessions_.find(session_id);
188   DCHECK(iter != sessions_.end());
189   Session* session = iter->second;
190
191   if (session->abort_requested)
192     return;
193
194   if (ask_user) {
195     SpeechRecognitionSessionContext& context = session->context;
196     context.label = media_stream_manager_->MakeMediaAccessRequest(
197         context.render_process_id,
198         context.render_view_id,
199         context.request_id,
200         StreamOptions(MEDIA_DEVICE_AUDIO_CAPTURE, MEDIA_NO_SERVICE),
201         GURL(context.context_name),
202         base::Bind(
203             &SpeechRecognitionManagerImpl::MediaRequestPermissionCallback,
204             weak_factory_.GetWeakPtr(), session_id));
205     return;
206   }
207
208   if (is_allowed) {
209     base::MessageLoop::current()->PostTask(
210         FROM_HERE,
211         base::Bind(&SpeechRecognitionManagerImpl::DispatchEvent,
212                    weak_factory_.GetWeakPtr(),
213                    session_id,
214                    EVENT_START));
215   } else {
216     OnRecognitionError(session_id, SpeechRecognitionError(
217         SPEECH_RECOGNITION_ERROR_NOT_ALLOWED));
218     base::MessageLoop::current()->PostTask(
219         FROM_HERE,
220         base::Bind(&SpeechRecognitionManagerImpl::DispatchEvent,
221                    weak_factory_.GetWeakPtr(),
222                    session_id,
223                    EVENT_ABORT));
224   }
225 }
226
227 void SpeechRecognitionManagerImpl::MediaRequestPermissionCallback(
228     int session_id,
229     const MediaStreamDevices& devices,
230     scoped_ptr<MediaStreamUIProxy> stream_ui) {
231   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
232
233   SessionsTable::iterator iter = sessions_.find(session_id);
234   if (iter == sessions_.end())
235     return;
236
237   bool is_allowed = !devices.empty();
238   if (is_allowed) {
239     // Copy the approved devices array to the context for UI indication.
240     iter->second->context.devices = devices;
241
242     // Save the UI object.
243     iter->second->ui = stream_ui.Pass();
244   }
245
246   // Clear the label to indicate the request has been done.
247   iter->second->context.label.clear();
248
249   // Notify the recognition about the request result.
250   RecognitionAllowedCallback(iter->first, false, is_allowed);
251 }
252
253 void SpeechRecognitionManagerImpl::AbortSession(int session_id) {
254   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
255   if (!SessionExists(session_id))
256     return;
257
258   SessionsTable::iterator iter = sessions_.find(session_id);
259   iter->second->ui.reset();
260
261   if (iter->second->abort_requested)
262     return;
263
264   iter->second->abort_requested = true;
265
266   base::MessageLoop::current()->PostTask(
267       FROM_HERE,
268       base::Bind(&SpeechRecognitionManagerImpl::DispatchEvent,
269                  weak_factory_.GetWeakPtr(),
270                  session_id,
271                  EVENT_ABORT));
272 }
273
274 void SpeechRecognitionManagerImpl::StopAudioCaptureForSession(int session_id) {
275   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
276   if (!SessionExists(session_id))
277     return;
278
279   SessionsTable::iterator iter = sessions_.find(session_id);
280   iter->second->ui.reset();
281
282   base::MessageLoop::current()->PostTask(
283       FROM_HERE,
284       base::Bind(&SpeechRecognitionManagerImpl::DispatchEvent,
285                  weak_factory_.GetWeakPtr(),
286                  session_id,
287                  EVENT_STOP_CAPTURE));
288 }
289
290 // Here begins the SpeechRecognitionEventListener interface implementation,
291 // which will simply relay the events to the proper listener registered for the
292 // particular session (most likely InputTagSpeechDispatcherHost) and to the
293 // catch-all listener provided by the delegate (if any).
294
295 void SpeechRecognitionManagerImpl::OnRecognitionStart(int session_id) {
296   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
297   if (!SessionExists(session_id))
298     return;
299
300   SessionsTable::iterator iter = sessions_.find(session_id);
301   if (iter->second->ui) {
302     // Notify the UI that the devices are being used.
303     iter->second->ui->OnStarted(base::Closure());
304   }
305
306   DCHECK_EQ(primary_session_id_, session_id);
307   if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener())
308     delegate_listener->OnRecognitionStart(session_id);
309   if (SpeechRecognitionEventListener* listener = GetListener(session_id))
310     listener->OnRecognitionStart(session_id);
311 }
312
313 void SpeechRecognitionManagerImpl::OnAudioStart(int session_id) {
314   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
315   if (!SessionExists(session_id))
316     return;
317
318   DCHECK_EQ(primary_session_id_, session_id);
319   if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener())
320     delegate_listener->OnAudioStart(session_id);
321   if (SpeechRecognitionEventListener* listener = GetListener(session_id))
322     listener->OnAudioStart(session_id);
323 }
324
325 void SpeechRecognitionManagerImpl::OnEnvironmentEstimationComplete(
326     int session_id) {
327   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
328   if (!SessionExists(session_id))
329     return;
330
331   DCHECK_EQ(primary_session_id_, session_id);
332   if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener())
333     delegate_listener->OnEnvironmentEstimationComplete(session_id);
334   if (SpeechRecognitionEventListener* listener = GetListener(session_id))
335     listener->OnEnvironmentEstimationComplete(session_id);
336 }
337
338 void SpeechRecognitionManagerImpl::OnSoundStart(int session_id) {
339   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
340   if (!SessionExists(session_id))
341     return;
342
343   DCHECK_EQ(primary_session_id_, session_id);
344   if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener())
345     delegate_listener->OnSoundStart(session_id);
346   if (SpeechRecognitionEventListener* listener = GetListener(session_id))
347     listener->OnSoundStart(session_id);
348 }
349
350 void SpeechRecognitionManagerImpl::OnSoundEnd(int session_id) {
351   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
352   if (!SessionExists(session_id))
353     return;
354
355   if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener())
356     delegate_listener->OnSoundEnd(session_id);
357   if (SpeechRecognitionEventListener* listener = GetListener(session_id))
358     listener->OnSoundEnd(session_id);
359 }
360
361 void SpeechRecognitionManagerImpl::OnAudioEnd(int session_id) {
362   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
363   if (!SessionExists(session_id))
364     return;
365
366   if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener())
367     delegate_listener->OnAudioEnd(session_id);
368   if (SpeechRecognitionEventListener* listener = GetListener(session_id))
369     listener->OnAudioEnd(session_id);
370   base::MessageLoop::current()->PostTask(
371       FROM_HERE,
372       base::Bind(&SpeechRecognitionManagerImpl::DispatchEvent,
373                  weak_factory_.GetWeakPtr(),
374                  session_id,
375                  EVENT_AUDIO_ENDED));
376 }
377
378 void SpeechRecognitionManagerImpl::OnRecognitionResults(
379     int session_id, const SpeechRecognitionResults& results) {
380   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
381   if (!SessionExists(session_id))
382     return;
383
384   if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener())
385     delegate_listener->OnRecognitionResults(session_id, results);
386   if (SpeechRecognitionEventListener* listener = GetListener(session_id))
387     listener->OnRecognitionResults(session_id, results);
388 }
389
390 void SpeechRecognitionManagerImpl::OnRecognitionError(
391     int session_id, const SpeechRecognitionError& error) {
392   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
393   if (!SessionExists(session_id))
394     return;
395
396   if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener())
397     delegate_listener->OnRecognitionError(session_id, error);
398   if (SpeechRecognitionEventListener* listener = GetListener(session_id))
399     listener->OnRecognitionError(session_id, error);
400 }
401
402 void SpeechRecognitionManagerImpl::OnAudioLevelsChange(
403     int session_id, float volume, float noise_volume) {
404   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
405   if (!SessionExists(session_id))
406     return;
407
408   if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener())
409     delegate_listener->OnAudioLevelsChange(session_id, volume, noise_volume);
410   if (SpeechRecognitionEventListener* listener = GetListener(session_id))
411     listener->OnAudioLevelsChange(session_id, volume, noise_volume);
412 }
413
414 void SpeechRecognitionManagerImpl::OnRecognitionEnd(int session_id) {
415   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
416   if (!SessionExists(session_id))
417     return;
418
419   if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener())
420     delegate_listener->OnRecognitionEnd(session_id);
421   if (SpeechRecognitionEventListener* listener = GetListener(session_id))
422     listener->OnRecognitionEnd(session_id);
423   base::MessageLoop::current()->PostTask(
424       FROM_HERE,
425       base::Bind(&SpeechRecognitionManagerImpl::DispatchEvent,
426                  weak_factory_.GetWeakPtr(),
427                  session_id,
428                  EVENT_RECOGNITION_ENDED));
429 }
430
431 int SpeechRecognitionManagerImpl::GetSession(
432     int render_process_id, int render_view_id, int request_id) const {
433   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
434   SessionsTable::const_iterator iter;
435   for(iter = sessions_.begin(); iter != sessions_.end(); ++iter) {
436     const int session_id = iter->first;
437     const SpeechRecognitionSessionContext& context = iter->second->context;
438     if (context.render_process_id == render_process_id &&
439         context.render_view_id == render_view_id &&
440         context.request_id == request_id) {
441       return session_id;
442     }
443   }
444   return kSessionIDInvalid;
445 }
446
447 SpeechRecognitionSessionContext
448 SpeechRecognitionManagerImpl::GetSessionContext(int session_id) const {
449   return GetSession(session_id)->context;
450 }
451
452 void SpeechRecognitionManagerImpl::AbortAllSessionsForListener(
453     SpeechRecognitionEventListener* listener) {
454   // This method gracefully destroys sessions for the listener. However, since
455   // the listener itself is likely to be destroyed after this call, we avoid
456   // dispatching further events to it, marking the |listener_is_active| flag.
457   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
458   for (SessionsTable::iterator it = sessions_.begin(); it != sessions_.end();
459        ++it) {
460     Session* session = it->second;
461     if (session->config.event_listener == listener) {
462       AbortSession(session->id);
463       session->listener_is_active = false;
464     }
465   }
466 }
467
468 void SpeechRecognitionManagerImpl::AbortAllSessionsForRenderView(
469     int render_process_id,
470     int render_view_id) {
471   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
472   for (SessionsTable::iterator it = sessions_.begin(); it != sessions_.end();
473        ++it) {
474     Session* session = it->second;
475     if (session->context.render_process_id == render_process_id &&
476         session->context.render_view_id == render_view_id) {
477       AbortSession(session->id);
478     }
479   }
480 }
481
482 // -----------------------  Core FSM implementation ---------------------------
483 void SpeechRecognitionManagerImpl::DispatchEvent(int session_id,
484                                                  FSMEvent event) {
485   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
486
487   // There are some corner cases in which the session might be deleted (due to
488   // an EndRecognition event) between a request (e.g. Abort) and its dispatch.
489   if (!SessionExists(session_id))
490     return;
491
492   Session* session = GetSession(session_id);
493   FSMState session_state = GetSessionState(session_id);
494   DCHECK_LE(session_state, SESSION_STATE_MAX_VALUE);
495   DCHECK_LE(event, EVENT_MAX_VALUE);
496
497   // Event dispatching must be sequential, otherwise it will break all the rules
498   // and the assumptions of the finite state automata model.
499   DCHECK(!is_dispatching_event_);
500   is_dispatching_event_ = true;
501   ExecuteTransitionAndGetNextState(session, session_state, event);
502   is_dispatching_event_ = false;
503 }
504
505 // This FSM handles the evolution of each session, from the viewpoint of the
506 // interaction with the user (that may be either the browser end-user which
507 // interacts with UI bubbles, or JS developer intracting with JS methods).
508 // All the events received by the SpeechRecognizer instances (one for each
509 // session) are always routed to the SpeechRecognitionEventListener(s)
510 // regardless the choices taken in this FSM.
511 void SpeechRecognitionManagerImpl::ExecuteTransitionAndGetNextState(
512     Session* session, FSMState session_state, FSMEvent event) {
513   // Note: since we're not tracking the state of the recognizer object, rather
514   // we're directly retrieving it (through GetSessionState), we see its events
515   // (that are AUDIO_ENDED and RECOGNITION_ENDED) after its state evolution
516   // (e.g., when we receive the AUDIO_ENDED event, the recognizer has just
517   // completed the transition from CAPTURING_AUDIO to WAITING_FOR_RESULT, thus
518   // we perceive the AUDIO_ENDED event in WAITING_FOR_RESULT).
519   // This makes the code below a bit tricky but avoids a lot of code for
520   // tracking and reconstructing asynchronously the state of the recognizer.
521   switch (session_state) {
522     case SESSION_STATE_IDLE:
523       switch (event) {
524         case EVENT_START:
525           return SessionStart(*session);
526         case EVENT_ABORT:
527           return SessionAbort(*session);
528         case EVENT_RECOGNITION_ENDED:
529           return SessionDelete(session);
530         case EVENT_STOP_CAPTURE:
531           return SessionStopAudioCapture(*session);
532         case EVENT_AUDIO_ENDED:
533           return;
534       }
535       break;
536     case SESSION_STATE_CAPTURING_AUDIO:
537       switch (event) {
538         case EVENT_STOP_CAPTURE:
539           return SessionStopAudioCapture(*session);
540         case EVENT_ABORT:
541           return SessionAbort(*session);
542         case EVENT_START:
543           return;
544         case EVENT_AUDIO_ENDED:
545         case EVENT_RECOGNITION_ENDED:
546           return NotFeasible(*session, event);
547       }
548       break;
549     case SESSION_STATE_WAITING_FOR_RESULT:
550       switch (event) {
551         case EVENT_ABORT:
552           return SessionAbort(*session);
553         case EVENT_AUDIO_ENDED:
554           return ResetCapturingSessionId(*session);
555         case EVENT_START:
556         case EVENT_STOP_CAPTURE:
557           return;
558         case EVENT_RECOGNITION_ENDED:
559           return NotFeasible(*session, event);
560       }
561       break;
562   }
563   return NotFeasible(*session, event);
564 }
565
566 SpeechRecognitionManagerImpl::FSMState
567 SpeechRecognitionManagerImpl::GetSessionState(int session_id) const {
568   Session* session = GetSession(session_id);
569   if (!session->recognizer.get() || !session->recognizer->IsActive())
570     return SESSION_STATE_IDLE;
571   if (session->recognizer->IsCapturingAudio())
572     return SESSION_STATE_CAPTURING_AUDIO;
573   return SESSION_STATE_WAITING_FOR_RESULT;
574 }
575
576 // ----------- Contract for all the FSM evolution functions below -------------
577 //  - Are guaranteed to be executed in the IO thread;
578 //  - Are guaranteed to be not reentrant (themselves and each other);
579
580 void SpeechRecognitionManagerImpl::SessionStart(const Session& session) {
581   DCHECK_EQ(primary_session_id_, session.id);
582   const MediaStreamDevices& devices = session.context.devices;
583   std::string device_id;
584   if (devices.empty()) {
585     // From the ask_user=false path, use the default device.
586     // TODO(xians): Abort the session after we do not need to support this path
587     // anymore.
588     device_id = media::AudioManagerBase::kDefaultDeviceId;
589   } else {
590     // From the ask_user=true path, use the selected device.
591     DCHECK_EQ(1u, devices.size());
592     DCHECK_EQ(MEDIA_DEVICE_AUDIO_CAPTURE, devices.front().type);
593     device_id = devices.front().id;
594   }
595
596   session.recognizer->StartRecognition(device_id);
597 }
598
599 void SpeechRecognitionManagerImpl::SessionAbort(const Session& session) {
600   if (primary_session_id_ == session.id)
601     primary_session_id_ = kSessionIDInvalid;
602   DCHECK(session.recognizer.get());
603   session.recognizer->AbortRecognition();
604 }
605
606 void SpeechRecognitionManagerImpl::SessionStopAudioCapture(
607     const Session& session) {
608   DCHECK(session.recognizer.get());
609   session.recognizer->StopAudioCapture();
610 }
611
612 void SpeechRecognitionManagerImpl::ResetCapturingSessionId(
613     const Session& session) {
614   DCHECK_EQ(primary_session_id_, session.id);
615   primary_session_id_ = kSessionIDInvalid;
616 }
617
618 void SpeechRecognitionManagerImpl::SessionDelete(Session* session) {
619   DCHECK(session->recognizer.get() == NULL || !session->recognizer->IsActive());
620   if (primary_session_id_ == session->id)
621     primary_session_id_ = kSessionIDInvalid;
622   if (!session->context.label.empty())
623     media_stream_manager_->CancelRequest(session->context.label);
624   sessions_.erase(session->id);
625   delete session;
626 }
627
628 void SpeechRecognitionManagerImpl::NotFeasible(const Session& session,
629                                                FSMEvent event) {
630   NOTREACHED() << "Unfeasible event " << event
631                << " in state " << GetSessionState(session.id)
632                << " for session " << session.id;
633 }
634
635 int SpeechRecognitionManagerImpl::GetNextSessionID() {
636   ++last_session_id_;
637   // Deal with wrapping of last_session_id_. (How civilized).
638   if (last_session_id_ <= 0)
639     last_session_id_ = 1;
640   return last_session_id_;
641 }
642
643 bool SpeechRecognitionManagerImpl::SessionExists(int session_id) const {
644   return sessions_.find(session_id) != sessions_.end();
645 }
646
647 SpeechRecognitionManagerImpl::Session*
648 SpeechRecognitionManagerImpl::GetSession(int session_id) const {
649   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
650   SessionsTable::const_iterator iter = sessions_.find(session_id);
651   DCHECK(iter != sessions_.end());
652   return iter->second;
653 }
654
655 SpeechRecognitionEventListener* SpeechRecognitionManagerImpl::GetListener(
656     int session_id) const {
657   Session* session = GetSession(session_id);
658   return session->listener_is_active ? session->config.event_listener : NULL;
659 }
660
661 SpeechRecognitionEventListener*
662 SpeechRecognitionManagerImpl::GetDelegateListener() const {
663   return delegate_.get() ? delegate_->GetEventListener() : NULL;
664 }
665
666 const SpeechRecognitionSessionConfig&
667 SpeechRecognitionManagerImpl::GetSessionConfig(int session_id) const {
668   return GetSession(session_id)->config;
669 }
670
671 bool SpeechRecognitionManagerImpl::HasAudioInputDevices() {
672   return audio_manager_->HasAudioInputDevices();
673 }
674
675 string16 SpeechRecognitionManagerImpl::GetAudioInputDeviceModel() {
676   return audio_manager_->GetAudioInputDeviceModel();
677 }
678
679 void SpeechRecognitionManagerImpl::ShowAudioInputSettings() {
680   // Since AudioManager::ShowAudioInputSettings can potentially launch external
681   // processes, do that in the FILE thread to not block the calling threads.
682   BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
683                           base::Bind(&ShowAudioInputSettingsOnFileThread,
684                                      audio_manager_));
685 }
686
687 SpeechRecognitionManagerImpl::Session::Session()
688   : id(kSessionIDInvalid),
689     abort_requested(false),
690     listener_is_active(true) {
691 }
692
693 SpeechRecognitionManagerImpl::Session::~Session() {
694 }
695
696 }  // namespace content