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