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

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: Fix for dealing with tests destroying profile out of order. 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::UnregisterFromManagerOnIOThread,
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::UnregisterFromManagerOnIOThread() {
Satish 2012/05/24 09:50:44 UnregisterFromManager is a bit confusing since thi
Primiano Tucci (use gerrit) 2012/05/24 10:14:07 Uh right. Done
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, causing a crash on this line.
Satish 2012/05/24 09:50:44 'causing a crash on this line' -> 'so SpeechRecogn
Primiano Tucci (use gerrit) 2012/05/24 10:14:07 Done.
221 if (SpeechRecognitionManager* mgr = SpeechRecognitionManager::GetInstance())
222 mgr->AbortAllSessionsForListener(this);
223 }
224
211 void SpeechInputExtensionManager::ExtensionUnloaded( 225 void SpeechInputExtensionManager::ExtensionUnloaded(
212 const std::string& extension_id) { 226 const std::string& extension_id) {
213 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 227 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
214 228
215 base::AutoLock auto_lock(state_lock_); 229 base::AutoLock auto_lock(state_lock_);
216 if (state_ == kShutdown) 230 if (state_ == kShutdown)
217 return; 231 return;
218 232
219 VLOG(1) << "Extension unloaded. Requesting to enforce stop..."; 233 VLOG(1) << "Extension unloaded. Requesting to enforce stop...";
220 if (extension_id_in_use_ == extension_id) { 234 if (extension_id_in_use_ == extension_id) {
(...skipping 17 matching lines...) Expand all
238 void SpeechInputExtensionManager::ResetToIdleState() { 252 void SpeechInputExtensionManager::ResetToIdleState() {
239 VLOG(1) << "State changed to idle. Deassociating any extensions."; 253 VLOG(1) << "State changed to idle. Deassociating any extensions.";
240 state_ = kIdle; 254 state_ = kIdle;
241 extension_id_in_use_.clear(); 255 extension_id_in_use_.clear();
242 } 256 }
243 257
244 void SpeechInputExtensionManager::OnRecognitionResult( 258 void SpeechInputExtensionManager::OnRecognitionResult(
245 int session_id, 259 int session_id,
246 const content::SpeechRecognitionResult& result) { 260 const content::SpeechRecognitionResult& result) {
247 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 261 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
248 DCHECK_EQ(session_id, kSpeechInputSessionId); 262 DCHECK_EQ(session_id, speech_recognition_session_id_);
249 263
250 // Stopping will start the disassociation with the extension. 264 // Stopping will start the disassociation with the extension.
251 // Make a copy to report the results to the proper one. 265 // Make a copy to report the results to the proper one.
252 std::string extension_id = extension_id_in_use_; 266 std::string extension_id = extension_id_in_use_;
253 ForceStopOnIOThread(); 267 ForceStopOnIOThread();
254 268
255 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 269 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
256 base::Bind(&SpeechInputExtensionManager::SetRecognitionResultOnUIThread, 270 base::Bind(&SpeechInputExtensionManager::SetRecognitionResultOnUIThread,
257 this, result, extension_id)); 271 this, result, extension_id));
258 } 272 }
(...skipping 22 matching lines...) Expand all
281 hypothesis.confidence); 295 hypothesis.confidence);
282 } 296 }
283 297
284 std::string json_args; 298 std::string json_args;
285 base::JSONWriter::Write(&args, &json_args); 299 base::JSONWriter::Write(&args, &json_args);
286 VLOG(1) << "Results: " << json_args; 300 VLOG(1) << "Results: " << json_args;
287 DispatchEventToExtension(extension_id, kOnResultEvent, json_args); 301 DispatchEventToExtension(extension_id, kOnResultEvent, json_args);
288 } 302 }
289 303
290 void SpeechInputExtensionManager::OnRecognitionStart(int session_id) { 304 void SpeechInputExtensionManager::OnRecognitionStart(int session_id) {
291 DCHECK_EQ(session_id, kSpeechInputSessionId); 305 DCHECK_EQ(session_id, speech_recognition_session_id_);
292 } 306 }
293 307
294 void SpeechInputExtensionManager::OnAudioStart(int session_id) { 308 void SpeechInputExtensionManager::OnAudioStart(int session_id) {
295 VLOG(1) << "OnAudioStart"; 309 VLOG(1) << "OnAudioStart";
296 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 310 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
297 DCHECK_EQ(session_id, kSpeechInputSessionId); 311 DCHECK_EQ(session_id, speech_recognition_session_id_);
298 312
299 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 313 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
300 base::Bind(&SpeechInputExtensionManager::DidStartReceivingAudioOnUIThread, 314 base::Bind(&SpeechInputExtensionManager::DidStartReceivingAudioOnUIThread,
301 this)); 315 this));
302 } 316 }
303 317
304 void SpeechInputExtensionManager::OnAudioEnd(int session_id) { 318 void SpeechInputExtensionManager::OnAudioEnd(int session_id) {
305 DCHECK_EQ(session_id, kSpeechInputSessionId);
306 } 319 }
307 320
308 void SpeechInputExtensionManager::OnRecognitionEnd(int session_id) { 321 void SpeechInputExtensionManager::OnRecognitionEnd(int session_id) {
309 DCHECK_EQ(session_id, kSpeechInputSessionId); 322 // In the very exceptional case in which we requested a new recognition before
323 // the previous one ended, don't clobber the speech_recognition_session_id_.
324 if (speech_recognition_session_id_ == session_id) {
325 is_recognition_in_progress_ = false;
326 speech_recognition_session_id_ =
327 SpeechRecognitionManager::kSessionIDInvalid;
328 }
310 } 329 }
311 330
312 void SpeechInputExtensionManager::DidStartReceivingAudioOnUIThread() { 331 void SpeechInputExtensionManager::DidStartReceivingAudioOnUIThread() {
313 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 332 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
314 333
315 base::AutoLock auto_lock(state_lock_); 334 base::AutoLock auto_lock(state_lock_);
316 if (state_ == kShutdown) 335 if (state_ == kShutdown)
317 return; 336 return;
318 337
319 DCHECK_EQ(state_, kStarting); 338 DCHECK_EQ(state_, kStarting);
320 VLOG(1) << "State changed to recording"; 339 VLOG(1) << "State changed to recording";
321 state_ = kRecording; 340 state_ = kRecording;
322 341
323 const extensions::Extension* extension = profile_->GetExtensionService()-> 342 DCHECK(profile_);
324 GetExtensionById(extension_id_in_use_, true); 343 profile_->GetPrefs()->SetBoolean(
325 DCHECK(extension); 344 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 345
339 VLOG(1) << "Sending start notification"; 346 VLOG(1) << "Sending start notification";
340 content::NotificationService::current()->Notify( 347 content::NotificationService::current()->Notify(
341 chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_RECORDING_STARTED, 348 chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_RECORDING_STARTED,
342 content::Source<Profile>(profile_), 349 content::Source<Profile>(profile_),
343 content::Details<std::string>(&extension_id_in_use_)); 350 content::Details<std::string>(&extension_id_in_use_));
344 } 351 }
345 352
346 void SpeechInputExtensionManager::OnRecognitionError( 353 void SpeechInputExtensionManager::OnRecognitionError(
347 int session_id, const content::SpeechRecognitionError& error) { 354 int session_id, const content::SpeechRecognitionError& error) {
348 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 355 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
349 DCHECK_EQ(session_id, kSpeechInputSessionId); 356 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; 357 VLOG(1) << "OnRecognitionError: " << error.code;
357 358
358 base::AutoLock auto_lock(state_lock_); 359 base::AutoLock auto_lock(state_lock_);
359 if (state_ == kShutdown) 360 if (state_ == kShutdown)
360 return; 361 return;
361 362
362 // Release the recognizer object.
363 GetSpeechInputExtensionInterface()->StopRecording(true); 363 GetSpeechInputExtensionInterface()->StopRecording(true);
364 364
365 std::string event_error_code; 365 std::string event_error_code;
366 bool report_to_event = true; 366 bool report_to_event = true;
367 367
368 switch (error.code) { 368 switch (error.code) {
369 case content::SPEECH_RECOGNITION_ERROR_NONE: 369 case content::SPEECH_RECOGNITION_ERROR_NONE:
370 break; 370 break;
371 371
372 case content::SPEECH_RECOGNITION_ERROR_ABORTED:
373 // ERROR_ABORTED is received whenever AbortSession is called on the
374 // manager. However, we want propagate the error only if it is triggered
375 // by an external cause (another recognition started, aborting us), thus
376 // only if it occurs while we are capturing audio.
377 if (state_ == kRecording)
378 event_error_code = kErrorCaptureError;
379 break;
380
372 case content::SPEECH_RECOGNITION_ERROR_AUDIO: 381 case content::SPEECH_RECOGNITION_ERROR_AUDIO:
373 if (state_ == kStarting) { 382 if (state_ == kStarting) {
374 event_error_code = kErrorUnableToStart; 383 event_error_code = kErrorUnableToStart;
375 report_to_event = false; 384 report_to_event = false;
376 } else { 385 } else {
377 event_error_code = kErrorCaptureError; 386 event_error_code = kErrorCaptureError;
378 } 387 }
379 break; 388 break;
380 389
381 case content::SPEECH_RECOGNITION_ERROR_NETWORK: 390 case content::SPEECH_RECOGNITION_ERROR_NETWORK:
(...skipping 22 matching lines...) Expand all
404 413
405 if (!event_error_code.empty()) { 414 if (!event_error_code.empty()) {
406 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 415 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
407 base::Bind(&SpeechInputExtensionManager::DispatchError, 416 base::Bind(&SpeechInputExtensionManager::DispatchError,
408 this, event_error_code, report_to_event)); 417 this, event_error_code, report_to_event));
409 } 418 }
410 } 419 }
411 420
412 void SpeechInputExtensionManager::OnEnvironmentEstimationComplete( 421 void SpeechInputExtensionManager::OnEnvironmentEstimationComplete(
413 int session_id) { 422 int session_id) {
414 DCHECK_EQ(session_id, kSpeechInputSessionId); 423 DCHECK_EQ(session_id, speech_recognition_session_id_);
415 } 424 }
416 425
417 void SpeechInputExtensionManager::OnSoundStart(int session_id) { 426 void SpeechInputExtensionManager::OnSoundStart(int session_id) {
418 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 427 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
419 DCHECK_EQ(session_id, kSpeechInputSessionId); 428 DCHECK_EQ(session_id, speech_recognition_session_id_);
420 VLOG(1) << "OnSoundStart"; 429 VLOG(1) << "OnSoundStart";
421 430
422 std::string json_args; 431 std::string json_args;
423 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 432 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
424 base::Bind(&SpeechInputExtensionManager::DispatchEventToExtension, 433 base::Bind(&SpeechInputExtensionManager::DispatchEventToExtension,
425 this, extension_id_in_use_, std::string(kOnSoundStartEvent), 434 this, extension_id_in_use_, std::string(kOnSoundStartEvent),
426 json_args)); 435 json_args));
427 } 436 }
428 437
429 void SpeechInputExtensionManager::OnSoundEnd(int session_id) { 438 void SpeechInputExtensionManager::OnSoundEnd(int session_id) {
430 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 439 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
431 DCHECK_EQ(session_id, kSpeechInputSessionId);
432 VLOG(1) << "OnSoundEnd"; 440 VLOG(1) << "OnSoundEnd";
433 441
434 std::string json_args; 442 std::string json_args;
435 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 443 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
436 base::Bind(&SpeechInputExtensionManager::DispatchEventToExtension, 444 base::Bind(&SpeechInputExtensionManager::DispatchEventToExtension,
437 this, extension_id_in_use_, std::string(kOnSoundEndEvent), 445 this, extension_id_in_use_, std::string(kOnSoundEndEvent),
438 json_args)); 446 json_args));
439 } 447 }
440 448
441 void SpeechInputExtensionManager::DispatchEventToExtension( 449 void SpeechInputExtensionManager::DispatchEventToExtension(
(...skipping 22 matching lines...) Expand all
464 void SpeechInputExtensionManager::DispatchError( 472 void SpeechInputExtensionManager::DispatchError(
465 const std::string& error, bool dispatch_event) { 473 const std::string& error, bool dispatch_event) {
466 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 474 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
467 475
468 std::string extension_id; 476 std::string extension_id;
469 { 477 {
470 base::AutoLock auto_lock(state_lock_); 478 base::AutoLock auto_lock(state_lock_);
471 if (state_ == kShutdown) 479 if (state_ == kShutdown)
472 return; 480 return;
473 481
474 if (state_ == kRecording) {
475 DCHECK(notification_.get());
476 notification_->Hide();
477 }
478
479 extension_id = extension_id_in_use_; 482 extension_id = extension_id_in_use_;
480 ResetToIdleState(); 483 ResetToIdleState();
481 484
482 // Will set the error property in the ongoing extension function calls. 485 // Will set the error property in the ongoing extension function calls.
483 ExtensionError details(extension_id, error); 486 ExtensionError details(extension_id, error);
484 content::NotificationService::current()->Notify( 487 content::NotificationService::current()->Notify(
485 chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_FAILED, 488 chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_FAILED,
486 content::Source<Profile>(profile_), 489 content::Source<Profile>(profile_),
487 content::Details<ExtensionError>(&details)); 490 content::Details<ExtensionError>(&details));
488 } 491 }
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
524 527
525 case kRecording: 528 case kRecording:
526 case kStopping: 529 case kStopping:
527 *error = kErrorInvalidOperation; 530 *error = kErrorInvalidOperation;
528 return false; 531 return false;
529 532
530 default: 533 default:
531 NOTREACHED(); 534 NOTREACHED();
532 } 535 }
533 536
537 const extensions::Extension* extension = profile_->GetExtensionService()->
538 GetExtensionById(extension_id, true);
539 DCHECK(extension);
540 const string16& extension_name = UTF8ToUTF16(extension->name());
541
534 extension_id_in_use_ = extension_id; 542 extension_id_in_use_ = extension_id;
535 VLOG(1) << "State changed to starting"; 543 VLOG(1) << "State changed to starting";
536 state_ = kStarting; 544 state_ = kStarting;
537 545
546 // Checks if the security notification balloon has been already shown (only
547 // once for a profile). It is reset on DidStartReceivingAudioOnUIThread.
548 scoped_refptr<net::URLRequestContextGetter> url_request_context_getter =
549 profile_->GetRequestContext();
550 const bool show_notification = !profile_->GetPrefs()->GetBoolean(
551 prefs::kSpeechInputTrayNotificationShown);
552
538 BrowserThread::PostTask( 553 BrowserThread::PostTask(
539 BrowserThread::IO, FROM_HERE, 554 BrowserThread::IO, FROM_HERE,
540 base::Bind(&SpeechInputExtensionManager::StartOnIOThread, this, 555 base::Bind(&SpeechInputExtensionManager::StartOnIOThread, this,
541 profile_->GetRequestContext(), language, grammar, 556 url_request_context_getter, extension_name, language, grammar,
542 filter_profanities)); 557 filter_profanities, show_notification));
543 return true; 558 return true;
544 } 559 }
545 560
546 void SpeechInputExtensionManager::StartOnIOThread( 561 void SpeechInputExtensionManager::StartOnIOThread(
547 net::URLRequestContextGetter* context_getter, 562 scoped_refptr<net::URLRequestContextGetter> context_getter,
563 const string16& extension_name,
548 const std::string& language, 564 const std::string& language,
549 const std::string& grammar, 565 const std::string& grammar,
550 bool filter_profanities) { 566 bool filter_profanities,
567 bool show_notification) {
551 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 568 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
552 VLOG(1) << "Requesting start (IO thread)"; 569 VLOG(1) << "Requesting start (IO thread)";
553 570
554 // Everything put inside the lock to ensure the validity of context_getter, 571 // Everything put inside the lock to ensure the validity of context_getter,
555 // guaranteed while not in the shutdown state. Any ongoing or recognition 572 // guaranteed while not in the shutdown state. Any ongoing or recognition
556 // request will be requested to be aborted when entering the shutdown state. 573 // request will be requested to be aborted when entering the shutdown state.
557 base::AutoLock auto_lock(state_lock_); 574 base::AutoLock auto_lock(state_lock_);
558 if (state_ == kShutdown) 575 if (state_ == kShutdown)
559 return; 576 return;
560 577
578 // TODO(primiano) These two checks below could be avoided, since they are
579 // already handled in the speech recognition classes. However, since the
580 // speech input extensions tests are bypassing the manager, we need them to
581 // pass the tests. A complete unit test which puts all the pieces together,
582 // mocking just the endpoints (the audio input controller and the URL fetcher)
583 // should be written.
561 if (!GetSpeechInputExtensionInterface()->HasAudioInputDevices()) { 584 if (!GetSpeechInputExtensionInterface()->HasAudioInputDevices()) {
562 BrowserThread::PostTask( 585 BrowserThread::PostTask(
563 BrowserThread::UI, FROM_HERE, 586 BrowserThread::UI, FROM_HERE,
564 base::Bind(&SpeechInputExtensionManager::DispatchError, this, 587 base::Bind(&SpeechInputExtensionManager::DispatchError, this,
565 std::string(kErrorNoRecordingDeviceFound), false)); 588 std::string(kErrorNoRecordingDeviceFound), false));
566 return; 589 return;
567 } 590 }
568 591
569 if (GetSpeechInputExtensionInterface()->IsCapturingAudio()) { 592 if (GetSpeechInputExtensionInterface()->IsCapturingAudio()) {
570 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 593 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
571 base::Bind(&SpeechInputExtensionManager::DispatchError, this, 594 base::Bind(&SpeechInputExtensionManager::DispatchError, this,
572 std::string(kErrorRecordingDeviceInUse), false)); 595 std::string(kErrorRecordingDeviceInUse), false));
573 return; 596 return;
574 } 597 }
575 598
576 GetSpeechInputExtensionInterface()->StartRecording( 599 GetSpeechInputExtensionInterface()->StartRecording(this,
577 this, context_getter, kSpeechInputSessionId, language, grammar, 600 context_getter,
578 filter_profanities); 601 extension_name,
602 language,
603 grammar,
604 filter_profanities,
605 show_notification);
579 } 606 }
580 607
581 bool SpeechInputExtensionManager::HasAudioInputDevices() { 608 bool SpeechInputExtensionManager::HasAudioInputDevices() {
582 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 609 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
583 return SpeechRecognitionManager::GetInstance()->HasAudioInputDevices(); 610 return SpeechRecognitionManager::GetInstance()->HasAudioInputDevices();
584 } 611 }
585 612
586 bool SpeechInputExtensionManager::IsCapturingAudio() { 613 bool SpeechInputExtensionManager::IsCapturingAudio() {
587 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 614 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
588 return SpeechRecognitionManager::GetInstance()->IsCapturingAudio(); 615 return SpeechRecognitionManager::GetInstance()->IsCapturingAudio();
589 } 616 }
590 617
591 void SpeechInputExtensionManager::IsRecording( 618 void SpeechInputExtensionManager::IsRecording(
592 const IsRecordingCallback& callback) { 619 const IsRecordingCallback& callback) {
593 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 620 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
594 BrowserThread::PostTask( 621 BrowserThread::PostTask(
595 BrowserThread::IO, FROM_HERE, 622 BrowserThread::IO, FROM_HERE,
596 base::Bind(&SpeechInputExtensionManager::IsRecordingOnIOThread, 623 base::Bind(&SpeechInputExtensionManager::IsRecordingOnIOThread,
597 this, callback)); 624 this, callback));
598 } 625 }
599 626
600 void SpeechInputExtensionManager::IsRecordingOnIOThread( 627 void SpeechInputExtensionManager::IsRecordingOnIOThread(
601 const IsRecordingCallback& callback) { 628 const IsRecordingCallback& callback) {
602 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 629 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
603 630
604 bool result = GetSpeechInputExtensionInterface()->IsCapturingAudio(); 631 bool result = GetSpeechInputExtensionInterface()->IsCapturingAudio();
632
605 BrowserThread::PostTask( 633 BrowserThread::PostTask(
606 BrowserThread::UI, FROM_HERE, 634 BrowserThread::UI, FROM_HERE,
607 base::Bind(&SpeechInputExtensionManager::IsRecordingOnUIThread, 635 base::Bind(&SpeechInputExtensionManager::IsRecordingOnUIThread,
608 this, callback, result)); 636 this, callback, result));
609 } 637 }
610 638
611 void SpeechInputExtensionManager::IsRecordingOnUIThread( 639 void SpeechInputExtensionManager::IsRecordingOnUIThread(
612 const IsRecordingCallback& callback, 640 const IsRecordingCallback& callback,
613 bool result) { 641 bool result) {
614 BrowserThread::CurrentlyOn(BrowserThread::UI); 642 BrowserThread::CurrentlyOn(BrowserThread::UI);
615 callback.Run(result); 643 callback.Run(result);
616 } 644 }
617 645
618 void SpeechInputExtensionManager::StartRecording( 646 void SpeechInputExtensionManager::StartRecording(
619 content::SpeechRecognitionEventListener* listener, 647 content::SpeechRecognitionEventListener* listener,
620 net::URLRequestContextGetter* context_getter, 648 net::URLRequestContextGetter* context_getter,
621 int session_id, 649 const string16& extension_name,
622 const std::string& language, 650 const std::string& language,
623 const std::string& grammar, 651 const std::string& grammar,
624 bool filter_profanities) { 652 bool filter_profanities,
653 bool show_notification) {
625 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 654 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
626 DCHECK(!recognizer_); 655
627 recognizer_ = content::SpeechRecognizer::Create( 656 content::SpeechRecognitionSessionContext context;
628 listener, session_id, language, grammar, context_getter, 657 context.requested_by_page_element = false;
629 filter_profanities, "", ""); 658 context.is_first_request_for_context = show_notification;
630 recognizer_->StartRecognition(); 659 context.context_name = extension_name;
660
661 content::SpeechRecognitionSessionConfig config;
662 // config.is_one_shot = true; // TODO(primiano) Uncomment when CL2.0 lands.
663 config.language = language;
664 config.grammars.push_back(content::SpeechRecognitionGrammar(grammar));
665 config.initial_context = context;
666 config.url_request_context_getter = context_getter;
667 config.filter_profanities = filter_profanities;
668 config.event_listener = listener;
669
670 DCHECK(!is_recognition_in_progress_);
671 SpeechRecognitionManager& manager = *SpeechRecognitionManager::GetInstance();
672 speech_recognition_session_id_ =
673 manager.CreateSession(config);
674 DCHECK_NE(speech_recognition_session_id_,
675 SpeechRecognitionManager::kSessionIDInvalid);
676 is_recognition_in_progress_ = true;
677 manager.StartSession(speech_recognition_session_id_);
631 } 678 }
632 679
633 bool SpeechInputExtensionManager::HasValidRecognizer() { 680 bool SpeechInputExtensionManager::HasValidRecognizer() {
634 return !!recognizer_; 681 if (!is_recognition_in_progress_)
682 return false;
683 return SpeechRecognitionManager::GetInstance()->IsCapturingAudio();
635 } 684 }
636 685
637 bool SpeechInputExtensionManager::Stop(const std::string& extension_id, 686 bool SpeechInputExtensionManager::Stop(const std::string& extension_id,
638 std::string* error) { 687 std::string* error) {
639 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 688 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
640 DCHECK(error); 689 DCHECK(error);
641 VLOG(1) << "Requesting stop (UI thread)"; 690 VLOG(1) << "Requesting stop (UI thread)";
642 691
643 base::AutoLock auto_lock(state_lock_); 692 base::AutoLock auto_lock(state_lock_);
644 if (state_ == kShutdown || 693 if (state_ == kShutdown ||
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
685 GetSpeechInputExtensionInterface()->StopRecording(false); 734 GetSpeechInputExtensionInterface()->StopRecording(false);
686 735
687 if (state_ == kShutdown) 736 if (state_ == kShutdown)
688 return; 737 return;
689 738
690 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 739 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
691 base::Bind(&SpeechInputExtensionManager::StopSucceededOnUIThread, this)); 740 base::Bind(&SpeechInputExtensionManager::StopSucceededOnUIThread, this));
692 } 741 }
693 742
694 void SpeechInputExtensionManager::StopRecording(bool recognition_failed) { 743 void SpeechInputExtensionManager::StopRecording(bool recognition_failed) {
695 if (recognizer_) { 744 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
696 // Recognition is already cancelled in case of failure. 745 if (!is_recognition_in_progress_)
697 // Double-cancelling leads to assertion failures. 746 return;
698 if (!recognition_failed) 747 DCHECK_NE(speech_recognition_session_id_,
699 recognizer_->AbortRecognition(); 748 SpeechRecognitionManager::kSessionIDInvalid);
700 recognizer_.release(); 749 SpeechRecognitionManager::GetInstance()->AbortSession(
701 } 750 speech_recognition_session_id_);
702 } 751 }
703 752
704 void SpeechInputExtensionManager::StopSucceededOnUIThread() { 753 void SpeechInputExtensionManager::StopSucceededOnUIThread() {
705 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 754 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
706 VLOG(1) << "Stop succeeded (UI thread)"; 755 VLOG(1) << "Stop succeeded (UI thread)";
707 756
708 base::AutoLock auto_lock(state_lock_); 757 base::AutoLock auto_lock(state_lock_);
709 if (state_ == kShutdown) 758 if (state_ == kShutdown)
710 return; 759 return;
711 760
712 std::string extension_id = extension_id_in_use_; 761 std::string extension_id = extension_id_in_use_;
713 ResetToIdleState(); 762 ResetToIdleState();
714 763
715 DCHECK(notification_.get());
716 notification_->Hide();
717
718 content::NotificationService::current()->Notify( 764 content::NotificationService::current()->Notify(
719 chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_RECORDING_STOPPED, 765 chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_RECORDING_STOPPED,
720 // Guarded by the state_ == kShutdown check. 766 // Guarded by the state_ == kShutdown check.
721 content::Source<Profile>(profile_), 767 content::Source<Profile>(profile_),
722 content::Details<std::string>(&extension_id)); 768 content::Details<std::string>(&extension_id));
723 } 769 }
724 770
725 void SpeechInputExtensionManager::OnAudioLevelsChange(int session_id, 771 void SpeechInputExtensionManager::OnAudioLevelsChange(int session_id,
726 float volume, 772 float volume,
727 float noise_volume) { 773 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