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
ShowAudioInputSettingsOnFileThread(media::AudioManager * audio_manager)41 void ShowAudioInputSettingsOnFileThread(media::AudioManager* audio_manager) {
42 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
43 audio_manager->ShowAudioInputSettings();
44 }
45
46 } // namespace
47
GetInstance()48 SpeechRecognitionManager* SpeechRecognitionManager::GetInstance() {
49 if (manager_for_tests_)
50 return manager_for_tests_;
51 return SpeechRecognitionManagerImpl::GetInstance();
52 }
53
SetManagerForTesting(SpeechRecognitionManager * manager)54 void SpeechRecognitionManager::SetManagerForTesting(
55 SpeechRecognitionManager* manager) {
56 manager_for_tests_ = manager;
57 }
58
GetInstance()59 SpeechRecognitionManagerImpl* SpeechRecognitionManagerImpl::GetInstance() {
60 return g_speech_recognition_manager_impl;
61 }
62
SpeechRecognitionManagerImpl(media::AudioManager * audio_manager,MediaStreamManager * media_stream_manager)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
~SpeechRecognitionManagerImpl()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
CreateSession(const SpeechRecognitionSessionConfig & config)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
StartSession(int session_id)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
RecognitionAllowedCallback(int session_id,bool ask_user,bool is_allowed)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(true, false),
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
MediaRequestPermissionCallback(int session_id,const MediaStreamDevices & devices,scoped_ptr<MediaStreamUIProxy> stream_ui)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
AbortSession(int session_id)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
StopAudioCaptureForSession(int session_id)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
OnRecognitionStart(int session_id)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
OnAudioStart(int session_id)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
OnEnvironmentEstimationComplete(int session_id)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
OnSoundStart(int session_id)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
OnSoundEnd(int session_id)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
OnAudioEnd(int session_id)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
OnRecognitionResults(int session_id,const SpeechRecognitionResults & results)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
OnRecognitionError(int session_id,const SpeechRecognitionError & error)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
OnAudioLevelsChange(int session_id,float volume,float noise_volume)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
OnRecognitionEnd(int session_id)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
GetSession(int render_process_id,int render_view_id,int request_id) const431 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
GetSessionContext(int session_id) const448 SpeechRecognitionManagerImpl::GetSessionContext(int session_id) const {
449 return GetSession(session_id)->context;
450 }
451
AbortAllSessionsForRenderProcess(int render_process_id)452 void SpeechRecognitionManagerImpl::AbortAllSessionsForRenderProcess(
453 int render_process_id) {
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->context.render_process_id == render_process_id) {
462 AbortSession(session->id);
463 session->listener_is_active = false;
464 }
465 }
466 }
467
AbortAllSessionsForRenderView(int render_process_id,int render_view_id)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 ---------------------------
DispatchEvent(int session_id,FSMEvent event)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.
ExecuteTransitionAndGetNextState(Session * session,FSMState session_state,FSMEvent event)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
GetSessionState(int session_id) const567 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
SessionStart(const Session & session)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
SessionAbort(const Session & session)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
SessionStopAudioCapture(const Session & session)606 void SpeechRecognitionManagerImpl::SessionStopAudioCapture(
607 const Session& session) {
608 DCHECK(session.recognizer.get());
609 session.recognizer->StopAudioCapture();
610 }
611
ResetCapturingSessionId(const Session & session)612 void SpeechRecognitionManagerImpl::ResetCapturingSessionId(
613 const Session& session) {
614 DCHECK_EQ(primary_session_id_, session.id);
615 primary_session_id_ = kSessionIDInvalid;
616 }
617
SessionDelete(Session * session)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
NotFeasible(const Session & session,FSMEvent event)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
GetNextSessionID()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
SessionExists(int session_id) const643 bool SpeechRecognitionManagerImpl::SessionExists(int session_id) const {
644 return sessions_.find(session_id) != sessions_.end();
645 }
646
647 SpeechRecognitionManagerImpl::Session*
GetSession(int session_id) const648 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
GetListener(int session_id) const655 SpeechRecognitionEventListener* SpeechRecognitionManagerImpl::GetListener(
656 int session_id) const {
657 Session* session = GetSession(session_id);
658 if (session->listener_is_active && session->config.event_listener)
659 return session->config.event_listener.get();
660 return NULL;
661 }
662
663 SpeechRecognitionEventListener*
GetDelegateListener() const664 SpeechRecognitionManagerImpl::GetDelegateListener() const {
665 return delegate_.get() ? delegate_->GetEventListener() : NULL;
666 }
667
668 const SpeechRecognitionSessionConfig&
GetSessionConfig(int session_id) const669 SpeechRecognitionManagerImpl::GetSessionConfig(int session_id) const {
670 return GetSession(session_id)->config;
671 }
672
HasAudioInputDevices()673 bool SpeechRecognitionManagerImpl::HasAudioInputDevices() {
674 return audio_manager_->HasAudioInputDevices();
675 }
676
GetAudioInputDeviceModel()677 base::string16 SpeechRecognitionManagerImpl::GetAudioInputDeviceModel() {
678 return audio_manager_->GetAudioInputDeviceModel();
679 }
680
ShowAudioInputSettings()681 void SpeechRecognitionManagerImpl::ShowAudioInputSettings() {
682 // Since AudioManager::ShowAudioInputSettings can potentially launch external
683 // processes, do that in the FILE thread to not block the calling threads.
684 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
685 base::Bind(&ShowAudioInputSettingsOnFileThread,
686 audio_manager_));
687 }
688
Session()689 SpeechRecognitionManagerImpl::Session::Session()
690 : id(kSessionIDInvalid),
691 abort_requested(false),
692 listener_is_active(true) {
693 }
694
~Session()695 SpeechRecognitionManagerImpl::Session::~Session() {
696 }
697
698 } // namespace content
699