Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(86)

Side by Side Diff: chrome/browser/speech/speech_input_extension_manager.cc

Issue 14230005: Remove all code for chrome.experimental.speechInput extension API (Closed) Base URL: https://src.chromium.org/svn/trunk/src/
Patch Set: rebase Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/browser/speech/speech_input_extension_manager.h ('k') | chrome/chrome_browser.gypi » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2012 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 "chrome/browser/speech/speech_input_extension_manager.h"
6
7 #include "base/bind.h"
8 #include "base/json/json_writer.h"
9 #include "base/lazy_instance.h"
10 #include "base/utf_string_conversions.h"
11 #include "base/values.h"
12 #include "chrome/browser/extensions/event_router.h"
13 #include "chrome/browser/extensions/extension_function_registry.h"
14 #include "chrome/browser/extensions/extension_host.h"
15 #include "chrome/browser/extensions/extension_process_manager.h"
16 #include "chrome/browser/extensions/extension_service.h"
17 #include "chrome/browser/extensions/extension_system.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/profiles/profile_dependency_manager.h"
20 #include "chrome/browser/profiles/profile_keyed_service.h"
21 #include "chrome/browser/profiles/profile_keyed_service_factory.h"
22 #include "chrome/browser/speech/speech_input_extension_api.h"
23 #include "chrome/common/chrome_notification_types.h"
24 #include "chrome/common/extensions/extension.h"
25 #include "content/public/browser/browser_thread.h"
26 #include "content/public/browser/notification_registrar.h"
27 #include "content/public/browser/notification_service.h"
28 #include "content/public/browser/render_process_host.h"
29 #include "content/public/browser/speech_recognition_manager.h"
30 #include "content/public/browser/speech_recognition_session_config.h"
31 #include "content/public/browser/speech_recognition_session_context.h"
32 #include "content/public/common/speech_recognition_error.h"
33 #include "content/public/common/speech_recognition_result.h"
34 #include "net/url_request/url_request_context_getter.h"
35
36 using content::BrowserThread;
37 using content::SpeechRecognitionHypothesis;
38 using content::SpeechRecognitionManager;
39
40 namespace {
41
42 const char kErrorNoRecordingDeviceFound[] = "noRecordingDeviceFound";
43 const char kErrorRecordingDeviceInUse[] = "recordingDeviceInUse";
44 const char kErrorUnableToStart[] = "unableToStart";
45 const char kErrorRequestDenied[] = "requestDenied";
46 const char kErrorRequestInProgress[] = "requestInProgress";
47 const char kErrorInvalidOperation[] = "invalidOperation";
48
49 const char kErrorCodeKey[] = "code";
50 const char kErrorCaptureError[] = "captureError";
51 const char kErrorNetworkError[] = "networkError";
52 const char kErrorNoSpeechHeard[] = "noSpeechHeard";
53 const char kErrorNoResults[] = "noResults";
54
55 const char kUtteranceKey[] = "utterance";
56 const char kConfidenceKey[] = "confidence";
57 const char kHypothesesKey[] = "hypotheses";
58
59 const char kOnErrorEvent[] = "experimental.speechInput.onError";
60 const char kOnResultEvent[] = "experimental.speechInput.onResult";
61 const char kOnSoundStartEvent[] = "experimental.speechInput.onSoundStart";
62 const char kOnSoundEndEvent[] = "experimental.speechInput.onSoundEnd";
63
64 }
65
66 SpeechInputExtensionInterface::SpeechInputExtensionInterface() {
67 }
68
69 SpeechInputExtensionInterface::~SpeechInputExtensionInterface() {
70 }
71
72 SpeechInputExtensionManager::SpeechInputExtensionManager(Profile* profile)
73 : profile_(profile),
74 state_(kIdle),
75 registrar_(new content::NotificationRegistrar),
76 speech_interface_(NULL),
77 is_recognition_in_progress_(false),
78 speech_recognition_session_id_(
79 SpeechRecognitionManager::kSessionIDInvalid) {
80 registrar_->Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
81 content::Source<Profile>(profile_));
82 }
83
84 SpeechInputExtensionManager::~SpeechInputExtensionManager() {
85 }
86
87 SpeechInputExtensionManager* SpeechInputExtensionManager::GetForProfile(
88 Profile* profile) {
89 extensions::SpeechInputAPI* speech_input_api =
90 extensions::ProfileKeyedAPIFactory<extensions::SpeechInputAPI>::
91 GetForProfile(profile);
92 if (!speech_input_api)
93 return NULL;
94 return speech_input_api->manager();
95 }
96
97 void SpeechInputExtensionManager::Observe(int type,
98 const content::NotificationSource& source,
99 const content::NotificationDetails& details) {
100 if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) {
101 ExtensionUnloaded(
102 content::Details<extensions::UnloadedExtensionInfo>(details)->
103 extension->id());
104 } else {
105 NOTREACHED();
106 }
107 }
108
109 void SpeechInputExtensionManager::ShutdownOnUIThread() {
110 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
111 VLOG(1) << "Profile shutting down.";
112
113 // Note: Unretained(this) is safe, also if we are freed in the meanwhile.
114 // It is used by the SR manager just for comparing the raw pointer and remove
115 // the associated sessions.
116 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
117 base::Bind(&SpeechInputExtensionManager::AbortAllSessionsOnIOThread,
118 base::Unretained(this)));
119
120 base::AutoLock auto_lock(state_lock_);
121 DCHECK(state_ != kShutdown);
122 if (state_ != kIdle) {
123 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
124 base::Bind(&SpeechInputExtensionManager::ForceStopOnIOThread, this));
125 }
126 state_ = kShutdown;
127 VLOG(1) << "Entering the shutdown sink state.";
128 registrar_.reset();
129 profile_ = NULL;
130 }
131
132 void SpeechInputExtensionManager::AbortAllSessionsOnIOThread() {
133 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
134 // TODO(primiano): The following check should not be really needed if the
135 // SpeechRecognitionManager and this class are destroyed in the correct order
136 // (this class first), as it is in current chrome implementation.
137 // However, it seems the some ChromiumOS tests violate the destruction order
138 // envisaged by browser_main_loop, so SpeechRecognitionmanager could have been
139 // freed by now.
140 if (SpeechRecognitionManager* mgr = SpeechRecognitionManager::GetInstance())
141 mgr->AbortAllSessionsForListener(this);
142 }
143
144 void SpeechInputExtensionManager::ExtensionUnloaded(
145 const std::string& extension_id) {
146 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
147
148 base::AutoLock auto_lock(state_lock_);
149 if (state_ == kShutdown)
150 return;
151
152 VLOG(1) << "Extension unloaded. Requesting to enforce stop...";
153 if (extension_id_in_use_ == extension_id) {
154 if (state_ != kIdle) {
155 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
156 base::Bind(&SpeechInputExtensionManager::ForceStopOnIOThread, this));
157 }
158 }
159 }
160
161 void SpeechInputExtensionManager::SetSpeechInputExtensionInterface(
162 SpeechInputExtensionInterface* speech_interface) {
163 speech_interface_ = speech_interface;
164 }
165
166 SpeechInputExtensionInterface*
167 SpeechInputExtensionManager::GetSpeechInputExtensionInterface() {
168 return speech_interface_ ? speech_interface_ : this;
169 }
170
171 void SpeechInputExtensionManager::ResetToIdleState() {
172 VLOG(1) << "State changed to idle. Deassociating any extensions.";
173 state_ = kIdle;
174 extension_id_in_use_.clear();
175 }
176
177 int SpeechInputExtensionManager::GetRenderProcessIDForExtension(
178 const std::string& extension_id) const {
179 ExtensionProcessManager* epm =
180 extensions::ExtensionSystem::Get(profile_)->process_manager();
181 DCHECK(epm);
182 extensions::ExtensionHost* eh =
183 epm->GetBackgroundHostForExtension(extension_id);
184 DCHECK(eh);
185 content::RenderProcessHost* rph = eh->render_process_host();
186 DCHECK(rph);
187 return rph->GetID();
188 }
189
190 void SpeechInputExtensionManager::OnRecognitionResults(
191 int session_id,
192 const content::SpeechRecognitionResults& results) {
193 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
194 DCHECK_EQ(session_id, speech_recognition_session_id_);
195
196 // Stopping will start the disassociation with the extension.
197 // Make a copy to report the results to the proper one.
198 std::string extension_id = extension_id_in_use_;
199 ForceStopOnIOThread();
200
201 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
202 base::Bind(&SpeechInputExtensionManager::SetRecognitionResultsOnUIThread,
203 this, results, extension_id));
204 }
205
206 void SpeechInputExtensionManager::SetRecognitionResultsOnUIThread(
207 const content::SpeechRecognitionResults& results,
208 const std::string& extension_id) {
209 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
210
211 content::SpeechRecognitionResults::const_iterator it = results.begin();
212 for (; it != results.end(); ++it) {
213 const content::SpeechRecognitionResult& result = (*it);
214
215 scoped_ptr<ListValue> args(new ListValue());
216 DictionaryValue* js_event = new DictionaryValue();
217 args->Append(js_event);
218
219 ListValue* js_hypothesis_array = new ListValue();
220 js_event->Set(kHypothesesKey, js_hypothesis_array);
221
222 for (size_t i = 0; i < result.hypotheses.size(); ++i) {
223 const SpeechRecognitionHypothesis& hypothesis = result.hypotheses[i];
224
225 DictionaryValue* js_hypothesis_object = new DictionaryValue();
226 js_hypothesis_array->Append(js_hypothesis_object);
227
228 js_hypothesis_object->SetString(kUtteranceKey,
229 UTF16ToUTF8(hypothesis.utterance));
230 js_hypothesis_object->SetDouble(kConfidenceKey,
231 hypothesis.confidence);
232 }
233
234 DispatchEventToExtension(extension_id, kOnResultEvent, args.Pass());
235 }
236 }
237
238 void SpeechInputExtensionManager::OnRecognitionStart(int session_id) {
239 DCHECK_EQ(session_id, speech_recognition_session_id_);
240 }
241
242 void SpeechInputExtensionManager::OnAudioStart(int session_id) {
243 VLOG(1) << "OnAudioStart";
244 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
245 DCHECK_EQ(session_id, speech_recognition_session_id_);
246
247 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
248 base::Bind(&SpeechInputExtensionManager::DidStartReceivingAudioOnUIThread,
249 this));
250 }
251
252 void SpeechInputExtensionManager::OnAudioEnd(int session_id) {
253 }
254
255 void SpeechInputExtensionManager::OnRecognitionEnd(int session_id) {
256 // In the very exceptional case in which we requested a new recognition before
257 // the previous one ended, don't clobber the speech_recognition_session_id_.
258 if (speech_recognition_session_id_ == session_id) {
259 is_recognition_in_progress_ = false;
260 speech_recognition_session_id_ =
261 SpeechRecognitionManager::kSessionIDInvalid;
262 }
263 }
264
265 void SpeechInputExtensionManager::DidStartReceivingAudioOnUIThread() {
266 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
267
268 base::AutoLock auto_lock(state_lock_);
269 if (state_ == kShutdown)
270 return;
271
272 DCHECK_EQ(state_, kStarting);
273 VLOG(1) << "State changed to recording";
274 state_ = kRecording;
275
276 DCHECK(profile_);
277
278 VLOG(1) << "Sending start notification";
279 content::NotificationService::current()->Notify(
280 chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_RECORDING_STARTED,
281 content::Source<Profile>(profile_),
282 content::Details<std::string>(&extension_id_in_use_));
283 }
284
285 void SpeechInputExtensionManager::OnRecognitionError(
286 int session_id, const content::SpeechRecognitionError& error) {
287 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
288 DCHECK_EQ(session_id, speech_recognition_session_id_);
289 VLOG(1) << "OnRecognitionError: " << error.code;
290
291 base::AutoLock auto_lock(state_lock_);
292 if (state_ == kShutdown)
293 return;
294
295 GetSpeechInputExtensionInterface()->StopRecording(true);
296
297 std::string event_error_code;
298 bool report_to_event = true;
299
300 switch (error.code) {
301 case content::SPEECH_RECOGNITION_ERROR_NONE:
302 break;
303
304 case content::SPEECH_RECOGNITION_ERROR_ABORTED:
305 // ERROR_ABORTED is received whenever AbortSession is called on the
306 // manager. However, we want propagate the error only if it is triggered
307 // by an external cause (another recognition started, aborting us), thus
308 // only if it occurs while we are capturing audio.
309 if (state_ == kRecording)
310 event_error_code = kErrorCaptureError;
311 break;
312
313 case content::SPEECH_RECOGNITION_ERROR_AUDIO:
314 if (state_ == kStarting) {
315 event_error_code = kErrorUnableToStart;
316 report_to_event = false;
317 } else {
318 event_error_code = kErrorCaptureError;
319 }
320 break;
321
322 case content::SPEECH_RECOGNITION_ERROR_NETWORK:
323 event_error_code = kErrorNetworkError;
324 break;
325
326 case content::SPEECH_RECOGNITION_ERROR_BAD_GRAMMAR:
327 // No error is returned on invalid language, for example.
328 // To avoid confusion about when this is would be fired, the invalid
329 // params error is not being exposed to the onError event.
330 event_error_code = kErrorUnableToStart;
331 break;
332
333 case content::SPEECH_RECOGNITION_ERROR_NO_SPEECH:
334 event_error_code = kErrorNoSpeechHeard;
335 break;
336
337 case content::SPEECH_RECOGNITION_ERROR_NO_MATCH:
338 event_error_code = kErrorNoResults;
339 break;
340
341 // The remaining kErrorAborted case should never be returned by the server.
342 default:
343 NOTREACHED();
344 }
345
346 if (!event_error_code.empty()) {
347 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
348 base::Bind(&SpeechInputExtensionManager::DispatchError,
349 this, event_error_code, report_to_event));
350 }
351 }
352
353 void SpeechInputExtensionManager::OnEnvironmentEstimationComplete(
354 int session_id) {
355 DCHECK_EQ(session_id, speech_recognition_session_id_);
356 }
357
358 void SpeechInputExtensionManager::OnSoundStart(int session_id) {
359 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
360 DCHECK_EQ(session_id, speech_recognition_session_id_);
361 VLOG(1) << "OnSoundStart";
362
363 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
364 base::Bind(&SpeechInputExtensionManager::DispatchEventToExtension,
365 this, extension_id_in_use_, std::string(kOnSoundStartEvent),
366 Passed(scoped_ptr<ListValue>(new ListValue()))));
367 }
368
369 void SpeechInputExtensionManager::OnSoundEnd(int session_id) {
370 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
371 VLOG(1) << "OnSoundEnd";
372
373 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
374 base::Bind(&SpeechInputExtensionManager::DispatchEventToExtension,
375 this, extension_id_in_use_, std::string(kOnSoundEndEvent),
376 Passed(scoped_ptr<ListValue>(new ListValue()))));
377 }
378
379 void SpeechInputExtensionManager::DispatchEventToExtension(
380 const std::string& extension_id, const std::string& event_name,
381 scoped_ptr<ListValue> event_args) {
382 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
383
384 base::AutoLock auto_lock(state_lock_);
385 if (state_ == kShutdown)
386 return;
387
388 if (profile_ && extensions::ExtensionSystem::Get(profile_)->event_router()) {
389 scoped_ptr<extensions::Event> event(new extensions::Event(
390 event_name, event_args.Pass()));
391 event->restrict_to_profile = profile_;
392 extensions::ExtensionSystem::Get(profile_)->event_router()->
393 DispatchEventToExtension(extension_id, event.Pass());
394 }
395 }
396
397 void SpeechInputExtensionManager::DispatchError(
398 const std::string& error, bool dispatch_event) {
399 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
400
401 std::string extension_id;
402 {
403 base::AutoLock auto_lock(state_lock_);
404 if (state_ == kShutdown)
405 return;
406
407 extension_id = extension_id_in_use_;
408 ResetToIdleState();
409
410 // Will set the error property in the ongoing extension function calls.
411 ExtensionError details(extension_id, error);
412 content::NotificationService::current()->Notify(
413 chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_FAILED,
414 content::Source<Profile>(profile_),
415 content::Details<ExtensionError>(&details));
416 }
417
418 // Used for errors that are also reported via the onError event.
419 if (dispatch_event) {
420 scoped_ptr<ListValue> args(new ListValue());
421 DictionaryValue* js_error = new DictionaryValue();
422 args->Append(js_error);
423 js_error->SetString(kErrorCodeKey, error);
424 DispatchEventToExtension(extension_id, kOnErrorEvent, args.Pass());
425 }
426 }
427
428 bool SpeechInputExtensionManager::Start(
429 const std::string& extension_id, const std::string& language,
430 const std::string& grammar, bool filter_profanities, std::string* error) {
431 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
432 DCHECK(error);
433 VLOG(1) << "Requesting start (UI thread)";
434
435 base::AutoLock auto_lock(state_lock_);
436 if (state_ == kShutdown ||
437 (!extension_id_in_use_.empty() && extension_id_in_use_ != extension_id)) {
438 *error = kErrorRequestDenied;
439 return false;
440 }
441
442 switch (state_) {
443 case kIdle:
444 break;
445
446 case kStarting:
447 *error = kErrorRequestInProgress;
448 return false;
449
450 case kRecording:
451 case kStopping:
452 *error = kErrorInvalidOperation;
453 return false;
454
455 default:
456 NOTREACHED();
457 }
458
459 const extensions::Extension* extension =
460 extensions::ExtensionSystem::Get(profile_)->extension_service()->
461 GetExtensionById(extension_id, true);
462 DCHECK(extension);
463 const std::string& extension_name = extension->name();
464
465 extension_id_in_use_ = extension_id;
466 VLOG(1) << "State changed to starting";
467 state_ = kStarting;
468
469 scoped_refptr<net::URLRequestContextGetter> url_request_context_getter =
470 profile_->GetRequestContext();
471
472 const int render_process_id = GetRenderProcessIDForExtension(extension_id);
473
474 BrowserThread::PostTask(
475 BrowserThread::IO, FROM_HERE,
476 base::Bind(&SpeechInputExtensionManager::StartOnIOThread, this,
477 url_request_context_getter, extension_name, language, grammar,
478 filter_profanities, render_process_id));
479 return true;
480 }
481
482 void SpeechInputExtensionManager::StartOnIOThread(
483 scoped_refptr<net::URLRequestContextGetter> context_getter,
484 const std::string& extension_name,
485 const std::string& language,
486 const std::string& grammar,
487 bool filter_profanities,
488 int render_process_id) {
489 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
490 VLOG(1) << "Requesting start (IO thread)";
491
492 // Everything put inside the lock to ensure the validity of context_getter,
493 // guaranteed while not in the shutdown state. Any ongoing or recognition
494 // request will be requested to be aborted when entering the shutdown state.
495 base::AutoLock auto_lock(state_lock_);
496 if (state_ == kShutdown)
497 return;
498
499 // TODO(primiano): These two checks below could be avoided, since they are
500 // already handled in the speech recognition classes. However, since the
501 // speech input extensions tests are bypassing the manager, we need them to
502 // pass the tests.
503 if (!GetSpeechInputExtensionInterface()->HasAudioInputDevices()) {
504 BrowserThread::PostTask(
505 BrowserThread::UI, FROM_HERE,
506 base::Bind(&SpeechInputExtensionManager::DispatchError, this,
507 std::string(kErrorNoRecordingDeviceFound), false));
508 return;
509 }
510
511 if (GetSpeechInputExtensionInterface()->IsCapturingAudio()) {
512 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
513 base::Bind(&SpeechInputExtensionManager::DispatchError, this,
514 std::string(kErrorRecordingDeviceInUse), false));
515 return;
516 }
517
518 GetSpeechInputExtensionInterface()->StartRecording(this,
519 context_getter,
520 extension_name,
521 language,
522 grammar,
523 filter_profanities,
524 render_process_id);
525 }
526
527 bool SpeechInputExtensionManager::HasAudioInputDevices() {
528 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
529 return SpeechRecognitionManager::GetInstance()->HasAudioInputDevices();
530 }
531
532 bool SpeechInputExtensionManager::IsCapturingAudio() {
533 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
534 return SpeechRecognitionManager::GetInstance()->IsCapturingAudio();
535 }
536
537 void SpeechInputExtensionManager::IsRecording(
538 const IsRecordingCallback& callback) {
539 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
540 BrowserThread::PostTask(
541 BrowserThread::IO, FROM_HERE,
542 base::Bind(&SpeechInputExtensionManager::IsRecordingOnIOThread,
543 this, callback));
544 }
545
546 void SpeechInputExtensionManager::IsRecordingOnIOThread(
547 const IsRecordingCallback& callback) {
548 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
549
550 bool result = GetSpeechInputExtensionInterface()->IsCapturingAudio();
551
552 BrowserThread::PostTask(
553 BrowserThread::UI, FROM_HERE,
554 base::Bind(&SpeechInputExtensionManager::IsRecordingOnUIThread,
555 this, callback, result));
556 }
557
558 void SpeechInputExtensionManager::IsRecordingOnUIThread(
559 const IsRecordingCallback& callback,
560 bool result) {
561 BrowserThread::CurrentlyOn(BrowserThread::UI);
562 callback.Run(result);
563 }
564
565 void SpeechInputExtensionManager::StartRecording(
566 content::SpeechRecognitionEventListener* listener,
567 net::URLRequestContextGetter* context_getter,
568 const std::string& extension_name,
569 const std::string& language,
570 const std::string& grammar,
571 bool filter_profanities,
572 int render_process_id) {
573 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
574
575 content::SpeechRecognitionSessionContext context;
576 context.requested_by_page_element = false;
577 context.render_process_id = render_process_id;
578 context.context_name = extension_name;
579
580 content::SpeechRecognitionSessionConfig config;
581 config.language = language;
582 config.grammars.push_back(content::SpeechRecognitionGrammar(grammar));
583 config.initial_context = context;
584 config.url_request_context_getter = context_getter;
585 config.filter_profanities = filter_profanities;
586 config.event_listener = listener;
587
588 DCHECK(!is_recognition_in_progress_);
589 SpeechRecognitionManager& manager = *SpeechRecognitionManager::GetInstance();
590 speech_recognition_session_id_ =
591 manager.CreateSession(config);
592 DCHECK_NE(speech_recognition_session_id_,
593 SpeechRecognitionManager::kSessionIDInvalid);
594 is_recognition_in_progress_ = true;
595 manager.StartSession(speech_recognition_session_id_);
596 }
597
598 bool SpeechInputExtensionManager::HasValidRecognizer() {
599 if (!is_recognition_in_progress_)
600 return false;
601 return SpeechRecognitionManager::GetInstance()->IsCapturingAudio();
602 }
603
604 bool SpeechInputExtensionManager::Stop(const std::string& extension_id,
605 std::string* error) {
606 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
607 DCHECK(error);
608 VLOG(1) << "Requesting stop (UI thread)";
609
610 base::AutoLock auto_lock(state_lock_);
611 if (state_ == kShutdown ||
612 (!extension_id_in_use_.empty() && extension_id_in_use_ != extension_id)) {
613 *error = kErrorRequestDenied;
614 return false;
615 }
616
617 switch (state_) {
618 case kRecording:
619 break;
620
621 case kStopping:
622 *error = kErrorRequestInProgress;
623 return false;
624
625 case kIdle:
626 case kStarting:
627 *error = kErrorInvalidOperation;
628 return false;
629
630 default:
631 NOTREACHED();
632 }
633
634 // Guarded by the state lock.
635 DCHECK(GetSpeechInputExtensionInterface()->HasValidRecognizer());
636
637 VLOG(1) << "State changed to stopping";
638 state_ = kStopping;
639
640 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
641 base::Bind(&SpeechInputExtensionManager::ForceStopOnIOThread, this));
642 return true;
643 }
644
645 void SpeechInputExtensionManager::ForceStopOnIOThread() {
646 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
647 VLOG(1) << "Requesting forced stop (IO thread)";
648
649 base::AutoLock auto_lock(state_lock_);
650 DCHECK(state_ != kIdle);
651
652 GetSpeechInputExtensionInterface()->StopRecording(false);
653
654 if (state_ == kShutdown)
655 return;
656
657 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
658 base::Bind(&SpeechInputExtensionManager::StopSucceededOnUIThread, this));
659 }
660
661 void SpeechInputExtensionManager::StopRecording(bool recognition_failed) {
662 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
663 if (!is_recognition_in_progress_)
664 return;
665 DCHECK_NE(speech_recognition_session_id_,
666 SpeechRecognitionManager::kSessionIDInvalid);
667 SpeechRecognitionManager::GetInstance()->AbortSession(
668 speech_recognition_session_id_);
669 }
670
671 void SpeechInputExtensionManager::StopSucceededOnUIThread() {
672 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
673 VLOG(1) << "Stop succeeded (UI thread)";
674
675 base::AutoLock auto_lock(state_lock_);
676 if (state_ == kShutdown)
677 return;
678
679 std::string extension_id = extension_id_in_use_;
680 ResetToIdleState();
681
682 content::NotificationService::current()->Notify(
683 chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_RECORDING_STOPPED,
684 // Guarded by the state_ == kShutdown check.
685 content::Source<Profile>(profile_),
686 content::Details<std::string>(&extension_id));
687 }
688
689 void SpeechInputExtensionManager::OnAudioLevelsChange(int session_id,
690 float volume,
691 float noise_volume) {}
692
693 namespace extensions {
694
695 SpeechInputAPI::SpeechInputAPI(Profile* profile)
696 : manager_(new SpeechInputExtensionManager(profile)) {
697 ExtensionFunctionRegistry* registry =
698 ExtensionFunctionRegistry::GetInstance();
699 registry->RegisterFunction<StartSpeechInputFunction>();
700 registry->RegisterFunction<StopSpeechInputFunction>();
701 registry->RegisterFunction<IsRecordingSpeechInputFunction>();
702 }
703
704 SpeechInputAPI::~SpeechInputAPI() {
705 }
706
707 void SpeechInputAPI::Shutdown() {
708 manager_->ShutdownOnUIThread();
709 }
710
711 static base::LazyInstance<ProfileKeyedAPIFactory<SpeechInputAPI> >
712 g_factory = LAZY_INSTANCE_INITIALIZER;
713
714 // static
715 ProfileKeyedAPIFactory<SpeechInputAPI>* SpeechInputAPI::GetFactoryInstance() {
716 return &g_factory.Get();
717 }
718
719 } // namespace extensions
OLDNEW
« no previous file with comments | « chrome/browser/speech/speech_input_extension_manager.h ('k') | chrome/chrome_browser.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698