Home | History | Annotate | Download | only in speech
      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_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 
    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
    701