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

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

Issue 10440011: SpeechInputExtensionManager now interface (exclusively) with SpeechRecognitionManagerDelegate (Spee… (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Satish review Created 8 years, 7 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/speech/speech_input_extension_manager.h" 5 #include "chrome/browser/speech/speech_input_extension_manager.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/json/json_writer.h" 8 #include "base/json/json_writer.h"
9 #include "base/utf_string_conversions.h" 9 #include "base/utf_string_conversions.h"
10 #include "base/values.h" 10 #include "base/values.h"
11 #include "chrome/browser/extensions/extension_event_router.h" 11 #include "chrome/browser/extensions/extension_event_router.h"
12 #include "chrome/browser/extensions/extension_service.h" 12 #include "chrome/browser/extensions/extension_service.h"
13 #include "chrome/browser/prefs/pref_service.h" 13 #include "chrome/browser/prefs/pref_service.h"
14 #include "chrome/browser/profiles/profile.h" 14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/profiles/profile_dependency_manager.h" 15 #include "chrome/browser/profiles/profile_dependency_manager.h"
16 #include "chrome/browser/profiles/profile_keyed_service.h" 16 #include "chrome/browser/profiles/profile_keyed_service.h"
17 #include "chrome/browser/profiles/profile_keyed_service_factory.h" 17 #include "chrome/browser/profiles/profile_keyed_service_factory.h"
18 #include "chrome/browser/speech/speech_recognition_tray_icon_controller.h"
19 #include "chrome/common/chrome_notification_types.h" 18 #include "chrome/common/chrome_notification_types.h"
20 #include "chrome/common/extensions/extension.h" 19 #include "chrome/common/extensions/extension.h"
21 #include "chrome/common/pref_names.h" 20 #include "chrome/common/pref_names.h"
22 #include "content/public/browser/browser_thread.h" 21 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/notification_registrar.h" 22 #include "content/public/browser/notification_registrar.h"
24 #include "content/public/browser/notification_service.h" 23 #include "content/public/browser/notification_service.h"
25 #include "content/public/browser/speech_recognition_manager.h" 24 #include "content/public/browser/speech_recognition_manager.h"
26 #include "content/public/browser/speech_recognizer.h" 25 #include "content/public/browser/speech_recognition_session_config.h"
26 #include "content/public/browser/speech_recognition_session_context.h"
27 #include "content/public/common/speech_recognition_error.h" 27 #include "content/public/common/speech_recognition_error.h"
28 #include "content/public/common/speech_recognition_result.h" 28 #include "content/public/common/speech_recognition_result.h"
29 #include "net/url_request/url_request_context_getter.h"
29 30
30 using content::BrowserThread; 31 using content::BrowserThread;
31 using content::SpeechRecognitionHypothesis; 32 using content::SpeechRecognitionHypothesis;
32 using content::SpeechRecognitionManager; 33 using content::SpeechRecognitionManager;
33 34
34 namespace { 35 namespace {
35 36
36 const char kErrorNoRecordingDeviceFound[] = "noRecordingDeviceFound"; 37 const char kErrorNoRecordingDeviceFound[] = "noRecordingDeviceFound";
37 const char kErrorRecordingDeviceInUse[] = "recordingDeviceInUse"; 38 const char kErrorRecordingDeviceInUse[] = "recordingDeviceInUse";
38 const char kErrorUnableToStart[] = "unableToStart"; 39 const char kErrorUnableToStart[] = "unableToStart";
39 const char kErrorRequestDenied[] = "requestDenied"; 40 const char kErrorRequestDenied[] = "requestDenied";
40 const char kErrorRequestInProgress[] = "requestInProgress"; 41 const char kErrorRequestInProgress[] = "requestInProgress";
41 const char kErrorInvalidOperation[] = "invalidOperation"; 42 const char kErrorInvalidOperation[] = "invalidOperation";
42 43
43 const char kErrorCodeKey[] = "code"; 44 const char kErrorCodeKey[] = "code";
44 const char kErrorCaptureError[] = "captureError"; 45 const char kErrorCaptureError[] = "captureError";
45 const char kErrorNetworkError[] = "networkError"; 46 const char kErrorNetworkError[] = "networkError";
46 const char kErrorNoSpeechHeard[] = "noSpeechHeard"; 47 const char kErrorNoSpeechHeard[] = "noSpeechHeard";
47 const char kErrorNoResults[] = "noResults"; 48 const char kErrorNoResults[] = "noResults";
48 49
49 const char kUtteranceKey[] = "utterance"; 50 const char kUtteranceKey[] = "utterance";
50 const char kConfidenceKey[] = "confidence"; 51 const char kConfidenceKey[] = "confidence";
51 const char kHypothesesKey[] = "hypotheses"; 52 const char kHypothesesKey[] = "hypotheses";
52 53
53 const char kOnErrorEvent[] = "experimental.speechInput.onError"; 54 const char kOnErrorEvent[] = "experimental.speechInput.onError";
54 const char kOnResultEvent[] = "experimental.speechInput.onResult"; 55 const char kOnResultEvent[] = "experimental.speechInput.onResult";
55 const char kOnSoundStartEvent[] = "experimental.speechInput.onSoundStart"; 56 const char kOnSoundStartEvent[] = "experimental.speechInput.onSoundStart";
56 const char kOnSoundEndEvent[] = "experimental.speechInput.onSoundEnd"; 57 const char kOnSoundEndEvent[] = "experimental.speechInput.onSoundEnd";
57 58
58 // Session id provided to the speech recognizer. Since only one extension can
59 // be recording on the same time a constant value is enough as id.
60 // TODO(primiano) this will not be valid anymore once speech input extension
61 // will use the SpeechRecognitionManager and not the SpeechRecognizer directly.
62 static const int kSpeechInputSessionId = 1;
63
64 // Wrap an SpeechInputExtensionManager using scoped_refptr to avoid 59 // Wrap an SpeechInputExtensionManager using scoped_refptr to avoid
65 // assertion failures on destruction because of not using release(). 60 // assertion failures on destruction because of not using release().
66 class SpeechInputExtensionManagerWrapper : public ProfileKeyedService { 61 class SpeechInputExtensionManagerWrapper : public ProfileKeyedService {
67 public: 62 public:
68 explicit SpeechInputExtensionManagerWrapper( 63 explicit SpeechInputExtensionManagerWrapper(
69 SpeechInputExtensionManager* manager) 64 SpeechInputExtensionManager* manager)
70 : manager_(manager) {} 65 : manager_(manager) {}
71 66
72 virtual ~SpeechInputExtensionManagerWrapper() {} 67 virtual ~SpeechInputExtensionManagerWrapper() {}
73 68
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
150 SpeechInputExtensionInterface::SpeechInputExtensionInterface() { 145 SpeechInputExtensionInterface::SpeechInputExtensionInterface() {
151 } 146 }
152 147
153 SpeechInputExtensionInterface::~SpeechInputExtensionInterface() { 148 SpeechInputExtensionInterface::~SpeechInputExtensionInterface() {
154 } 149 }
155 150
156 SpeechInputExtensionManager::SpeechInputExtensionManager(Profile* profile) 151 SpeechInputExtensionManager::SpeechInputExtensionManager(Profile* profile)
157 : profile_(profile), 152 : profile_(profile),
158 state_(kIdle), 153 state_(kIdle),
159 registrar_(new content::NotificationRegistrar), 154 registrar_(new content::NotificationRegistrar),
160 speech_interface_(NULL) { 155 speech_interface_(NULL),
156 is_recognition_in_progress_(false),
157 speech_recognition_session_id_(
158 SpeechRecognitionManager::kSessionIDInvalid) {
161 registrar_->Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, 159 registrar_->Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
162 content::Source<Profile>(profile_)); 160 content::Source<Profile>(profile_));
163 } 161 }
164 162
165 SpeechInputExtensionManager::~SpeechInputExtensionManager() { 163 SpeechInputExtensionManager::~SpeechInputExtensionManager() {
166 } 164 }
167 165
168 SpeechInputExtensionManager* SpeechInputExtensionManager::GetForProfile( 166 SpeechInputExtensionManager* SpeechInputExtensionManager::GetForProfile(
169 Profile* profile) { 167 Profile* profile) {
170 SpeechInputExtensionManagerWrapper* wrapper = 168 SpeechInputExtensionManagerWrapper* wrapper =
(...skipping 16 matching lines...) Expand all
187 extension->id()); 185 extension->id());
188 } else { 186 } else {
189 NOTREACHED(); 187 NOTREACHED();
190 } 188 }
191 } 189 }
192 190
193 void SpeechInputExtensionManager::ShutdownOnUIThread() { 191 void SpeechInputExtensionManager::ShutdownOnUIThread() {
194 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 192 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
195 VLOG(1) << "Profile shutting down."; 193 VLOG(1) << "Profile shutting down.";
196 194
195 // Note: Unretained(this) is safe, also if we are freed in the meanwhile.
196 // It is used by the SR manager just for comparing the raw pointer and remove
197 // the associated sessions.
198 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
199 base::Bind(&SpeechInputExtensionManager::AbortAllSessionsOnIOThread,
200 base::Unretained(this)));
201
197 base::AutoLock auto_lock(state_lock_); 202 base::AutoLock auto_lock(state_lock_);
198 DCHECK(state_ != kShutdown); 203 DCHECK(state_ != kShutdown);
199 if (state_ != kIdle) { 204 if (state_ != kIdle) {
200 DCHECK(notification_.get());
201 notification_->Hide();
202 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, 205 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
203 base::Bind(&SpeechInputExtensionManager::ForceStopOnIOThread, this)); 206 base::Bind(&SpeechInputExtensionManager::ForceStopOnIOThread, this));
204 } 207 }
205 state_ = kShutdown; 208 state_ = kShutdown;
206 VLOG(1) << "Entering the shutdown sink state."; 209 VLOG(1) << "Entering the shutdown sink state.";
207 registrar_.reset(); 210 registrar_.reset();
208 profile_ = NULL; 211 profile_ = NULL;
209 } 212 }
210 213
214 void SpeechInputExtensionManager::AbortAllSessionsOnIOThread() {
215 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
216 // TODO(primiano) The following check should not be really needed if the
217 // SpeechRecognitionManager and this class are destroyed in the correct order
218 // (this class first), as it is in current chrome implementation.
219 // However, it seems the some ChromiumOS tests violate the destruction order
220 // envisaged by browser_main_loop, so SpeechRecognitionmanager could have been
221 // freed by now.
222 if (SpeechRecognitionManager* mgr = SpeechRecognitionManager::GetInstance())
223 mgr->AbortAllSessionsForListener(this);
224 }
225
211 void SpeechInputExtensionManager::ExtensionUnloaded( 226 void SpeechInputExtensionManager::ExtensionUnloaded(
212 const std::string& extension_id) { 227 const std::string& extension_id) {
213 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 228 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
214 229
215 base::AutoLock auto_lock(state_lock_); 230 base::AutoLock auto_lock(state_lock_);
216 if (state_ == kShutdown) 231 if (state_ == kShutdown)
217 return; 232 return;
218 233
219 VLOG(1) << "Extension unloaded. Requesting to enforce stop..."; 234 VLOG(1) << "Extension unloaded. Requesting to enforce stop...";
220 if (extension_id_in_use_ == extension_id) { 235 if (extension_id_in_use_ == extension_id) {
(...skipping 17 matching lines...) Expand all
238 void SpeechInputExtensionManager::ResetToIdleState() { 253 void SpeechInputExtensionManager::ResetToIdleState() {
239 VLOG(1) << "State changed to idle. Deassociating any extensions."; 254 VLOG(1) << "State changed to idle. Deassociating any extensions.";
240 state_ = kIdle; 255 state_ = kIdle;
241 extension_id_in_use_.clear(); 256 extension_id_in_use_.clear();
242 } 257 }
243 258
244 void SpeechInputExtensionManager::OnRecognitionResult( 259 void SpeechInputExtensionManager::OnRecognitionResult(
245 int session_id, 260 int session_id,
246 const content::SpeechRecognitionResult& result) { 261 const content::SpeechRecognitionResult& result) {
247 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 262 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
248 DCHECK_EQ(session_id, kSpeechInputSessionId); 263 DCHECK_EQ(session_id, speech_recognition_session_id_);
249 264
250 // Stopping will start the disassociation with the extension. 265 // Stopping will start the disassociation with the extension.
251 // Make a copy to report the results to the proper one. 266 // Make a copy to report the results to the proper one.
252 std::string extension_id = extension_id_in_use_; 267 std::string extension_id = extension_id_in_use_;
253 ForceStopOnIOThread(); 268 ForceStopOnIOThread();
254 269
255 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 270 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
256 base::Bind(&SpeechInputExtensionManager::SetRecognitionResultOnUIThread, 271 base::Bind(&SpeechInputExtensionManager::SetRecognitionResultOnUIThread,
257 this, result, extension_id)); 272 this, result, extension_id));
258 } 273 }
(...skipping 22 matching lines...) Expand all
281 hypothesis.confidence); 296 hypothesis.confidence);
282 } 297 }
283 298
284 std::string json_args; 299 std::string json_args;
285 base::JSONWriter::Write(&args, &json_args); 300 base::JSONWriter::Write(&args, &json_args);
286 VLOG(1) << "Results: " << json_args; 301 VLOG(1) << "Results: " << json_args;
287 DispatchEventToExtension(extension_id, kOnResultEvent, json_args); 302 DispatchEventToExtension(extension_id, kOnResultEvent, json_args);
288 } 303 }
289 304
290 void SpeechInputExtensionManager::OnRecognitionStart(int session_id) { 305 void SpeechInputExtensionManager::OnRecognitionStart(int session_id) {
291 DCHECK_EQ(session_id, kSpeechInputSessionId); 306 DCHECK_EQ(session_id, speech_recognition_session_id_);
292 } 307 }
293 308
294 void SpeechInputExtensionManager::OnAudioStart(int session_id) { 309 void SpeechInputExtensionManager::OnAudioStart(int session_id) {
295 VLOG(1) << "OnAudioStart"; 310 VLOG(1) << "OnAudioStart";
296 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 311 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
297 DCHECK_EQ(session_id, kSpeechInputSessionId); 312 DCHECK_EQ(session_id, speech_recognition_session_id_);
298 313
299 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 314 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
300 base::Bind(&SpeechInputExtensionManager::DidStartReceivingAudioOnUIThread, 315 base::Bind(&SpeechInputExtensionManager::DidStartReceivingAudioOnUIThread,
301 this)); 316 this));
302 } 317 }
303 318
304 void SpeechInputExtensionManager::OnAudioEnd(int session_id) { 319 void SpeechInputExtensionManager::OnAudioEnd(int session_id) {
305 DCHECK_EQ(session_id, kSpeechInputSessionId);
306 } 320 }
307 321
308 void SpeechInputExtensionManager::OnRecognitionEnd(int session_id) { 322 void SpeechInputExtensionManager::OnRecognitionEnd(int session_id) {
309 DCHECK_EQ(session_id, kSpeechInputSessionId); 323 // In the very exceptional case in which we requested a new recognition before
324 // the previous one ended, don't clobber the speech_recognition_session_id_.
325 if (speech_recognition_session_id_ == session_id) {
326 is_recognition_in_progress_ = false;
327 speech_recognition_session_id_ =
328 SpeechRecognitionManager::kSessionIDInvalid;
329 }
310 } 330 }
311 331
312 void SpeechInputExtensionManager::DidStartReceivingAudioOnUIThread() { 332 void SpeechInputExtensionManager::DidStartReceivingAudioOnUIThread() {
313 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 333 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
314 334
315 base::AutoLock auto_lock(state_lock_); 335 base::AutoLock auto_lock(state_lock_);
316 if (state_ == kShutdown) 336 if (state_ == kShutdown)
317 return; 337 return;
318 338
319 DCHECK_EQ(state_, kStarting); 339 DCHECK_EQ(state_, kStarting);
320 VLOG(1) << "State changed to recording"; 340 VLOG(1) << "State changed to recording";
321 state_ = kRecording; 341 state_ = kRecording;
322 342
323 const extensions::Extension* extension = profile_->GetExtensionService()-> 343 DCHECK(profile_);
324 GetExtensionById(extension_id_in_use_, true); 344 profile_->GetPrefs()->SetBoolean(
325 DCHECK(extension); 345 prefs::kSpeechInputTrayNotificationShown, true);
326
327 bool show_notification = !profile_->GetPrefs()->GetBoolean(
328 prefs::kSpeechInputTrayNotificationShown);
329
330 if (!notification_.get())
331 notification_ = new SpeechRecognitionTrayIconController();
332 notification_->Show(UTF8ToUTF16(extension->name()), show_notification);
333
334 if (show_notification) {
335 profile_->GetPrefs()->SetBoolean(
336 prefs::kSpeechInputTrayNotificationShown, true);
337 }
338 346
339 VLOG(1) << "Sending start notification"; 347 VLOG(1) << "Sending start notification";
340 content::NotificationService::current()->Notify( 348 content::NotificationService::current()->Notify(
341 chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_RECORDING_STARTED, 349 chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_RECORDING_STARTED,
342 content::Source<Profile>(profile_), 350 content::Source<Profile>(profile_),
343 content::Details<std::string>(&extension_id_in_use_)); 351 content::Details<std::string>(&extension_id_in_use_));
344 } 352 }
345 353
346 void SpeechInputExtensionManager::OnRecognitionError( 354 void SpeechInputExtensionManager::OnRecognitionError(
347 int session_id, const content::SpeechRecognitionError& error) { 355 int session_id, const content::SpeechRecognitionError& error) {
348 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 356 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
349 DCHECK_EQ(session_id, kSpeechInputSessionId); 357 DCHECK_EQ(session_id, speech_recognition_session_id_);
350
351 // Simply return in case of an ERROR_ABORTED, since it is not contemplated
352 // in the speech input extensions architecture.
353 if (error.code == content::SPEECH_RECOGNITION_ERROR_ABORTED)
354 return;
355
356 VLOG(1) << "OnRecognitionError: " << error.code; 358 VLOG(1) << "OnRecognitionError: " << error.code;
357 359
358 base::AutoLock auto_lock(state_lock_); 360 base::AutoLock auto_lock(state_lock_);
359 if (state_ == kShutdown) 361 if (state_ == kShutdown)
360 return; 362 return;
361 363
362 // Release the recognizer object.
363 GetSpeechInputExtensionInterface()->StopRecording(true); 364 GetSpeechInputExtensionInterface()->StopRecording(true);
364 365
365 std::string event_error_code; 366 std::string event_error_code;
366 bool report_to_event = true; 367 bool report_to_event = true;
367 368
368 switch (error.code) { 369 switch (error.code) {
369 case content::SPEECH_RECOGNITION_ERROR_NONE: 370 case content::SPEECH_RECOGNITION_ERROR_NONE:
370 break; 371 break;
371 372
373 case content::SPEECH_RECOGNITION_ERROR_ABORTED:
374 // ERROR_ABORTED is received whenever AbortSession is called on the
375 // manager. However, we want propagate the error only if it is triggered
376 // by an external cause (another recognition started, aborting us), thus
377 // only if it occurs while we are capturing audio.
378 if (state_ == kRecording)
379 event_error_code = kErrorCaptureError;
380 break;
381
372 case content::SPEECH_RECOGNITION_ERROR_AUDIO: 382 case content::SPEECH_RECOGNITION_ERROR_AUDIO:
373 if (state_ == kStarting) { 383 if (state_ == kStarting) {
374 event_error_code = kErrorUnableToStart; 384 event_error_code = kErrorUnableToStart;
375 report_to_event = false; 385 report_to_event = false;
376 } else { 386 } else {
377 event_error_code = kErrorCaptureError; 387 event_error_code = kErrorCaptureError;
378 } 388 }
379 break; 389 break;
380 390
381 case content::SPEECH_RECOGNITION_ERROR_NETWORK: 391 case content::SPEECH_RECOGNITION_ERROR_NETWORK:
(...skipping 22 matching lines...) Expand all
404 414
405 if (!event_error_code.empty()) { 415 if (!event_error_code.empty()) {
406 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 416 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
407 base::Bind(&SpeechInputExtensionManager::DispatchError, 417 base::Bind(&SpeechInputExtensionManager::DispatchError,
408 this, event_error_code, report_to_event)); 418 this, event_error_code, report_to_event));
409 } 419 }
410 } 420 }
411 421
412 void SpeechInputExtensionManager::OnEnvironmentEstimationComplete( 422 void SpeechInputExtensionManager::OnEnvironmentEstimationComplete(
413 int session_id) { 423 int session_id) {
414 DCHECK_EQ(session_id, kSpeechInputSessionId); 424 DCHECK_EQ(session_id, speech_recognition_session_id_);
415 } 425 }
416 426
417 void SpeechInputExtensionManager::OnSoundStart(int session_id) { 427 void SpeechInputExtensionManager::OnSoundStart(int session_id) {
418 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 428 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
419 DCHECK_EQ(session_id, kSpeechInputSessionId); 429 DCHECK_EQ(session_id, speech_recognition_session_id_);
420 VLOG(1) << "OnSoundStart"; 430 VLOG(1) << "OnSoundStart";
421 431
422 std::string json_args; 432 std::string json_args;
423 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 433 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
424 base::Bind(&SpeechInputExtensionManager::DispatchEventToExtension, 434 base::Bind(&SpeechInputExtensionManager::DispatchEventToExtension,
425 this, extension_id_in_use_, std::string(kOnSoundStartEvent), 435 this, extension_id_in_use_, std::string(kOnSoundStartEvent),
426 json_args)); 436 json_args));
427 } 437 }
428 438
429 void SpeechInputExtensionManager::OnSoundEnd(int session_id) { 439 void SpeechInputExtensionManager::OnSoundEnd(int session_id) {
430 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 440 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
431 DCHECK_EQ(session_id, kSpeechInputSessionId);
432 VLOG(1) << "OnSoundEnd"; 441 VLOG(1) << "OnSoundEnd";
433 442
434 std::string json_args; 443 std::string json_args;
435 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 444 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
436 base::Bind(&SpeechInputExtensionManager::DispatchEventToExtension, 445 base::Bind(&SpeechInputExtensionManager::DispatchEventToExtension,
437 this, extension_id_in_use_, std::string(kOnSoundEndEvent), 446 this, extension_id_in_use_, std::string(kOnSoundEndEvent),
438 json_args)); 447 json_args));
439 } 448 }
440 449
441 void SpeechInputExtensionManager::DispatchEventToExtension( 450 void SpeechInputExtensionManager::DispatchEventToExtension(
(...skipping 22 matching lines...) Expand all
464 void SpeechInputExtensionManager::DispatchError( 473 void SpeechInputExtensionManager::DispatchError(
465 const std::string& error, bool dispatch_event) { 474 const std::string& error, bool dispatch_event) {
466 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 475 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
467 476
468 std::string extension_id; 477 std::string extension_id;
469 { 478 {
470 base::AutoLock auto_lock(state_lock_); 479 base::AutoLock auto_lock(state_lock_);
471 if (state_ == kShutdown) 480 if (state_ == kShutdown)
472 return; 481 return;
473 482
474 if (state_ == kRecording) {
475 DCHECK(notification_.get());
476 notification_->Hide();
477 }
478
479 extension_id = extension_id_in_use_; 483 extension_id = extension_id_in_use_;
480 ResetToIdleState(); 484 ResetToIdleState();
481 485
482 // Will set the error property in the ongoing extension function calls. 486 // Will set the error property in the ongoing extension function calls.
483 ExtensionError details(extension_id, error); 487 ExtensionError details(extension_id, error);
484 content::NotificationService::current()->Notify( 488 content::NotificationService::current()->Notify(
485 chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_FAILED, 489 chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_FAILED,
486 content::Source<Profile>(profile_), 490 content::Source<Profile>(profile_),
487 content::Details<ExtensionError>(&details)); 491 content::Details<ExtensionError>(&details));
488 } 492 }
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
524 528
525 case kRecording: 529 case kRecording:
526 case kStopping: 530 case kStopping:
527 *error = kErrorInvalidOperation; 531 *error = kErrorInvalidOperation;
528 return false; 532 return false;
529 533
530 default: 534 default:
531 NOTREACHED(); 535 NOTREACHED();
532 } 536 }
533 537
538 const extensions::Extension* extension = profile_->GetExtensionService()->
539 GetExtensionById(extension_id, true);
540 DCHECK(extension);
541 const string16& extension_name = UTF8ToUTF16(extension->name());
542
534 extension_id_in_use_ = extension_id; 543 extension_id_in_use_ = extension_id;
535 VLOG(1) << "State changed to starting"; 544 VLOG(1) << "State changed to starting";
536 state_ = kStarting; 545 state_ = kStarting;
537 546
547 // Checks if the security notification balloon has been already shown (only
548 // once for a profile). It is reset on DidStartReceivingAudioOnUIThread.
549 scoped_refptr<net::URLRequestContextGetter> url_request_context_getter =
550 profile_->GetRequestContext();
551 const bool show_notification = !profile_->GetPrefs()->GetBoolean(
552 prefs::kSpeechInputTrayNotificationShown);
553
538 BrowserThread::PostTask( 554 BrowserThread::PostTask(
539 BrowserThread::IO, FROM_HERE, 555 BrowserThread::IO, FROM_HERE,
540 base::Bind(&SpeechInputExtensionManager::StartOnIOThread, this, 556 base::Bind(&SpeechInputExtensionManager::StartOnIOThread, this,
541 profile_->GetRequestContext(), language, grammar, 557 url_request_context_getter, extension_name, language, grammar,
542 filter_profanities)); 558 filter_profanities, show_notification));
543 return true; 559 return true;
544 } 560 }
545 561
546 void SpeechInputExtensionManager::StartOnIOThread( 562 void SpeechInputExtensionManager::StartOnIOThread(
547 net::URLRequestContextGetter* context_getter, 563 scoped_refptr<net::URLRequestContextGetter> context_getter,
564 const string16& extension_name,
548 const std::string& language, 565 const std::string& language,
549 const std::string& grammar, 566 const std::string& grammar,
550 bool filter_profanities) { 567 bool filter_profanities,
568 bool show_notification) {
551 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 569 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
552 VLOG(1) << "Requesting start (IO thread)"; 570 VLOG(1) << "Requesting start (IO thread)";
553 571
554 // Everything put inside the lock to ensure the validity of context_getter, 572 // Everything put inside the lock to ensure the validity of context_getter,
555 // guaranteed while not in the shutdown state. Any ongoing or recognition 573 // guaranteed while not in the shutdown state. Any ongoing or recognition
556 // request will be requested to be aborted when entering the shutdown state. 574 // request will be requested to be aborted when entering the shutdown state.
557 base::AutoLock auto_lock(state_lock_); 575 base::AutoLock auto_lock(state_lock_);
558 if (state_ == kShutdown) 576 if (state_ == kShutdown)
559 return; 577 return;
560 578
579 // TODO(primiano) These two checks below could be avoided, since they are
580 // already handled in the speech recognition classes. However, since the
581 // speech input extensions tests are bypassing the manager, we need them to
582 // pass the tests. A complete unit test which puts all the pieces together,
583 // mocking just the endpoints (the audio input controller and the URL fetcher)
584 // should be written.
561 if (!GetSpeechInputExtensionInterface()->HasAudioInputDevices()) { 585 if (!GetSpeechInputExtensionInterface()->HasAudioInputDevices()) {
562 BrowserThread::PostTask( 586 BrowserThread::PostTask(
563 BrowserThread::UI, FROM_HERE, 587 BrowserThread::UI, FROM_HERE,
564 base::Bind(&SpeechInputExtensionManager::DispatchError, this, 588 base::Bind(&SpeechInputExtensionManager::DispatchError, this,
565 std::string(kErrorNoRecordingDeviceFound), false)); 589 std::string(kErrorNoRecordingDeviceFound), false));
566 return; 590 return;
567 } 591 }
568 592
569 if (GetSpeechInputExtensionInterface()->IsCapturingAudio()) { 593 if (GetSpeechInputExtensionInterface()->IsCapturingAudio()) {
570 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 594 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
571 base::Bind(&SpeechInputExtensionManager::DispatchError, this, 595 base::Bind(&SpeechInputExtensionManager::DispatchError, this,
572 std::string(kErrorRecordingDeviceInUse), false)); 596 std::string(kErrorRecordingDeviceInUse), false));
573 return; 597 return;
574 } 598 }
575 599
576 GetSpeechInputExtensionInterface()->StartRecording( 600 GetSpeechInputExtensionInterface()->StartRecording(this,
577 this, context_getter, kSpeechInputSessionId, language, grammar, 601 context_getter,
578 filter_profanities); 602 extension_name,
603 language,
604 grammar,
605 filter_profanities,
606 show_notification);
579 } 607 }
580 608
581 bool SpeechInputExtensionManager::HasAudioInputDevices() { 609 bool SpeechInputExtensionManager::HasAudioInputDevices() {
582 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 610 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
583 return SpeechRecognitionManager::GetInstance()->HasAudioInputDevices(); 611 return SpeechRecognitionManager::GetInstance()->HasAudioInputDevices();
584 } 612 }
585 613
586 bool SpeechInputExtensionManager::IsCapturingAudio() { 614 bool SpeechInputExtensionManager::IsCapturingAudio() {
587 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 615 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
588 return SpeechRecognitionManager::GetInstance()->IsCapturingAudio(); 616 return SpeechRecognitionManager::GetInstance()->IsCapturingAudio();
589 } 617 }
590 618
591 void SpeechInputExtensionManager::IsRecording( 619 void SpeechInputExtensionManager::IsRecording(
592 const IsRecordingCallback& callback) { 620 const IsRecordingCallback& callback) {
593 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 621 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
594 BrowserThread::PostTask( 622 BrowserThread::PostTask(
595 BrowserThread::IO, FROM_HERE, 623 BrowserThread::IO, FROM_HERE,
596 base::Bind(&SpeechInputExtensionManager::IsRecordingOnIOThread, 624 base::Bind(&SpeechInputExtensionManager::IsRecordingOnIOThread,
597 this, callback)); 625 this, callback));
598 } 626 }
599 627
600 void SpeechInputExtensionManager::IsRecordingOnIOThread( 628 void SpeechInputExtensionManager::IsRecordingOnIOThread(
601 const IsRecordingCallback& callback) { 629 const IsRecordingCallback& callback) {
602 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 630 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
603 631
604 bool result = GetSpeechInputExtensionInterface()->IsCapturingAudio(); 632 bool result = GetSpeechInputExtensionInterface()->IsCapturingAudio();
633
605 BrowserThread::PostTask( 634 BrowserThread::PostTask(
606 BrowserThread::UI, FROM_HERE, 635 BrowserThread::UI, FROM_HERE,
607 base::Bind(&SpeechInputExtensionManager::IsRecordingOnUIThread, 636 base::Bind(&SpeechInputExtensionManager::IsRecordingOnUIThread,
608 this, callback, result)); 637 this, callback, result));
609 } 638 }
610 639
611 void SpeechInputExtensionManager::IsRecordingOnUIThread( 640 void SpeechInputExtensionManager::IsRecordingOnUIThread(
612 const IsRecordingCallback& callback, 641 const IsRecordingCallback& callback,
613 bool result) { 642 bool result) {
614 BrowserThread::CurrentlyOn(BrowserThread::UI); 643 BrowserThread::CurrentlyOn(BrowserThread::UI);
615 callback.Run(result); 644 callback.Run(result);
616 } 645 }
617 646
618 void SpeechInputExtensionManager::StartRecording( 647 void SpeechInputExtensionManager::StartRecording(
619 content::SpeechRecognitionEventListener* listener, 648 content::SpeechRecognitionEventListener* listener,
620 net::URLRequestContextGetter* context_getter, 649 net::URLRequestContextGetter* context_getter,
621 int session_id, 650 const string16& extension_name,
622 const std::string& language, 651 const std::string& language,
623 const std::string& grammar, 652 const std::string& grammar,
624 bool filter_profanities) { 653 bool filter_profanities,
654 bool show_notification) {
625 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 655 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
626 DCHECK(!recognizer_); 656
627 recognizer_ = content::SpeechRecognizer::Create( 657 content::SpeechRecognitionSessionContext context;
628 listener, session_id, language, grammar, context_getter, 658 context.requested_by_page_element = false;
629 filter_profanities, "", ""); 659 context.is_first_request_for_context = show_notification;
630 recognizer_->StartRecognition(); 660 context.context_name = extension_name;
661
662 content::SpeechRecognitionSessionConfig config;
663 // config.is_one_shot = true; // TODO(primiano) Uncomment when CL2.0 lands.
664 config.language = language;
665 config.grammars.push_back(content::SpeechRecognitionGrammar(grammar));
666 config.initial_context = context;
667 config.url_request_context_getter = context_getter;
668 config.filter_profanities = filter_profanities;
669 config.event_listener = listener;
670
671 DCHECK(!is_recognition_in_progress_);
672 SpeechRecognitionManager& manager = *SpeechRecognitionManager::GetInstance();
673 speech_recognition_session_id_ =
674 manager.CreateSession(config);
675 DCHECK_NE(speech_recognition_session_id_,
676 SpeechRecognitionManager::kSessionIDInvalid);
677 is_recognition_in_progress_ = true;
678 manager.StartSession(speech_recognition_session_id_);
631 } 679 }
632 680
633 bool SpeechInputExtensionManager::HasValidRecognizer() { 681 bool SpeechInputExtensionManager::HasValidRecognizer() {
634 return !!recognizer_; 682 if (!is_recognition_in_progress_)
683 return false;
684 return SpeechRecognitionManager::GetInstance()->IsCapturingAudio();
635 } 685 }
636 686
637 bool SpeechInputExtensionManager::Stop(const std::string& extension_id, 687 bool SpeechInputExtensionManager::Stop(const std::string& extension_id,
638 std::string* error) { 688 std::string* error) {
639 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 689 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
640 DCHECK(error); 690 DCHECK(error);
641 VLOG(1) << "Requesting stop (UI thread)"; 691 VLOG(1) << "Requesting stop (UI thread)";
642 692
643 base::AutoLock auto_lock(state_lock_); 693 base::AutoLock auto_lock(state_lock_);
644 if (state_ == kShutdown || 694 if (state_ == kShutdown ||
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
685 GetSpeechInputExtensionInterface()->StopRecording(false); 735 GetSpeechInputExtensionInterface()->StopRecording(false);
686 736
687 if (state_ == kShutdown) 737 if (state_ == kShutdown)
688 return; 738 return;
689 739
690 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 740 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
691 base::Bind(&SpeechInputExtensionManager::StopSucceededOnUIThread, this)); 741 base::Bind(&SpeechInputExtensionManager::StopSucceededOnUIThread, this));
692 } 742 }
693 743
694 void SpeechInputExtensionManager::StopRecording(bool recognition_failed) { 744 void SpeechInputExtensionManager::StopRecording(bool recognition_failed) {
695 if (recognizer_) { 745 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
696 // Recognition is already cancelled in case of failure. 746 if (!is_recognition_in_progress_)
697 // Double-cancelling leads to assertion failures. 747 return;
698 if (!recognition_failed) 748 DCHECK_NE(speech_recognition_session_id_,
699 recognizer_->AbortRecognition(); 749 SpeechRecognitionManager::kSessionIDInvalid);
700 recognizer_.release(); 750 SpeechRecognitionManager::GetInstance()->AbortSession(
701 } 751 speech_recognition_session_id_);
702 } 752 }
703 753
704 void SpeechInputExtensionManager::StopSucceededOnUIThread() { 754 void SpeechInputExtensionManager::StopSucceededOnUIThread() {
705 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 755 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
706 VLOG(1) << "Stop succeeded (UI thread)"; 756 VLOG(1) << "Stop succeeded (UI thread)";
707 757
708 base::AutoLock auto_lock(state_lock_); 758 base::AutoLock auto_lock(state_lock_);
709 if (state_ == kShutdown) 759 if (state_ == kShutdown)
710 return; 760 return;
711 761
712 std::string extension_id = extension_id_in_use_; 762 std::string extension_id = extension_id_in_use_;
713 ResetToIdleState(); 763 ResetToIdleState();
714 764
715 DCHECK(notification_.get());
716 notification_->Hide();
717
718 content::NotificationService::current()->Notify( 765 content::NotificationService::current()->Notify(
719 chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_RECORDING_STOPPED, 766 chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_RECORDING_STOPPED,
720 // Guarded by the state_ == kShutdown check. 767 // Guarded by the state_ == kShutdown check.
721 content::Source<Profile>(profile_), 768 content::Source<Profile>(profile_),
722 content::Details<std::string>(&extension_id)); 769 content::Details<std::string>(&extension_id));
723 } 770 }
724 771
725 void SpeechInputExtensionManager::OnAudioLevelsChange(int session_id, 772 void SpeechInputExtensionManager::OnAudioLevelsChange(int session_id,
726 float volume, 773 float volume,
727 float noise_volume) { 774 float noise_volume) {}
728 DCHECK_EQ(session_id, kSpeechInputSessionId);
729 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
730 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
731 base::Bind(&SpeechInputExtensionManager::SetInputVolumeOnUIThread,
732 this, volume));
733 }
734
735 void SpeechInputExtensionManager::SetInputVolumeOnUIThread(
736 float volume) {
737 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
738 DCHECK(notification_.get());
739 notification_->SetVUMeterVolume(volume);
740 }
OLDNEW
« no previous file with comments | « chrome/browser/speech/speech_input_extension_manager.h ('k') | content/browser/speech/speech_recognition_manager_impl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698