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/json/json_writer.h" | |
| 8 #include "base/utf_string_conversions.h" | |
| 9 #include "base/values.h" | |
| 10 #include "chrome/browser/extensions/extension_event_router.h" | |
| 11 #include "chrome/browser/extensions/extension_speech_input_api_constants.h" | |
| 12 #include "chrome/browser/profiles/profile.h" | |
| 7 #include "chrome/browser/profiles/profile_dependency_manager.h" | 13 #include "chrome/browser/profiles/profile_dependency_manager.h" |
| 8 #include "chrome/browser/profiles/profile_keyed_service.h" | 14 #include "chrome/browser/profiles/profile_keyed_service.h" |
| 9 #include "chrome/browser/profiles/profile_keyed_service_factory.h" | 15 #include "chrome/browser/profiles/profile_keyed_service_factory.h" |
| 10 #include "chrome/common/chrome_notification_types.h" | 16 #include "chrome/common/chrome_notification_types.h" |
| 11 #include "chrome/common/extensions/extension.h" | 17 #include "chrome/common/extensions/extension.h" |
| 18 #include "content/browser/browser_thread.h" | |
| 12 #include "content/common/notification_service.h" | 19 #include "content/common/notification_service.h" |
| 13 | 20 |
| 21 using namespace speech_input; | |
| 22 namespace constants = extension_speech_input_api_constants; | |
| 23 | |
| 14 namespace { | 24 namespace { |
| 15 | 25 |
| 16 // Wrap an ExtensionSpeechInputManager using scoped_refptr to avoid | 26 // Wrap an ExtensionSpeechInputManager using scoped_refptr to avoid |
| 17 // assertion failures on destruction because of not using release(). | 27 // assertion failures on destruction because of not using release(). |
| 18 class ExtensionSpeechInputManagerWrapper : public ProfileKeyedService { | 28 class ExtensionSpeechInputManagerWrapper : public ProfileKeyedService { |
| 19 public: | 29 public: |
| 20 explicit ExtensionSpeechInputManagerWrapper( | 30 explicit ExtensionSpeechInputManagerWrapper( |
| 21 ExtensionSpeechInputManager* manager) | 31 ExtensionSpeechInputManager* manager) |
| 22 : manager_(manager) {} | 32 : manager_(manager) {} |
| 23 | 33 |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 50 Factory(); | 60 Factory(); |
| 51 virtual ~Factory(); | 61 virtual ~Factory(); |
| 52 | 62 |
| 53 // ProfileKeyedServiceFactory methods: | 63 // ProfileKeyedServiceFactory methods: |
| 54 virtual ProfileKeyedService* BuildServiceInstanceFor( | 64 virtual ProfileKeyedService* BuildServiceInstanceFor( |
| 55 Profile* profile) const; | 65 Profile* profile) const; |
| 56 virtual bool ServiceRedirectedInIncognito() { return false; } | 66 virtual bool ServiceRedirectedInIncognito() { return false; } |
| 57 virtual bool ServiceIsNULLWhileTesting() { return true; } | 67 virtual bool ServiceIsNULLWhileTesting() { return true; } |
| 58 virtual bool ServiceIsCreatedWithProfile() { return true; } | 68 virtual bool ServiceIsCreatedWithProfile() { return true; } |
| 59 | 69 |
| 70 static int per_profile_id_; | |
|
Satish
2011/10/08 23:15:07
add a comment here explaining that this is used fo
Leandro Graciá Gil
2011/10/09 10:29:21
Done.
| |
| 71 | |
| 60 DISALLOW_COPY_AND_ASSIGN(Factory); | 72 DISALLOW_COPY_AND_ASSIGN(Factory); |
| 61 }; | 73 }; |
| 62 | 74 |
| 75 int ExtensionSpeechInputManager::Factory::per_profile_id_ = 0; | |
| 76 | |
| 63 void ExtensionSpeechInputManager::Factory::Initialize() { | 77 void ExtensionSpeechInputManager::Factory::Initialize() { |
| 64 GetInstance(); | 78 GetInstance(); |
| 65 } | 79 } |
| 66 | 80 |
| 67 ExtensionSpeechInputManager::Factory* | 81 ExtensionSpeechInputManager::Factory* |
| 68 ExtensionSpeechInputManager::Factory::GetInstance() { | 82 ExtensionSpeechInputManager::Factory::GetInstance() { |
| 69 return Singleton<ExtensionSpeechInputManager::Factory>::get(); | 83 return Singleton<ExtensionSpeechInputManager::Factory>::get(); |
| 70 } | 84 } |
| 71 | 85 |
| 72 ExtensionSpeechInputManagerWrapper* | 86 ExtensionSpeechInputManagerWrapper* |
| 73 ExtensionSpeechInputManager::Factory::GetForProfile( | 87 ExtensionSpeechInputManager::Factory::GetForProfile( |
| 74 Profile* profile) { | 88 Profile* profile) { |
| 75 return static_cast<ExtensionSpeechInputManagerWrapper*>( | 89 return static_cast<ExtensionSpeechInputManagerWrapper*>( |
| 76 GetServiceForProfile(profile, true)); | 90 GetServiceForProfile(profile, true)); |
| 77 } | 91 } |
| 78 | 92 |
| 79 ExtensionSpeechInputManager::Factory::Factory() | 93 ExtensionSpeechInputManager::Factory::Factory() |
| 80 : ProfileKeyedServiceFactory(ProfileDependencyManager::GetInstance()) { | 94 : ProfileKeyedServiceFactory(ProfileDependencyManager::GetInstance()) { |
| 81 } | 95 } |
| 82 | 96 |
| 83 ExtensionSpeechInputManager::Factory::~Factory() { | 97 ExtensionSpeechInputManager::Factory::~Factory() { |
| 84 } | 98 } |
| 85 | 99 |
| 86 ProfileKeyedService* | 100 ProfileKeyedService* |
| 87 ExtensionSpeechInputManager::Factory::BuildServiceInstanceFor( | 101 ExtensionSpeechInputManager::Factory::BuildServiceInstanceFor( |
| 88 Profile* profile) const { | 102 Profile* profile) const { |
| 89 scoped_refptr<ExtensionSpeechInputManager> manager( | 103 scoped_refptr<ExtensionSpeechInputManager> manager( |
| 90 new ExtensionSpeechInputManager(profile)); | 104 new ExtensionSpeechInputManager(profile, ++per_profile_id_)); |
| 91 return new ExtensionSpeechInputManagerWrapper(manager); | 105 return new ExtensionSpeechInputManagerWrapper(manager); |
| 92 } | 106 } |
| 93 | 107 |
| 94 ExtensionSpeechInputManager::ExtensionSpeechInputManager(Profile* profile) | 108 ExtensionSpeechInputManager::ExtensionSpeechInputManager(Profile* profile, |
| 109 size_t speech_caller_id) | |
| 95 : profile_(profile), | 110 : profile_(profile), |
| 96 extension_(NULL) { | 111 speech_caller_id_(speech_caller_id), |
| 112 state_(kIdle) { | |
| 97 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, | 113 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, |
| 98 Source<Profile>(profile_)); | 114 Source<Profile>(profile_)); |
| 99 } | 115 } |
| 100 | 116 |
| 101 ExtensionSpeechInputManager::~ExtensionSpeechInputManager() { | 117 ExtensionSpeechInputManager::~ExtensionSpeechInputManager() { |
| 102 } | 118 } |
| 103 | 119 |
| 104 ExtensionSpeechInputManager* ExtensionSpeechInputManager::GetForProfile( | 120 ExtensionSpeechInputManager* ExtensionSpeechInputManager::GetForProfile( |
| 105 Profile* profile) { | 121 Profile* profile) { |
| 106 ExtensionSpeechInputManagerWrapper *wrapper = | 122 ExtensionSpeechInputManagerWrapper *wrapper = |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 117 void ExtensionSpeechInputManager::Observe(int type, | 133 void ExtensionSpeechInputManager::Observe(int type, |
| 118 const NotificationSource& source, | 134 const NotificationSource& source, |
| 119 const NotificationDetails& details) { | 135 const NotificationDetails& details) { |
| 120 if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) | 136 if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) |
| 121 ExtensionUnloaded(Details<UnloadedExtensionInfo>(details)->extension->id()); | 137 ExtensionUnloaded(Details<UnloadedExtensionInfo>(details)->extension->id()); |
| 122 else | 138 else |
| 123 NOTREACHED(); | 139 NOTREACHED(); |
| 124 } | 140 } |
| 125 | 141 |
| 126 void ExtensionSpeechInputManager::ShutdownOnUIThread() { | 142 void ExtensionSpeechInputManager::ShutdownOnUIThread() { |
| 127 // TODO(leandrogracia): Force stop to speech recognition if active. | 143 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 144 VLOG(1) << "Profile shutting down."; | |
| 145 | |
| 146 state_lock_.Acquire(); | |
| 147 DCHECK(state_ != kShutdown); | |
| 148 if (state_ != kIdle) { | |
| 149 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | |
| 150 NewRunnableMethod(this, | |
| 151 &ExtensionSpeechInputManager::ForceStopOnIOThread)); | |
| 152 } | |
| 153 state_ = kShutdown; | |
| 154 VLOG(1) << "Entering the shutdown sink state."; | |
| 128 registrar_.RemoveAll(); | 155 registrar_.RemoveAll(); |
| 129 profile_ = NULL; | 156 profile_ = NULL; |
| 157 state_lock_.Release(); | |
| 158 } | |
| 159 | |
| 160 bool ExtensionSpeechInputManager::ShuttingDown() { | |
| 161 state_lock_.Acquire(); | |
| 162 bool shutting_down = state_ == kShutdown; | |
| 163 state_lock_.Release(); | |
| 164 return shutting_down; | |
| 130 } | 165 } |
| 131 | 166 |
| 132 void ExtensionSpeechInputManager::ExtensionUnloaded(const std::string& id) { | 167 void ExtensionSpeechInputManager::ExtensionUnloaded(const std::string& id) { |
| 133 // TODO(leandrogracia): Force stop to speech recognition if the extension | 168 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 134 // is currently using the API. | 169 |
| 135 } | 170 VLOG(1) << "Extension unloaded. Requesting to enforce stop..."; |
| 136 | 171 state_lock_.Acquire(); |
| 137 void ExtensionSpeechInputManager::Start(const Extension *extension) { | 172 if (state_ == kShutdown) { |
| 138 // TODO(leandrogracia): Start speech recognition. | 173 state_lock_.Release(); |
| 139 } | 174 return; |
| 140 | 175 } |
| 141 void ExtensionSpeechInputManager::Stop(const Extension *extension) { | 176 |
| 142 // TODO(leandrogracia): Stop speech recognition. | 177 if (!extension_id_.empty() && extension_id_ == id) { |
| 143 } | 178 extension_id_.clear(); |
| 144 | 179 |
| 180 if (state_ != kIdle) { | |
| 181 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | |
| 182 NewRunnableMethod(this, | |
| 183 &ExtensionSpeechInputManager::ForceStopOnIOThread)); | |
| 184 } | |
| 185 } | |
| 186 state_lock_.Release(); | |
| 187 } | |
| 188 | |
| 189 void ExtensionSpeechInputManager::SetRecognitionResult( | |
| 190 int caller_id, | |
| 191 const SpeechInputResult& result) { | |
| 192 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 193 DCHECK_EQ(caller_id, speech_caller_id_); | |
| 194 | |
| 195 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 196 NewRunnableMethod(this, | |
| 197 &ExtensionSpeechInputManager::SetRecognitionResultOnUIThread, result)); | |
| 198 } | |
| 199 | |
| 200 void ExtensionSpeechInputManager::SetRecognitionResultOnUIThread( | |
| 201 const SpeechInputResult& result) { | |
| 202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 203 | |
| 204 state_lock_.Acquire(); | |
| 205 if (state_ == kShutdown) { | |
| 206 state_lock_.Release(); | |
| 207 return; | |
| 208 } | |
| 209 state_ = kIdle; | |
| 210 state_lock_.Release(); | |
| 211 | |
| 212 ListValue args; | |
| 213 DictionaryValue* js_event = new DictionaryValue(); | |
| 214 args.Append(js_event); | |
| 215 | |
| 216 ListValue* js_hypothesis_array = new ListValue(); | |
| 217 js_event->Set(constants::kHypothesesKey, js_hypothesis_array); | |
| 218 | |
| 219 for (size_t i = 0; i < result.hypotheses.size(); ++i) { | |
| 220 const SpeechInputHypothesis& hypothesis = result.hypotheses[i]; | |
| 221 | |
| 222 DictionaryValue* js_hypothesis_object = new DictionaryValue(); | |
| 223 js_hypothesis_array->Append(js_hypothesis_object); | |
| 224 | |
| 225 js_hypothesis_object->SetString(constants::kUtteranceKey, | |
| 226 UTF16ToUTF8(hypothesis.utterance)); | |
| 227 js_hypothesis_object->SetDouble(constants::kConfidenceKey, | |
| 228 hypothesis.confidence); | |
| 229 } | |
| 230 | |
| 231 std::string json_args; | |
| 232 base::JSONWriter::Write(&args, false, &json_args); | |
| 233 VLOG(1) << "Results: " << json_args; | |
| 234 DispatchEventToExtension(constants::kOnSpeechResultEvent, json_args); | |
| 235 } | |
| 236 | |
| 237 void ExtensionSpeechInputManager::DidStartReceivingAudio(int caller_id) { | |
| 238 VLOG(1) << "DidStartReceivingAudio"; | |
| 239 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 240 DCHECK_EQ(caller_id, speech_caller_id_); | |
| 241 | |
| 242 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 243 NewRunnableMethod(this, | |
| 244 &ExtensionSpeechInputManager::DidStartReceivingAudioOnUIThread)); | |
| 245 } | |
| 246 | |
| 247 void ExtensionSpeechInputManager::DidCompleteRecording(int caller_id) { | |
| 248 VLOG(1) << "DidCompleteRecording"; | |
| 249 DCHECK_EQ(caller_id, speech_caller_id_); | |
| 250 } | |
| 251 | |
| 252 void ExtensionSpeechInputManager::DidCompleteRecognition(int caller_id) { | |
| 253 VLOG(1) << "DidCompleteRecognition"; | |
| 254 DCHECK_EQ(caller_id, speech_caller_id_); | |
| 255 } | |
| 256 | |
| 257 void ExtensionSpeechInputManager::DidStartReceivingAudioOnUIThread() { | |
| 258 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 259 | |
| 260 state_lock_.Acquire(); | |
| 261 if (state_ == kShutdown) { | |
| 262 state_lock_.Release(); | |
| 263 return; | |
| 264 } | |
| 265 | |
| 266 DCHECK_EQ(state_, kStarting); | |
| 267 VLOG(1) << "State changed to recording"; | |
| 268 state_ = kRecording; | |
| 269 state_lock_.Release(); | |
| 270 | |
| 271 VLOG(1) << "Sending start notification"; | |
| 272 NotificationService::current()->Notify( | |
| 273 chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_STARTED, | |
| 274 Source<Profile>(profile_), | |
| 275 Details<std::string>(&extension_id_)); | |
| 276 } | |
| 277 | |
| 278 void ExtensionSpeechInputManager::OnRecognizerError( | |
| 279 int caller_id, SpeechInputError error) { | |
| 280 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 281 DCHECK_EQ(caller_id, speech_caller_id_); | |
| 282 VLOG(1) << "OnRecognizerError: " << error; | |
| 283 | |
| 284 state_lock_.Acquire(); | |
| 285 if (state_ == kShutdown) { | |
| 286 state_lock_.Release(); | |
| 287 return; | |
| 288 } | |
| 289 | |
| 290 std::string event_error_code; | |
| 291 switch (error) { | |
| 292 case kErrorNone: | |
| 293 break; | |
| 294 | |
| 295 case kErrorAudio: | |
| 296 if (state_ == kStarting) | |
| 297 event_error_code = constants::kErrorUnableToStart; | |
| 298 else | |
| 299 event_error_code = constants::kErrorCaptureError; | |
| 300 break; | |
| 301 | |
| 302 case kErrorNetwork: | |
| 303 event_error_code = constants::kErrorNetworkError; | |
| 304 break; | |
| 305 | |
| 306 case kErrorBadGrammar: | |
| 307 // No error is returned on invalid language, for example. | |
| 308 // To avoid confusion about when this is would be fired, the invalid | |
| 309 // params error is not being exposed to the onError event. | |
| 310 event_error_code = constants::kErrorUnableToStart; | |
| 311 break; | |
| 312 | |
| 313 case kErrorNoSpeech: | |
| 314 event_error_code = constants::kErrorNoSpeechHeard; | |
| 315 break; | |
| 316 | |
| 317 case kErrorNoMatch: | |
| 318 event_error_code = constants::kErrorNoResults; | |
| 319 break; | |
| 320 | |
| 321 // The remaining kErrorAborted case should never be returned by the server. | |
| 322 default: | |
| 323 NOTREACHED(); | |
| 324 } | |
| 325 | |
| 326 VLOG(1) << "State changed to idle"; | |
| 327 state_ = kIdle; | |
| 328 state_lock_.Release(); | |
| 329 | |
| 330 if (!event_error_code.empty()) | |
| 331 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 332 NewRunnableMethod(this, | |
| 333 &ExtensionSpeechInputManager::DispatchError, event_error_code)); | |
| 334 } | |
| 335 | |
| 336 void ExtensionSpeechInputManager::DidCompleteEnvironmentEstimation( | |
| 337 int caller_id) { | |
| 338 VLOG(1) << "DidCompleteEnvironmentEstimation"; | |
| 339 DCHECK_EQ(caller_id, speech_caller_id_); | |
| 340 } | |
| 341 | |
| 342 void ExtensionSpeechInputManager::DidStartReceivingSpeech(int caller_id) { | |
| 343 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 344 DCHECK_EQ(caller_id, speech_caller_id_); | |
| 345 VLOG(1) << "DidStartReceivingSpeech"; | |
| 346 | |
| 347 ListValue args; | |
| 348 std::string json_args; | |
| 349 base::JSONWriter::Write(&args, false, &json_args); | |
| 350 | |
| 351 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 352 NewRunnableMethod(this, | |
| 353 &ExtensionSpeechInputManager::DispatchEventToExtension, | |
| 354 std::string(constants::kOnSoundStartEvent), json_args)); | |
| 355 } | |
| 356 | |
| 357 void ExtensionSpeechInputManager::DidStopReceivingSpeech(int caller_id) { | |
| 358 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 359 DCHECK_EQ(caller_id, speech_caller_id_); | |
| 360 VLOG(1) << "DidStopReceivingSpeech"; | |
| 361 | |
| 362 ListValue args; | |
| 363 std::string json_args; | |
| 364 base::JSONWriter::Write(&args, false, &json_args); | |
| 365 | |
| 366 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 367 NewRunnableMethod(this, | |
| 368 &ExtensionSpeechInputManager::DispatchEventToExtension, | |
| 369 std::string(constants::kOnSoundEndEvent), json_args)); | |
| 370 } | |
| 371 | |
| 372 void ExtensionSpeechInputManager::DispatchEventToExtension( | |
| 373 const std::string& event, const std::string& json_args) { | |
| 374 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 375 | |
| 376 if (ShuttingDown()) | |
| 377 return; | |
| 378 | |
| 379 if (profile_ && profile_->GetExtensionEventRouter()) | |
| 380 profile_->GetExtensionEventRouter()->DispatchEventToExtension( | |
| 381 extension_id_, event, json_args, profile_, GURL()); | |
| 382 } | |
| 383 | |
| 384 void ExtensionSpeechInputManager::DispatchError(const std::string& code) { | |
| 385 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 386 | |
| 387 if (ShuttingDown()) | |
| 388 return; | |
| 389 | |
| 390 NotificationService::current()->Notify( | |
| 391 chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_FAILED, | |
| 392 Source<Profile>(profile_), | |
| 393 Details<std::string>(&extension_id_)); | |
| 394 | |
| 395 ListValue args; | |
| 396 DictionaryValue *js_error = new DictionaryValue(); | |
| 397 args.Append(js_error); | |
| 398 js_error->SetString(constants::kErrorCodeKey, code); | |
| 399 std::string json_args; | |
| 400 base::JSONWriter::Write(&args, false, &json_args); | |
| 401 DispatchEventToExtension(constants::kOnErrorEvent, json_args); | |
| 402 } | |
| 403 | |
| 404 void ExtensionSpeechInputManager::Start(const std::string& extension_id, | |
| 405 const std::string& language, const std::string& grammar, | |
| 406 bool filter_profanities) { | |
| 407 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 408 VLOG(1) << "Requesting start (UI thread)"; | |
| 409 | |
| 410 if (ShuttingDown()) | |
| 411 return; | |
| 412 | |
| 413 if (!extension_id_.empty() && extension_id != extension_id_) { | |
| 414 DispatchError(constants::kErrorRequestDenied); | |
| 415 return; | |
| 416 } | |
| 417 | |
| 418 state_lock_.Acquire(); | |
| 419 if (state_ != kIdle) { | |
| 420 DCHECK(recognizer_); | |
| 421 state_lock_.Release(); | |
| 422 return; | |
| 423 } | |
| 424 | |
| 425 extension_id_ = extension_id; | |
| 426 VLOG(1) << "Start"; | |
| 427 | |
| 428 VLOG(1) << "State changed to starting"; | |
| 429 state_ = kStarting; | |
| 430 state_lock_.Release(); | |
| 431 | |
| 432 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | |
| 433 NewRunnableMethod(this, &ExtensionSpeechInputManager::StartOnIOThread, | |
| 434 profile_->GetRequestContext(), language, grammar, filter_profanities)); | |
| 435 } | |
| 436 | |
| 437 void ExtensionSpeechInputManager::StartOnIOThread( | |
| 438 net::URLRequestContextGetter* context_getter, | |
| 439 const std::string& language, const std::string& grammar, | |
| 440 bool filter_profanities) { | |
| 441 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 442 VLOG(1) << "Requesting start (IO thread)"; | |
| 443 | |
| 444 // Everything put inside the lock to ensure the validity of context_getter, | |
| 445 // guaranteed while not in the shutdown state. Any ongoing or recognition | |
| 446 // request will be requested to be aborted when entering the shutdown state. | |
| 447 state_lock_.Acquire(); | |
| 448 if (state_ == kShutdown) { | |
| 449 state_lock_.Release(); | |
| 450 return; | |
| 451 } | |
| 452 | |
| 453 if (!AudioManager::GetAudioManager()->HasAudioInputDevices()) { | |
| 454 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 455 NewRunnableMethod(this, &ExtensionSpeechInputManager::DispatchError, | |
| 456 std::string(constants::kErrorNoDevicesAvailable))); | |
| 457 state_lock_.Release(); | |
| 458 return; | |
| 459 } | |
| 460 | |
| 461 recognizer_ = new SpeechRecognizer(this, speech_caller_id_, language, grammar, | |
|
Satish
2011/10/08 23:15:07
since you are directly using a SpeechRecognizer ob
Leandro Graciá Gil
2011/10/09 10:29:21
If that happens they will clash but also produce a
Satish
2011/10/09 20:01:47
There is one issue - if an extension was already d
| |
| 462 context_getter, filter_profanities, "", ""); | |
| 463 recognizer_->StartRecording(); | |
| 464 state_lock_.Release(); | |
| 465 } | |
| 466 | |
| 467 void ExtensionSpeechInputManager::Stop(const std::string& extension_id) { | |
| 468 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 469 VLOG(1) << "Requesting stop (UI thread)"; | |
| 470 | |
| 471 if (ShuttingDown()) | |
| 472 return; | |
| 473 | |
| 474 if (!extension_id_.empty() && extension_id != extension_id_) { | |
| 475 DispatchError(constants::kErrorRequestDenied); | |
| 476 return; | |
| 477 } | |
| 478 | |
| 479 state_lock_.Acquire(); | |
| 480 if (state_ != kRecording) { | |
| 481 state_lock_.Release(); | |
| 482 return; | |
| 483 } | |
| 484 | |
| 485 DCHECK(recognizer_); | |
| 486 | |
| 487 VLOG(1) << "State changed to stopping"; | |
| 488 state_ = kStopping; | |
| 489 state_lock_.Release(); | |
| 490 | |
| 491 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | |
| 492 NewRunnableMethod(this, | |
| 493 &ExtensionSpeechInputManager::ForceStopOnIOThread)); | |
| 494 } | |
| 495 | |
| 496 void ExtensionSpeechInputManager::ForceStopOnIOThread() { | |
| 497 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 498 VLOG(1) << "Requesting forced stop (IO thread)"; | |
| 499 | |
| 500 state_lock_.Acquire(); | |
| 501 DCHECK(state_ != kIdle); | |
| 502 | |
| 503 if (recognizer_) { | |
| 504 recognizer_->CancelRecognition(); | |
| 505 recognizer_ = NULL; | |
| 506 } | |
| 507 | |
| 508 if (state_ == kShutdown) { | |
| 509 state_lock_.Release(); | |
| 510 return; | |
| 511 } | |
| 512 | |
| 513 state_ = kIdle; | |
| 514 VLOG(1) << "State changed to idle"; | |
| 515 | |
| 516 NotificationService::current()->Notify( | |
| 517 chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_STOPPED, | |
| 518 Source<Profile>(profile_), // Guarded by the state_ == kShutdown check. | |
| 519 Details<std::string>(&extension_id_)); | |
| 520 | |
| 521 extension_id_.clear(); | |
| 522 state_lock_.Release(); | |
| 523 } | |
| 524 | |
| OLD | NEW |