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