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
31using base::Callback;
32
33namespace content {
34
35SpeechRecognitionManager* SpeechRecognitionManager::manager_for_tests_;
36
37namespace {
38
39SpeechRecognitionManagerImpl* g_speech_recognition_manager_impl;
40
41void ShowAudioInputSettingsOnFileThread(media::AudioManager* audio_manager) {
42  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
43  audio_manager->ShowAudioInputSettings();
44}
45
46}  // namespace
47
48SpeechRecognitionManager* SpeechRecognitionManager::GetInstance() {
49  if (manager_for_tests_)
50    return manager_for_tests_;
51  return SpeechRecognitionManagerImpl::GetInstance();
52}
53
54void SpeechRecognitionManager::SetManagerForTesting(
55    SpeechRecognitionManager* manager) {
56  manager_for_tests_ = manager;
57}
58
59SpeechRecognitionManagerImpl* SpeechRecognitionManagerImpl::GetInstance() {
60  return g_speech_recognition_manager_impl;
61}
62
63SpeechRecognitionManagerImpl::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
78SpeechRecognitionManagerImpl::~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
92int 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
159void 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
181void 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_view_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
228void 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
254void 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
275void 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
296void 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
315void 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
327void 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
340void 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
352void 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
363void 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
380void 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
392void 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
404void 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
416void 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
433int 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
449SpeechRecognitionSessionContext
450SpeechRecognitionManagerImpl::GetSessionContext(int session_id) const {
451  return GetSession(session_id)->context;
452}
453
454void 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
470void 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 ---------------------------
485void 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.
513void 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
568SpeechRecognitionManagerImpl::FSMState
569SpeechRecognitionManagerImpl::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
582void 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
601void 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
608void SpeechRecognitionManagerImpl::SessionStopAudioCapture(
609    const Session& session) {
610  DCHECK(session.recognizer.get());
611  session.recognizer->StopAudioCapture();
612}
613
614void SpeechRecognitionManagerImpl::ResetCapturingSessionId(
615    const Session& session) {
616  DCHECK_EQ(primary_session_id_, session.id);
617  primary_session_id_ = kSessionIDInvalid;
618}
619
620void 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
630void 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
637int 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
645bool SpeechRecognitionManagerImpl::SessionExists(int session_id) const {
646  return sessions_.find(session_id) != sessions_.end();
647}
648
649SpeechRecognitionManagerImpl::Session*
650SpeechRecognitionManagerImpl::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
657SpeechRecognitionEventListener* 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
665SpeechRecognitionEventListener*
666SpeechRecognitionManagerImpl::GetDelegateListener() const {
667  return delegate_.get() ? delegate_->GetEventListener() : NULL;
668}
669
670const SpeechRecognitionSessionConfig&
671SpeechRecognitionManagerImpl::GetSessionConfig(int session_id) const {
672  return GetSession(session_id)->config;
673}
674
675bool SpeechRecognitionManagerImpl::HasAudioInputDevices() {
676  return audio_manager_->HasAudioInputDevices();
677}
678
679base::string16 SpeechRecognitionManagerImpl::GetAudioInputDeviceModel() {
680  return audio_manager_->GetAudioInputDeviceModel();
681}
682
683void 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
691SpeechRecognitionManagerImpl::Session::Session()
692  : id(kSessionIDInvalid),
693    abort_requested(false),
694    listener_is_active(true) {
695}
696
697SpeechRecognitionManagerImpl::Session::~Session() {
698}
699
700}  // namespace content
701