Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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/extensions/extension_speech_input_manager.h" | 5 #include "chrome/browser/extensions/extension_speech_input_manager.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | |
| 8 #include "base/json/json_writer.h" | |
| 9 #include "base/utf_string_conversions.h" | |
| 10 #include "base/values.h" | |
| 11 #include "chrome/browser/extensions/extension_event_router.h" | |
| 12 #include "chrome/browser/extensions/extension_speech_input_api_constants.h" | |
| 13 #include "chrome/browser/profiles/profile.h" | |
| 7 #include "chrome/browser/profiles/profile_dependency_manager.h" | 14 #include "chrome/browser/profiles/profile_dependency_manager.h" |
| 8 #include "chrome/browser/profiles/profile_keyed_service.h" | 15 #include "chrome/browser/profiles/profile_keyed_service.h" |
| 9 #include "chrome/browser/profiles/profile_keyed_service_factory.h" | 16 #include "chrome/browser/profiles/profile_keyed_service_factory.h" |
| 10 #include "chrome/common/chrome_notification_types.h" | 17 #include "chrome/common/chrome_notification_types.h" |
| 11 #include "chrome/common/extensions/extension.h" | 18 #include "chrome/common/extensions/extension.h" |
| 19 #include "content/browser/browser_thread.h" | |
| 12 #include "content/common/notification_service.h" | 20 #include "content/common/notification_service.h" |
| 13 | 21 |
| 22 using namespace speech_input; | |
| 23 | |
| 24 namespace constants = extension_speech_input_api_constants; | |
| 25 | |
| 14 namespace { | 26 namespace { |
| 15 | 27 |
| 28 // Caller id provided to the speech recognizer. Since only one extension can | |
| 29 // be recording on the same time a constant value is enough as id. | |
| 30 static const int kSpeechCallerId = 1; | |
| 31 | |
| 16 // Wrap an ExtensionSpeechInputManager using scoped_refptr to avoid | 32 // Wrap an ExtensionSpeechInputManager using scoped_refptr to avoid |
| 17 // assertion failures on destruction because of not using release(). | 33 // assertion failures on destruction because of not using release(). |
| 18 class ExtensionSpeechInputManagerWrapper : public ProfileKeyedService { | 34 class ExtensionSpeechInputManagerWrapper : public ProfileKeyedService { |
| 19 public: | 35 public: |
| 20 explicit ExtensionSpeechInputManagerWrapper( | 36 explicit ExtensionSpeechInputManagerWrapper( |
| 21 ExtensionSpeechInputManager* manager) | 37 ExtensionSpeechInputManager* manager) |
| 22 : manager_(manager) {} | 38 : manager_(manager) {} |
| 23 | 39 |
| 24 virtual ~ExtensionSpeechInputManagerWrapper() {} | 40 virtual ~ExtensionSpeechInputManagerWrapper() {} |
| 25 | 41 |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 45 ExtensionSpeechInputManagerWrapper* GetForProfile(Profile* profile); | 61 ExtensionSpeechInputManagerWrapper* GetForProfile(Profile* profile); |
| 46 | 62 |
| 47 private: | 63 private: |
| 48 friend struct DefaultSingletonTraits<Factory>; | 64 friend struct DefaultSingletonTraits<Factory>; |
| 49 | 65 |
| 50 Factory(); | 66 Factory(); |
| 51 virtual ~Factory(); | 67 virtual ~Factory(); |
| 52 | 68 |
| 53 // ProfileKeyedServiceFactory methods: | 69 // ProfileKeyedServiceFactory methods: |
| 54 virtual ProfileKeyedService* BuildServiceInstanceFor( | 70 virtual ProfileKeyedService* BuildServiceInstanceFor( |
| 55 Profile* profile) const; | 71 Profile* profile) const OVERRIDE; |
| 56 virtual bool ServiceRedirectedInIncognito() { return false; } | 72 virtual bool ServiceRedirectedInIncognito() { return false; } |
|
Leandro GraciĆ” Gil
2011/10/11 18:10:51
Adding OVERRIDE to these.
| |
| 57 virtual bool ServiceIsNULLWhileTesting() { return true; } | 73 virtual bool ServiceIsNULLWhileTesting() { return true; } |
| 58 virtual bool ServiceIsCreatedWithProfile() { return true; } | 74 virtual bool ServiceIsCreatedWithProfile() { return true; } |
| 59 | 75 |
| 60 DISALLOW_COPY_AND_ASSIGN(Factory); | 76 DISALLOW_COPY_AND_ASSIGN(Factory); |
| 61 }; | 77 }; |
| 62 | 78 |
| 63 void ExtensionSpeechInputManager::Factory::Initialize() { | 79 void ExtensionSpeechInputManager::Factory::Initialize() { |
| 64 GetInstance(); | 80 GetInstance(); |
| 65 } | 81 } |
| 66 | 82 |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 86 ProfileKeyedService* | 102 ProfileKeyedService* |
| 87 ExtensionSpeechInputManager::Factory::BuildServiceInstanceFor( | 103 ExtensionSpeechInputManager::Factory::BuildServiceInstanceFor( |
| 88 Profile* profile) const { | 104 Profile* profile) const { |
| 89 scoped_refptr<ExtensionSpeechInputManager> manager( | 105 scoped_refptr<ExtensionSpeechInputManager> manager( |
| 90 new ExtensionSpeechInputManager(profile)); | 106 new ExtensionSpeechInputManager(profile)); |
| 91 return new ExtensionSpeechInputManagerWrapper(manager); | 107 return new ExtensionSpeechInputManagerWrapper(manager); |
| 92 } | 108 } |
| 93 | 109 |
| 94 ExtensionSpeechInputManager::ExtensionSpeechInputManager(Profile* profile) | 110 ExtensionSpeechInputManager::ExtensionSpeechInputManager(Profile* profile) |
| 95 : profile_(profile), | 111 : profile_(profile), |
| 96 extension_(NULL) { | 112 state_(kIdle), |
| 113 speech_interface_(NULL) { | |
| 97 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, | 114 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, |
| 98 Source<Profile>(profile_)); | 115 Source<Profile>(profile_)); |
| 99 } | 116 } |
| 100 | 117 |
| 101 ExtensionSpeechInputManager::~ExtensionSpeechInputManager() { | 118 ExtensionSpeechInputManager::~ExtensionSpeechInputManager() { |
| 102 } | 119 } |
| 103 | 120 |
| 104 ExtensionSpeechInputManager* ExtensionSpeechInputManager::GetForProfile( | 121 ExtensionSpeechInputManager* ExtensionSpeechInputManager::GetForProfile( |
| 105 Profile* profile) { | 122 Profile* profile) { |
| 106 ExtensionSpeechInputManagerWrapper *wrapper = | 123 ExtensionSpeechInputManagerWrapper *wrapper = |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 117 void ExtensionSpeechInputManager::Observe(int type, | 134 void ExtensionSpeechInputManager::Observe(int type, |
| 118 const NotificationSource& source, | 135 const NotificationSource& source, |
| 119 const NotificationDetails& details) { | 136 const NotificationDetails& details) { |
| 120 if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) | 137 if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) |
| 121 ExtensionUnloaded(Details<UnloadedExtensionInfo>(details)->extension->id()); | 138 ExtensionUnloaded(Details<UnloadedExtensionInfo>(details)->extension->id()); |
| 122 else | 139 else |
| 123 NOTREACHED(); | 140 NOTREACHED(); |
| 124 } | 141 } |
| 125 | 142 |
| 126 void ExtensionSpeechInputManager::ShutdownOnUIThread() { | 143 void ExtensionSpeechInputManager::ShutdownOnUIThread() { |
| 127 // TODO(leandrogracia): Force stop to speech recognition if active. | 144 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 145 VLOG(1) << "Profile shutting down."; | |
| 146 | |
| 147 base::AutoLock auto_lock(state_lock_); | |
| 148 DCHECK(state_ != kShutdown); | |
| 149 if (state_ != kIdle) { | |
| 150 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | |
| 151 base::Bind(&ExtensionSpeechInputManager::ForceStopOnIOThread, this)); | |
| 152 } | |
| 153 state_ = kShutdown; | |
| 154 VLOG(1) << "Entering the shutdown sink state."; | |
| 128 registrar_.RemoveAll(); | 155 registrar_.RemoveAll(); |
| 129 profile_ = NULL; | 156 profile_ = NULL; |
| 130 } | 157 } |
| 131 | 158 |
| 132 void ExtensionSpeechInputManager::ExtensionUnloaded(const std::string& id) { | 159 void ExtensionSpeechInputManager::ExtensionUnloaded( |
| 133 // TODO(leandrogracia): Force stop to speech recognition if the extension | 160 const std::string& extension_id) { |
| 134 // is currently using the API. | 161 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 135 } | 162 |
| 136 | 163 base::AutoLock auto_lock(state_lock_); |
| 137 void ExtensionSpeechInputManager::Start(const Extension *extension) { | 164 if (state_ == kShutdown) |
| 138 // TODO(leandrogracia): Start speech recognition. | 165 return; |
| 139 } | 166 |
| 140 | 167 VLOG(1) << "Extension unloaded. Requesting to enforce stop..."; |
| 141 void ExtensionSpeechInputManager::Stop(const Extension *extension) { | 168 if (extension_id_in_use_ == extension_id) { |
| 142 // TODO(leandrogracia): Stop speech recognition. | 169 if (state_ != kIdle) { |
| 143 } | 170 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, |
| 144 | 171 base::Bind(&ExtensionSpeechInputManager::ForceStopOnIOThread, this)); |
| 172 } | |
| 173 } | |
| 174 } | |
| 175 | |
| 176 void ExtensionSpeechInputManager::SetExtensionSpeechInterface( | |
| 177 ExtensionSpeechInterface* interface) { | |
| 178 speech_interface_ = interface; | |
| 179 } | |
| 180 | |
| 181 ExtensionSpeechInterface* | |
| 182 ExtensionSpeechInputManager::GetExtensionSpeechInterface() { | |
| 183 return speech_interface_ ? speech_interface_ : this; | |
| 184 } | |
| 185 | |
| 186 void ExtensionSpeechInputManager::SetRecognitionResult( | |
| 187 int caller_id, | |
| 188 const SpeechInputResult& result) { | |
| 189 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 190 DCHECK_EQ(caller_id, kSpeechCallerId); | |
| 191 | |
| 192 // Stopping will start the disassociation with the extension. | |
| 193 // Make a copy to report the results to the proper one. | |
| 194 std::string extension_id = extension_id_in_use_; | |
| 195 ForceStopOnIOThread(); | |
| 196 | |
| 197 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 198 base::Bind(&ExtensionSpeechInputManager::SetRecognitionResultOnUIThread, | |
| 199 this, result, extension_id)); | |
| 200 } | |
| 201 | |
| 202 void ExtensionSpeechInputManager::SetRecognitionResultOnUIThread( | |
| 203 const SpeechInputResult& result, const std::string& extension_id) { | |
| 204 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 205 | |
| 206 { | |
| 207 base::AutoLock auto_lock(state_lock_); | |
| 208 if (state_ == kShutdown) | |
| 209 return; | |
| 210 state_ = kIdle; | |
| 211 } | |
| 212 | |
| 213 ListValue args; | |
| 214 DictionaryValue* js_event = new DictionaryValue(); | |
| 215 args.Append(js_event); | |
| 216 | |
| 217 ListValue* js_hypothesis_array = new ListValue(); | |
| 218 js_event->Set(constants::kHypothesesKey, js_hypothesis_array); | |
| 219 | |
| 220 for (size_t i = 0; i < result.hypotheses.size(); ++i) { | |
| 221 const SpeechInputHypothesis& hypothesis = result.hypotheses[i]; | |
| 222 | |
| 223 DictionaryValue* js_hypothesis_object = new DictionaryValue(); | |
| 224 js_hypothesis_array->Append(js_hypothesis_object); | |
| 225 | |
| 226 js_hypothesis_object->SetString(constants::kUtteranceKey, | |
| 227 UTF16ToUTF8(hypothesis.utterance)); | |
| 228 js_hypothesis_object->SetDouble(constants::kConfidenceKey, | |
| 229 hypothesis.confidence); | |
| 230 } | |
| 231 | |
| 232 std::string json_args; | |
| 233 base::JSONWriter::Write(&args, false, &json_args); | |
| 234 VLOG(1) << "Results: " << json_args; | |
| 235 DispatchEventToExtension(extension_id, constants::kOnResultEvent, json_args); | |
| 236 } | |
| 237 | |
| 238 void ExtensionSpeechInputManager::DidStartReceivingAudio(int caller_id) { | |
| 239 VLOG(1) << "DidStartReceivingAudio"; | |
| 240 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 241 DCHECK_EQ(caller_id, kSpeechCallerId); | |
| 242 | |
| 243 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 244 base::Bind(&ExtensionSpeechInputManager::DidStartReceivingAudioOnUIThread, | |
| 245 this)); | |
| 246 } | |
| 247 | |
| 248 void ExtensionSpeechInputManager::DidCompleteRecording(int caller_id) { | |
| 249 DCHECK_EQ(caller_id, kSpeechCallerId); | |
| 250 } | |
| 251 | |
| 252 void ExtensionSpeechInputManager::DidCompleteRecognition(int caller_id) { | |
| 253 DCHECK_EQ(caller_id, kSpeechCallerId); | |
| 254 } | |
| 255 | |
| 256 void ExtensionSpeechInputManager::DidStartReceivingAudioOnUIThread() { | |
| 257 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 258 | |
| 259 base::AutoLock auto_lock(state_lock_); | |
| 260 if (state_ == kShutdown) | |
| 261 return; | |
| 262 | |
| 263 DCHECK_EQ(state_, kStarting); | |
| 264 VLOG(1) << "State changed to recording"; | |
| 265 state_ = kRecording; | |
| 266 | |
| 267 VLOG(1) << "Sending start notification"; | |
| 268 NotificationService::current()->Notify( | |
| 269 chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_RECORDING_STARTED, | |
| 270 Source<Profile>(profile_), | |
| 271 Details<std::string>(&extension_id_in_use_)); | |
| 272 } | |
| 273 | |
| 274 void ExtensionSpeechInputManager::OnRecognizerError( | |
| 275 int caller_id, SpeechInputError error) { | |
| 276 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 277 DCHECK_EQ(caller_id, kSpeechCallerId); | |
| 278 VLOG(1) << "OnRecognizerError: " << error; | |
| 279 | |
| 280 base::AutoLock auto_lock(state_lock_); | |
| 281 if (state_ == kShutdown) | |
| 282 return; | |
| 283 | |
| 284 // Release the recognizer object. | |
| 285 GetExtensionSpeechInterface()->StopRecording(true); | |
| 286 | |
| 287 std::string event_error_code; | |
| 288 bool report_to_event = true; | |
| 289 | |
| 290 switch (error) { | |
| 291 case kErrorNone: | |
| 292 break; | |
| 293 | |
| 294 case kErrorAudio: | |
| 295 if (state_ == kStarting) { | |
| 296 event_error_code = constants::kErrorUnableToStart; | |
| 297 report_to_event = false; | |
| 298 } else { | |
| 299 event_error_code = constants::kErrorCaptureError; | |
| 300 } | |
| 301 break; | |
| 302 | |
| 303 case kErrorNetwork: | |
| 304 event_error_code = constants::kErrorNetworkError; | |
| 305 break; | |
| 306 | |
| 307 case kErrorBadGrammar: | |
| 308 // No error is returned on invalid language, for example. | |
| 309 // To avoid confusion about when this is would be fired, the invalid | |
| 310 // params error is not being exposed to the onError event. | |
| 311 event_error_code = constants::kErrorUnableToStart; | |
| 312 break; | |
| 313 | |
| 314 case kErrorNoSpeech: | |
| 315 event_error_code = constants::kErrorNoSpeechHeard; | |
| 316 break; | |
| 317 | |
| 318 case kErrorNoMatch: | |
| 319 event_error_code = constants::kErrorNoResults; | |
| 320 break; | |
| 321 | |
| 322 // The remaining kErrorAborted case should never be returned by the server. | |
| 323 default: | |
| 324 NOTREACHED(); | |
| 325 } | |
| 326 | |
| 327 VLOG(1) << "State changed to idle"; | |
| 328 state_ = kIdle; | |
| 329 | |
| 330 if (!event_error_code.empty()) { | |
| 331 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 332 base::Bind(&ExtensionSpeechInputManager::DispatchError, | |
| 333 this, event_error_code, report_to_event)); | |
| 334 } | |
| 335 } | |
| 336 | |
| 337 void ExtensionSpeechInputManager::DidCompleteEnvironmentEstimation( | |
| 338 int caller_id) { | |
| 339 DCHECK_EQ(caller_id, kSpeechCallerId); | |
| 340 } | |
| 341 | |
| 342 void ExtensionSpeechInputManager::DidStartReceivingSpeech(int caller_id) { | |
| 343 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 344 DCHECK_EQ(caller_id, kSpeechCallerId); | |
| 345 VLOG(1) << "DidStartReceivingSpeech"; | |
| 346 | |
| 347 std::string json_args; | |
| 348 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 349 base::Bind(&ExtensionSpeechInputManager::DispatchEventToExtension, | |
| 350 this, extension_id_in_use_, std::string(constants::kOnSoundStartEvent), | |
| 351 json_args)); | |
| 352 } | |
| 353 | |
| 354 void ExtensionSpeechInputManager::DidStopReceivingSpeech(int caller_id) { | |
| 355 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 356 DCHECK_EQ(caller_id, kSpeechCallerId); | |
| 357 VLOG(1) << "DidStopReceivingSpeech"; | |
| 358 | |
| 359 std::string json_args; | |
| 360 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 361 base::Bind(&ExtensionSpeechInputManager::DispatchEventToExtension, | |
| 362 this, extension_id_in_use_, std::string(constants::kOnSoundEndEvent), | |
| 363 json_args)); | |
| 364 } | |
| 365 | |
| 366 void ExtensionSpeechInputManager::DispatchEventToExtension( | |
| 367 const std::string& extension_id, const std::string& event, | |
| 368 const std::string& json_args) { | |
| 369 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 370 | |
| 371 base::AutoLock auto_lock(state_lock_); | |
| 372 if (state_ == kShutdown) | |
| 373 return; | |
| 374 | |
| 375 if (profile_ && profile_->GetExtensionEventRouter()) { | |
| 376 std::string final_args; | |
| 377 if (json_args.empty()) { | |
| 378 ListValue args; | |
| 379 base::JSONWriter::Write(&args, false, &final_args); | |
| 380 } else { | |
| 381 final_args = json_args; | |
| 382 } | |
| 383 | |
| 384 profile_->GetExtensionEventRouter()->DispatchEventToExtension( | |
| 385 extension_id, event, final_args, profile_, GURL()); | |
| 386 } | |
| 387 } | |
| 388 | |
| 389 void ExtensionSpeechInputManager::DispatchError( | |
| 390 const std::string& error, bool dispatch_event) { | |
| 391 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 392 | |
| 393 { | |
| 394 base::AutoLock auto_lock(state_lock_); | |
| 395 if (state_ == kShutdown) | |
| 396 return; | |
| 397 | |
| 398 // Will set the error property in the ongoing extension function calls. | |
| 399 ExtensionError details(extension_id_in_use_, error); | |
| 400 NotificationService::current()->Notify( | |
| 401 chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_FAILED, | |
| 402 Source<Profile>(profile_), | |
| 403 Details<ExtensionError>(&details)); | |
| 404 } | |
| 405 | |
| 406 // Used for errors that are also reported via the onError event. | |
| 407 if (dispatch_event) { | |
| 408 ListValue args; | |
| 409 DictionaryValue *js_error = new DictionaryValue(); | |
| 410 args.Append(js_error); | |
| 411 js_error->SetString(constants::kErrorCodeKey, error); | |
| 412 std::string json_args; | |
| 413 base::JSONWriter::Write(&args, false, &json_args); | |
| 414 DispatchEventToExtension(extension_id_in_use_, | |
| 415 constants::kOnErrorEvent, json_args); | |
| 416 } | |
| 417 } | |
| 418 | |
| 419 bool ExtensionSpeechInputManager::Start(const std::string& extension_id, | |
| 420 const std::string& language, const std::string& grammar, | |
| 421 bool filter_profanities, std::string* error) { | |
| 422 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 423 DCHECK(error); | |
| 424 VLOG(1) << "Requesting start (UI thread)"; | |
| 425 | |
| 426 base::AutoLock auto_lock(state_lock_); | |
| 427 if (state_ == kShutdown || | |
| 428 (!extension_id_in_use_.empty() && extension_id_in_use_ != extension_id)) { | |
| 429 *error = constants::kErrorRequestDenied; | |
| 430 return false; | |
| 431 } | |
| 432 | |
| 433 switch (state_) { | |
| 434 case kIdle: | |
| 435 break; | |
| 436 | |
| 437 case kStarting: | |
| 438 *error = constants::kErrorRequestInProgress; | |
| 439 return false; | |
| 440 | |
| 441 case kRecording: | |
| 442 case kStopping: | |
| 443 *error = constants::kErrorInvalidOperation; | |
| 444 return false; | |
| 445 | |
| 446 default: | |
| 447 NOTREACHED(); | |
| 448 } | |
| 449 | |
| 450 extension_id_in_use_ = extension_id; | |
| 451 VLOG(1) << "State changed to starting"; | |
| 452 state_ = kStarting; | |
| 453 | |
| 454 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | |
| 455 base::Bind(&ExtensionSpeechInputManager::StartOnIOThread, this, | |
| 456 profile_->GetRequestContext(), language, grammar, filter_profanities)); | |
| 457 return true; | |
| 458 } | |
| 459 | |
| 460 void ExtensionSpeechInputManager::StartOnIOThread( | |
| 461 net::URLRequestContextGetter* context_getter, | |
| 462 const std::string& language, const std::string& grammar, | |
| 463 bool filter_profanities) { | |
| 464 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 465 VLOG(1) << "Requesting start (IO thread)"; | |
| 466 | |
| 467 // Everything put inside the lock to ensure the validity of context_getter, | |
| 468 // guaranteed while not in the shutdown state. Any ongoing or recognition | |
| 469 // request will be requested to be aborted when entering the shutdown state. | |
| 470 base::AutoLock auto_lock(state_lock_); | |
| 471 if (state_ == kShutdown) | |
| 472 return; | |
| 473 | |
| 474 if (!GetExtensionSpeechInterface()->CheckForAvailableDevices()) { | |
| 475 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 476 base::Bind(&ExtensionSpeechInputManager::DispatchError, this, | |
| 477 std::string(constants::kErrorNoDevicesAvailable), false)); | |
| 478 return; | |
| 479 } | |
| 480 | |
| 481 GetExtensionSpeechInterface()->StartRecording(this, context_getter, | |
| 482 kSpeechCallerId, language, grammar, filter_profanities); | |
| 483 } | |
| 484 | |
| 485 bool ExtensionSpeechInputManager::CheckForAvailableDevices() { | |
| 486 return AudioManager::GetAudioManager()->HasAudioInputDevices(); | |
| 487 } | |
| 488 | |
| 489 void ExtensionSpeechInputManager::StartRecording( | |
| 490 speech_input::SpeechRecognizerDelegate* delegate, | |
| 491 net::URLRequestContextGetter* context_getter, int caller_id, | |
| 492 const std::string& language, const std::string& grammar, | |
| 493 bool filter_profanities) { | |
| 494 DCHECK(!recognizer_); | |
| 495 recognizer_ = new SpeechRecognizer(delegate, caller_id, language, grammar, | |
| 496 context_getter, filter_profanities, "", ""); | |
| 497 recognizer_->StartRecording(); | |
| 498 } | |
| 499 | |
| 500 bool ExtensionSpeechInputManager::HasValidRecognizer() const { | |
| 501 return recognizer_; | |
| 502 } | |
| 503 | |
| 504 bool ExtensionSpeechInputManager::Stop(const std::string& extension_id, | |
| 505 std::string* error) { | |
| 506 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 507 DCHECK(error); | |
| 508 VLOG(1) << "Requesting stop (UI thread)"; | |
| 509 | |
| 510 base::AutoLock auto_lock(state_lock_); | |
| 511 if (state_ == kShutdown || | |
| 512 (!extension_id_in_use_.empty() && extension_id_in_use_ != extension_id)) { | |
| 513 *error = constants::kErrorRequestDenied; | |
| 514 return false; | |
| 515 } | |
| 516 | |
| 517 switch (state_) { | |
| 518 case kRecording: | |
| 519 break; | |
| 520 | |
| 521 case kStopping: | |
| 522 *error = constants::kErrorRequestInProgress; | |
| 523 return false; | |
| 524 | |
| 525 case kIdle: | |
| 526 case kStarting: | |
| 527 *error = constants::kErrorInvalidOperation; | |
| 528 return false; | |
| 529 | |
| 530 default: | |
| 531 NOTREACHED(); | |
| 532 } | |
| 533 | |
| 534 // Guarded by the state lock. | |
| 535 DCHECK(GetExtensionSpeechInterface()->HasValidRecognizer()); | |
| 536 | |
| 537 VLOG(1) << "State changed to stopping"; | |
| 538 state_ = kStopping; | |
| 539 | |
| 540 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | |
| 541 base::Bind(&ExtensionSpeechInputManager::ForceStopOnIOThread, this)); | |
| 542 return true; | |
| 543 } | |
| 544 | |
| 545 void ExtensionSpeechInputManager::ForceStopOnIOThread() { | |
| 546 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 547 VLOG(1) << "Requesting forced stop (IO thread)"; | |
| 548 | |
| 549 base::AutoLock auto_lock(state_lock_); | |
| 550 DCHECK(state_ != kIdle); | |
| 551 | |
| 552 GetExtensionSpeechInterface()->StopRecording(false); | |
| 553 | |
| 554 if (state_ == kShutdown) | |
| 555 return; | |
| 556 | |
| 557 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 558 base::Bind(&ExtensionSpeechInputManager::StopSucceededOnUIThread, this)); | |
| 559 } | |
| 560 | |
| 561 void ExtensionSpeechInputManager::StopRecording(bool recognition_failed) { | |
| 562 if (recognizer_) { | |
| 563 // Recognition is already cancelled in case of failure. | |
| 564 // Double-cancelling leads to assertion failures. | |
| 565 if (!recognition_failed) | |
| 566 recognizer_->CancelRecognition(); | |
| 567 recognizer_.release(); | |
| 568 } | |
| 569 } | |
| 570 | |
| 571 void ExtensionSpeechInputManager::StopSucceededOnUIThread() { | |
| 572 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 573 VLOG(1) << "Stop succeeded (UI thread)"; | |
| 574 | |
| 575 base::AutoLock auto_lock(state_lock_); | |
| 576 if (state_ == kShutdown) | |
| 577 return; | |
| 578 | |
| 579 state_ = kIdle; | |
| 580 VLOG(1) << "State changed to idle"; | |
| 581 | |
| 582 NotificationService::current()->Notify( | |
| 583 chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_RECORDING_STOPPED, | |
| 584 Source<Profile>(profile_), // Guarded by the state_ == kShutdown check. | |
| 585 Details<std::string>(&extension_id_in_use_)); | |
| 586 | |
| 587 extension_id_in_use_.clear(); | |
| 588 } | |
| OLD | NEW |