Switch Speech Recognition to asynchronous callback-based AudioManager interactions.

1) This introduces AudioSystem, which provides an asynchronous interface to AudioManager and eventually will replace AudioManager.
The plan is to gradually populate it with all the required methods and switch all AudioManager clients to it,
in preparation for moving AudioManager functionality to Mojo audio service.

2) SpeechRecognizerImpl now uses this interface, which required adding an extra state to its FSM.

3) SpeechRecognizerImpl unit tests are updated to take into account the new threading model and the new state.

BUG=687981,672468
CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.linux:linux_optional_gpu_tests_rel;master.tryserver.chromium.mac:mac_optional_gpu_tests_rel;master.tryserver.chromium.win:win_optional_gpu_tests_rel

Review-Url: https://codereview.chromium.org/2675713002
Cr-Commit-Position: refs/heads/master@{#448273}
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index d07d9f01..bf849b7 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -92,6 +92,7 @@
 #include "device/battery/battery_status_service.h"
 #include "device/gamepad/gamepad_service.h"
 #include "device/sensors/device_sensor_service.h"
+#include "media/audio/audio_system_impl.h"
 #include "media/base/media.h"
 #include "media/base/user_input_monitor.h"
 #include "media/midi/midi_service.h"
@@ -1539,8 +1540,8 @@
   {
     TRACE_EVENT0("startup",
       "BrowserMainLoop::BrowserThreadsStarted:InitSpeechRecognition");
-    speech_recognition_manager_.reset(
-        new SpeechRecognitionManagerImpl(media_stream_manager_.get()));
+    speech_recognition_manager_.reset(new SpeechRecognitionManagerImpl(
+        audio_system_.get(), media_stream_manager_.get()));
   }
 
   {
@@ -1799,6 +1800,9 @@
         MediaInternals::GetInstance());
   }
   CHECK(audio_manager_);
+
+  audio_system_ = media::AudioSystemImpl::Create(audio_manager_.get());
+  CHECK(audio_system_);
 }
 
 }  // namespace content
diff --git a/content/browser/browser_main_loop.h b/content/browser/browser_main_loop.h
index a307ed7..8c535ff 100644
--- a/content/browser/browser_main_loop.h
+++ b/content/browser/browser_main_loop.h
@@ -40,6 +40,7 @@
 }
 
 namespace media {
+class AudioSystem;
 #if defined(OS_WIN)
 class SystemMessageWindowWin;
 #elif defined(OS_LINUX) && defined(USE_UDEV)
@@ -137,6 +138,7 @@
   int GetResultCode() const { return result_code_; }
 
   media::AudioManager* audio_manager() const { return audio_manager_.get(); }
+  media::AudioSystem* audio_system() const { return audio_system_.get(); }
   MediaStreamManager* media_stream_manager() const {
     return media_stream_manager_.get();
   }
@@ -295,6 +297,9 @@
   // AudioThread needs to outlive |audio_manager_|.
   std::unique_ptr<AudioManagerThread> audio_thread_;
   media::ScopedAudioManagerPtr audio_manager_;
+  // Calls to |audio_system_| must not be posted to the audio thread if it
+  // differs from the UI one.
+  std::unique_ptr<media::AudioSystem> audio_system_;
 
   std::unique_ptr<midi::MidiService> midi_service_;
 
diff --git a/content/browser/speech/speech_recognition_browsertest.cc b/content/browser/speech/speech_recognition_browsertest.cc
index 7ddd4ee0..32fb5dea 100644
--- a/content/browser/speech/speech_recognition_browsertest.cc
+++ b/content/browser/speech/speech_recognition_browsertest.cc
@@ -27,6 +27,7 @@
 #include "content/public/test/test_utils.h"
 #include "content/shell/browser/shell.h"
 #include "content/test/mock_google_streaming_server.h"
+#include "media/audio/audio_system_impl.h"
 #include "media/audio/mock_audio_manager.h"
 #include "media/audio/test_audio_input_controller_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -122,16 +123,20 @@
     ASSERT_TRUE(SpeechRecognitionManagerImpl::GetInstance());
     media::AudioManager::StartHangMonitorIfNeeded(
         BrowserThread::GetTaskRunnerForThread(BrowserThread::IO));
-    SpeechRecognizerImpl::SetAudioManagerForTesting(new media::MockAudioManager(
+    audio_manager_.reset(new media::MockAudioManager(
         BrowserThread::GetTaskRunnerForThread(BrowserThread::IO)));
+    audio_manager_->SetInputStreamParameters(
+        media::AudioParameters::UnavailableDeviceParams());
+    audio_system_ = media::AudioSystemImpl::Create(audio_manager_.get());
+    SpeechRecognizerImpl::SetAudioSystemForTesting(audio_system_.get());
   }
 
   void TearDownOnMainThread() override {
-    SpeechRecognizerImpl::SetAudioManagerForTesting(NULL);
+    SpeechRecognizerImpl::SetAudioSystemForTesting(nullptr);
   }
 
   void TearDownInProcessBrowserTestFixture() override {
-    test_audio_input_controller_factory_.set_delegate(NULL);
+    test_audio_input_controller_factory_.set_delegate(nullptr);
     mock_streaming_server_.reset();
   }
 
@@ -189,6 +194,8 @@
     return result;
   }
 
+  media::MockAudioManager::UniquePtr audio_manager_;
+  std::unique_ptr<media::AudioSystem> audio_system_;
   StreamingServerState streaming_server_state_;
   std::unique_ptr<MockGoogleStreamingServer> mock_streaming_server_;
   media::TestAudioInputControllerFactory test_audio_input_controller_factory_;
diff --git a/content/browser/speech/speech_recognition_manager_impl.cc b/content/browser/speech/speech_recognition_manager_impl.cc
index c9e470c2..ab0d981 100644
--- a/content/browser/speech/speech_recognition_manager_impl.cc
+++ b/content/browser/speech/speech_recognition_manager_impl.cc
@@ -61,8 +61,10 @@
 }
 
 SpeechRecognitionManagerImpl::SpeechRecognitionManagerImpl(
+    media::AudioSystem* audio_system,
     MediaStreamManager* media_stream_manager)
-    : media_stream_manager_(media_stream_manager),
+    : audio_system_(audio_system),
+      media_stream_manager_(media_stream_manager),
       primary_session_id_(kSessionIDInvalid),
       last_session_id_(kSessionIDInvalid),
       is_dispatching_event_(false),
@@ -130,11 +132,8 @@
   google_remote_engine->SetConfig(remote_engine_config);
 
   session->recognizer = new SpeechRecognizerImpl(
-      this,
-      session_id,
-      config.continuous,
-      config.interim_results,
-      google_remote_engine);
+      this, audio_system_, session_id, config.continuous,
+      config.interim_results, google_remote_engine);
 #else
   session->recognizer = new SpeechRecognizerImplAndroid(this, session_id);
 #endif
diff --git a/content/browser/speech/speech_recognition_manager_impl.h b/content/browser/speech/speech_recognition_manager_impl.h
index 9469016af0b..2f17def 100644
--- a/content/browser/speech/speech_recognition_manager_impl.h
+++ b/content/browser/speech/speech_recognition_manager_impl.h
@@ -19,6 +19,10 @@
 #include "content/public/browser/speech_recognition_session_context.h"
 #include "content/public/common/speech_recognition_error.h"
 
+namespace media {
+class AudioSystem;
+}
+
 namespace content {
 class BrowserMainLoop;
 class MediaStreamManager;
@@ -92,7 +96,8 @@
   friend class BrowserMainLoop;
   // Needed for dtor.
   friend std::default_delete<SpeechRecognitionManagerImpl>;
-  SpeechRecognitionManagerImpl(MediaStreamManager* media_stream_manager);
+  SpeechRecognitionManagerImpl(media::AudioSystem* audio_system,
+                               MediaStreamManager* media_stream_manager);
   ~SpeechRecognitionManagerImpl() override;
 
  private:
@@ -166,6 +171,7 @@
   SpeechRecognitionEventListener* GetDelegateListener() const;
   int GetNextSessionID();
 
+  media::AudioSystem* audio_system_;
   MediaStreamManager* media_stream_manager_;
   typedef std::map<int, Session*> SessionsTable;
   SessionsTable sessions_;
diff --git a/content/browser/speech/speech_recognizer_impl.cc b/content/browser/speech/speech_recognizer_impl.cc
index 8a57992..acec267 100644
--- a/content/browser/speech/speech_recognizer_impl.cc
+++ b/content/browser/speech/speech_recognizer_impl.cc
@@ -17,6 +17,8 @@
 #include "content/browser/speech/audio_buffer.h"
 #include "content/public/browser/speech_recognition_event_listener.h"
 #include "media/audio/audio_file_writer.h"
+#include "media/audio/audio_manager.h"
+#include "media/audio/audio_system.h"
 #include "media/base/audio_converter.h"
 
 #if defined(OS_WIN)
@@ -112,7 +114,7 @@
 const int SpeechRecognizerImpl::kNumBitsPerAudioSample = 16;
 const int SpeechRecognizerImpl::kNoSpeechTimeoutMs = 8000;
 const int SpeechRecognizerImpl::kEndpointerEstimationTimeMs = 300;
-media::AudioManager* SpeechRecognizerImpl::audio_manager_for_tests_ = NULL;
+media::AudioSystem* SpeechRecognizerImpl::audio_system_for_tests_ = nullptr;
 
 static_assert(SpeechRecognizerImpl::kNumBitsPerAudioSample % 8 == 0,
               "kNumBitsPerAudioSample must be a multiple of 8");
@@ -176,11 +178,13 @@
 
 SpeechRecognizerImpl::SpeechRecognizerImpl(
     SpeechRecognitionEventListener* listener,
+    media::AudioSystem* audio_system,
     int session_id,
     bool continuous,
     bool provisional_results,
     SpeechRecognitionEngine* engine)
     : SpeechRecognizer(listener, session_id),
+      audio_system_(audio_system),
       recognition_engine_(engine),
       endpointer_(kAudioSampleRate),
       audio_log_(MediaInternals::GetInstance()->CreateAudioLog(
@@ -188,8 +192,10 @@
       is_dispatching_event_(false),
       provisional_results_(provisional_results),
       end_of_utterance_(false),
-      state_(STATE_IDLE) {
-  DCHECK(recognition_engine_ != NULL);
+      state_(STATE_IDLE),
+      weak_ptr_factory_(this) {
+  DCHECK(recognition_engine_ != nullptr);
+  DCHECK(audio_system_ != nullptr);
   if (!continuous) {
     // In single shot (non-continous) recognition,
     // the session is automatically ended after:
@@ -223,8 +229,8 @@
   device_id_ = device_id;
 
   BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
-                          base::Bind(&SpeechRecognizerImpl::DispatchEvent,
-                                     this, FSMEventArgs(EVENT_START)));
+                          base::Bind(&SpeechRecognizerImpl::DispatchEvent, this,
+                                     FSMEventArgs(EVENT_PREPARE)));
 }
 
 void SpeechRecognizerImpl::AbortRecognition() {
@@ -376,6 +382,25 @@
         // EVENT_STOP_CAPTURE below once speech input extensions are fixed.
         case EVENT_ABORT:
           return AbortSilently(event_args);
+        case EVENT_PREPARE:
+          return PrepareRecognition(event_args);
+        case EVENT_START:
+          return NotFeasible(event_args);
+        case EVENT_STOP_CAPTURE:
+          return AbortSilently(event_args);
+        case EVENT_AUDIO_DATA:     // Corner cases related to queued messages
+        case EVENT_ENGINE_RESULT:  // being lately dispatched.
+        case EVENT_ENGINE_ERROR:
+        case EVENT_AUDIO_ERROR:
+          return DoNothing(event_args);
+      }
+      break;
+    case STATE_PREPARING:
+      switch (event) {
+        case EVENT_ABORT:
+          return AbortSilently(event_args);
+        case EVENT_PREPARE:
+          return NotFeasible(event_args);
         case EVENT_START:
           return StartRecording(event_args);
         case EVENT_STOP_CAPTURE:
@@ -391,6 +416,8 @@
       switch (event) {
         case EVENT_ABORT:
           return AbortWithError(event_args);
+        case EVENT_PREPARE:
+          return NotFeasible(event_args);
         case EVENT_START:
           return NotFeasible(event_args);
         case EVENT_STOP_CAPTURE:
@@ -408,6 +435,8 @@
       switch (event) {
         case EVENT_ABORT:
           return AbortWithError(event_args);
+        case EVENT_PREPARE:
+          return NotFeasible(event_args);
         case EVENT_START:
           return NotFeasible(event_args);
         case EVENT_STOP_CAPTURE:
@@ -425,6 +454,8 @@
       switch (event) {
         case EVENT_ABORT:
           return AbortWithError(event_args);
+        case EVENT_PREPARE:
+          return NotFeasible(event_args);
         case EVENT_START:
           return NotFeasible(event_args);
         case EVENT_STOP_CAPTURE:
@@ -442,6 +473,8 @@
       switch (event) {
         case EVENT_ABORT:
           return AbortWithError(event_args);
+        case EVENT_PREPARE:
+          return NotFeasible(event_args);
         case EVENT_START:
           return NotFeasible(event_args);
         case EVENT_STOP_CAPTURE:
@@ -459,6 +492,8 @@
       switch (event) {
         case EVENT_ABORT:
           return AbortWithError(event_args);
+        case EVENT_PREPARE:
+          return NotFeasible(event_args);
         case EVENT_START:
           return NotFeasible(event_args);
         case EVENT_STOP_CAPTURE:
@@ -515,38 +550,43 @@
   }
 }
 
-SpeechRecognizerImpl::FSMState
-SpeechRecognizerImpl::StartRecording(const FSMEventArgs&) {
+void SpeechRecognizerImpl::OnDeviceInfo(const media::AudioParameters& params) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  device_params_ = params;
+  DVLOG(1) << "Device parameters: " << device_params_.AsHumanReadableString();
+  DispatchEvent(FSMEventArgs(EVENT_START));
+}
+
+SpeechRecognizerImpl::FSMState SpeechRecognizerImpl::PrepareRecognition(
+    const FSMEventArgs&) {
   DCHECK(state_ == STATE_IDLE);
   DCHECK(recognition_engine_.get() != NULL);
   DCHECK(!IsCapturingAudio());
-  const bool unit_test_is_active = (audio_manager_for_tests_ != NULL);
-  AudioManager* audio_manager = unit_test_is_active ?
-                                audio_manager_for_tests_ :
-                                AudioManager::Get();
-  DCHECK(audio_manager != NULL);
+  GetAudioSystem()->GetInputStreamParameters(
+      device_id_, base::Bind(&SpeechRecognizerImpl::OnDeviceInfo,
+                             weak_ptr_factory_.GetWeakPtr()));
+
+  listener()->OnRecognitionStart(session_id());
+  return STATE_PREPARING;
+}
+
+SpeechRecognizerImpl::FSMState
+SpeechRecognizerImpl::StartRecording(const FSMEventArgs&) {
+  DCHECK(state_ == STATE_PREPARING);
+  DCHECK(recognition_engine_.get() != NULL);
+  DCHECK(!IsCapturingAudio());
 
   DVLOG(1) << "SpeechRecognizerImpl starting audio capture.";
   num_samples_recorded_ = 0;
   audio_level_ = 0;
   end_of_utterance_ = false;
-  listener()->OnRecognitionStart(session_id());
-
-  // TODO(xians): Check if the OS has the device with |device_id_|, return
-  // |SPEECH_AUDIO_ERROR_DETAILS_NO_MIC| if the target device does not exist.
-  if (!audio_manager->HasAudioInputDevices()) {
-    return Abort(SpeechRecognitionError(SPEECH_RECOGNITION_ERROR_AUDIO_CAPTURE,
-                                        SPEECH_AUDIO_ERROR_DETAILS_NO_MIC));
-  }
 
   int chunk_duration_ms = recognition_engine_->GetDesiredAudioChunkDurationMs();
 
-  AudioParameters in_params = audio_manager->GetInputStreamParameters(
-      device_id_);
-  if (!in_params.IsValid() && !unit_test_is_active) {
-    DLOG(ERROR) << "Invalid native audio input parameters";
-    return Abort(
-        SpeechRecognitionError(SPEECH_RECOGNITION_ERROR_AUDIO_CAPTURE));
+  if (!device_params_.IsValid()) {
+    DLOG(ERROR) << "Audio input device not found";
+    return Abort(SpeechRecognitionError(SPEECH_RECOGNITION_ERROR_AUDIO_CAPTURE,
+                                        SPEECH_AUDIO_ERROR_DETAILS_NO_MIC));
   }
 
   // Audio converter shall provide audio based on these parameters as output.
@@ -571,16 +611,18 @@
 #endif
 
   AudioParameters input_parameters = output_parameters;
-  if (use_native_audio_params && !unit_test_is_active) {
+
+  // AUDIO_FAKE means we are running a test.
+  if (use_native_audio_params &&
+      device_params_.format() != media::AudioParameters::AUDIO_FAKE) {
     // Use native audio parameters but avoid opening up at the native buffer
     // size. Instead use same frame size (in milliseconds) as WebSpeech uses.
     // We rely on internal buffers in the audio back-end to fulfill this request
     // and the idea is to simplify the audio conversion since each Convert()
     // call will then render exactly one ProvideInput() call.
-    // in_params.sample_rate()
-    input_parameters = in_params;
+    input_parameters = device_params_;
     frames_per_buffer =
-        ((in_params.sample_rate() * chunk_duration_ms) / 1000.0) + 0.5;
+        ((input_parameters.sample_rate() * chunk_duration_ms) / 1000.0) + 0.5;
     input_parameters.set_frames_per_buffer(frames_per_buffer);
     DVLOG(1) << "SRI::input_parameters: "
              << input_parameters.AsHumanReadableString();
@@ -592,7 +634,8 @@
       new OnDataConverter(input_parameters, output_parameters));
 
   audio_controller_ = AudioInputController::Create(
-      audio_manager, this, this, nullptr, nullptr, input_parameters, device_id_,
+      GetAudioSystem()->GetAudioManager(), this, this, nullptr, nullptr,
+      input_parameters, device_id_,
       /*agc_is_enabled*/ false);
 
   if (!audio_controller_.get()) {
@@ -692,11 +735,18 @@
 
 SpeechRecognizerImpl::FSMState SpeechRecognizerImpl::Abort(
     const SpeechRecognitionError& error) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
   if (IsCapturingAudio())
     CloseAudioControllerAsynchronously();
 
   DVLOG(1) << "SpeechRecognizerImpl canceling recognition. ";
 
+  if (state_ == STATE_PREPARING) {
+    // Cancel an outstanding reply from AudioSystem.
+    weak_ptr_factory_.InvalidateWeakPtrs();
+  }
+
   // The recognition engine is initialized only after STATE_STARTING.
   if (state_ > STATE_STARTING) {
     DCHECK(recognition_engine_.get() != NULL);
@@ -833,9 +883,13 @@
       session_id(), clip_detected ? 1.0f : audio_level_, noise_level);
 }
 
-void SpeechRecognizerImpl::SetAudioManagerForTesting(
-    AudioManager* audio_manager) {
-  audio_manager_for_tests_ = audio_manager;
+void SpeechRecognizerImpl::SetAudioSystemForTesting(
+    media::AudioSystem* audio_system) {
+  audio_system_for_tests_ = audio_system;
+}
+
+media::AudioSystem* SpeechRecognizerImpl::GetAudioSystem() {
+  return audio_system_for_tests_ ? audio_system_for_tests_ : audio_system_;
 }
 
 SpeechRecognizerImpl::FSMEventArgs::FSMEventArgs(FSMEvent event_value)
diff --git a/content/browser/speech/speech_recognizer_impl.h b/content/browser/speech/speech_recognizer_impl.h
index 7f6d7789..d14cdf4 100644
--- a/content/browser/speech/speech_recognizer_impl.h
+++ b/content/browser/speech/speech_recognizer_impl.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "content/browser/speech/endpointer/endpointer.h"
 #include "content/browser/speech/speech_recognition_engine.h"
 #include "content/browser/speech/speech_recognizer.h"
@@ -20,7 +21,7 @@
 
 namespace media {
 class AudioBus;
-class AudioManager;
+class AudioSystem;
 }
 
 namespace content {
@@ -42,9 +43,10 @@
   static const int kNoSpeechTimeoutMs;
   static const int kEndpointerEstimationTimeMs;
 
-  static void SetAudioManagerForTesting(media::AudioManager* audio_manager);
+  static void SetAudioSystemForTesting(media::AudioSystem* audio_system);
 
   SpeechRecognizerImpl(SpeechRecognitionEventListener* listener,
+                       media::AudioSystem* audio_system,
                        int session_id,
                        bool continuous,
                        bool provisional_results,
@@ -62,6 +64,7 @@
 
   enum FSMState {
     STATE_IDLE = 0,
+    STATE_PREPARING,
     STATE_STARTING,
     STATE_ESTIMATING_ENVIRONMENT,
     STATE_WAITING_FOR_SPEECH,
@@ -73,6 +76,7 @@
 
   enum FSMEvent {
     EVENT_ABORT = 0,
+    EVENT_PREPARE,
     EVENT_START,
     EVENT_STOP_CAPTURE,
     EVENT_AUDIO_DATA,
@@ -105,7 +109,11 @@
   // Process a new audio chunk in the audio pipeline (endpointer, vumeter, etc).
   void ProcessAudioPipeline(const AudioChunk& raw_audio);
 
+  // Callback from AudioSystem.
+  void OnDeviceInfo(const media::AudioParameters& params);
+
   // The methods below handle transitions of the recognizer FSM.
+  FSMState PrepareRecognition(const FSMEventArgs&);
   FSMState StartRecording(const FSMEventArgs& event_args);
   FSMState StartRecognitionEngine(const FSMEventArgs& event_args);
   FSMState WaitEnvironmentEstimationCompletion(const FSMEventArgs& event_args);
@@ -153,8 +161,11 @@
   void OnSpeechRecognitionEngineError(
       const SpeechRecognitionError& error) override;
 
-  static media::AudioManager* audio_manager_for_tests_;
+  media::AudioSystem* GetAudioSystem();
 
+  // Substitutes the real audio system in browser tests.
+  static media::AudioSystem* audio_system_for_tests_;
+  media::AudioSystem* audio_system_;
   std::unique_ptr<SpeechRecognitionEngine> recognition_engine_;
   Endpointer endpointer_;
   scoped_refptr<media::AudioInputController> audio_controller_;
@@ -166,6 +177,7 @@
   bool end_of_utterance_;
   FSMState state_;
   std::string device_id_;
+  media::AudioParameters device_params_;
 
   class OnDataConverter;
 
@@ -173,6 +185,7 @@
   // output format.
   std::unique_ptr<SpeechRecognizerImpl::OnDataConverter> audio_converter_;
 
+  base::WeakPtrFactory<SpeechRecognizerImpl> weak_ptr_factory_;
   DISALLOW_COPY_AND_ASSIGN(SpeechRecognizerImpl);
 };
 
diff --git a/content/browser/speech/speech_recognizer_impl_unittest.cc b/content/browser/speech/speech_recognizer_impl_unittest.cc
index 80b94bc..2240fddc 100644
--- a/content/browser/speech/speech_recognizer_impl_unittest.cc
+++ b/content/browser/speech/speech_recognizer_impl_unittest.cc
@@ -8,7 +8,9 @@
 #include <vector>
 
 #include "base/run_loop.h"
+#include "base/synchronization/waitable_event.h"
 #include "base/sys_byteorder.h"
+#include "base/threading/thread.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "content/browser/speech/proto/google_streaming_api.pb.h"
 #include "content/browser/speech/speech_recognition_engine.h"
@@ -16,11 +18,13 @@
 #include "content/public/browser/speech_recognition_event_listener.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "media/audio/audio_device_description.h"
+#include "media/audio/audio_system_impl.h"
 #include "media/audio/fake_audio_input_stream.h"
 #include "media/audio/fake_audio_output_stream.h"
 #include "media/audio/mock_audio_manager.h"
 #include "media/audio/test_audio_input_controller_factory.h"
 #include "media/base/audio_bus.h"
+#include "media/base/test_helpers.h"
 #include "net/base/net_errors.h"
 #include "net/url_request/test_url_fetcher_factory.h"
 #include "net/url_request/url_request_status.h"
@@ -39,7 +43,8 @@
                                  public testing::Test {
  public:
   SpeechRecognizerImplTest()
-      : recognition_started_(false),
+      : audio_thread_("SpeechAudioThread"),
+        recognition_started_(false),
         recognition_ended_(false),
         result_received_(false),
         audio_started_(false),
@@ -59,11 +64,15 @@
     sr_engine->SetConfig(config);
 
     const int kTestingSessionId = 1;
-    recognizer_ = new SpeechRecognizerImpl(
-        this, kTestingSessionId, false, false, sr_engine);
+
+    audio_thread_.StartAndWaitForTesting();
     audio_manager_.reset(
-        new media::MockAudioManager(base::ThreadTaskRunnerHandle::Get().get()));
-    recognizer_->SetAudioManagerForTesting(audio_manager_.get());
+        new media::MockAudioManager(audio_thread_.task_runner()));
+    audio_manager_->SetInputStreamParameters(
+        media::AudioParameters::UnavailableDeviceParams());
+    audio_system_ = media::AudioSystemImpl::Create(audio_manager_.get());
+    recognizer_ = new SpeechRecognizerImpl(
+        this, audio_system_.get(), kTestingSessionId, false, false, sr_engine);
 
     int audio_packet_length_bytes =
         (SpeechRecognizerImpl::kAudioSampleRate *
@@ -80,6 +89,12 @@
     audio_bus_->Zero();
   }
 
+  ~SpeechRecognizerImplTest() override {
+    // Deleting |audio_manager_| on audio thread.
+    audio_manager_.reset();
+    audio_thread_.Stop();
+  }
+
   void CheckEventsConsistency() {
     // Note: "!x || y" == "x implies y".
     EXPECT_TRUE(!recognition_ended_ || recognition_started_);
@@ -189,10 +204,22 @@
     writer->Write(data, 0.0, false, 0);
   }
 
+  void WaitForAudioThreadToPostDeviceInfo() {
+    media::WaitableMessageLoopEvent event;
+    audio_thread_.task_runner()->PostTaskAndReply(
+        FROM_HERE, base::Bind(&base::DoNothing), event.GetClosure());
+    // Runs the loop and waits for the |audio_thread_| to call event's closure,
+    // which means AudioSystem reply containing device parameters is already
+    // queued on the main thread.
+    event.RunAndWait();
+  }
+
  protected:
   TestBrowserThreadBundle thread_bundle_;
   scoped_refptr<SpeechRecognizerImpl> recognizer_;
-  media::ScopedAudioManagerPtr audio_manager_;
+  base::Thread audio_thread_;
+  media::MockAudioManager::UniquePtr audio_manager_;
+  std::unique_ptr<media::AudioSystem> audio_system_;
   bool recognition_started_;
   bool recognition_ended_;
   bool result_received_;
@@ -210,10 +237,81 @@
   float noise_volume_;
 };
 
+TEST_F(SpeechRecognizerImplTest, StartNoInputDevices) {
+  // Check for callbacks when stopping record before any audio gets recorded.
+  audio_manager_->SetHasInputDevices(false);
+  recognizer_->StartRecognition(
+      media::AudioDeviceDescription::kDefaultDeviceId);
+  WaitForAudioThreadToPostDeviceInfo();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(recognition_started_);
+  EXPECT_FALSE(audio_started_);
+  EXPECT_FALSE(result_received_);
+  EXPECT_EQ(SPEECH_RECOGNITION_ERROR_AUDIO_CAPTURE, error_);
+  CheckFinalEventsConsistency();
+}
+
+TEST_F(SpeechRecognizerImplTest, StopBeforeDeviceInfoReceived) {
+  // Check for callbacks when stopping record before reply is received from
+  // AudioSystem.
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+
+  // Block audio thread.
+  audio_thread_.task_runner()->PostTask(
+      FROM_HERE,
+      base::Bind(&base::WaitableEvent::Wait, base::Unretained(&event)));
+
+  recognizer_->StartRecognition(
+      media::AudioDeviceDescription::kDefaultDeviceId);
+  recognizer_->StopAudioCapture();
+  base::RunLoop().RunUntilIdle();
+
+  // Release audio thread and receive a callback from it.
+  event.Signal();
+  WaitForAudioThreadToPostDeviceInfo();
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(recognition_started_);
+  EXPECT_FALSE(audio_started_);
+  EXPECT_FALSE(result_received_);
+  EXPECT_EQ(SPEECH_RECOGNITION_ERROR_NONE, error_);
+  CheckFinalEventsConsistency();
+}
+
+TEST_F(SpeechRecognizerImplTest, CancelBeforeDeviceInfoReceived) {
+  // Check for callbacks when stopping record before reply is received from
+  // AudioSystem.
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+
+  // Block audio thread.
+  audio_thread_.task_runner()->PostTask(
+      FROM_HERE,
+      base::Bind(&base::WaitableEvent::Wait, base::Unretained(&event)));
+
+  recognizer_->StartRecognition(
+      media::AudioDeviceDescription::kDefaultDeviceId);
+  recognizer_->AbortRecognition();
+  base::RunLoop().RunUntilIdle();
+
+  // Release audio thread and receive a callback from it.
+  event.Signal();
+  WaitForAudioThreadToPostDeviceInfo();
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(recognition_started_);
+  EXPECT_FALSE(audio_started_);
+  EXPECT_FALSE(result_received_);
+  EXPECT_EQ(SPEECH_RECOGNITION_ERROR_NONE, error_);
+  CheckFinalEventsConsistency();
+}
+
 TEST_F(SpeechRecognizerImplTest, StopNoData) {
   // Check for callbacks when stopping record before any audio gets recorded.
   recognizer_->StartRecognition(
       media::AudioDeviceDescription::kDefaultDeviceId);
+  WaitForAudioThreadToPostDeviceInfo();
   recognizer_->StopAudioCapture();
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(recognition_started_);
@@ -228,6 +326,7 @@
   // recorded.
   recognizer_->StartRecognition(
       media::AudioDeviceDescription::kDefaultDeviceId);
+  WaitForAudioThreadToPostDeviceInfo();
   recognizer_->AbortRecognition();
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(recognition_started_);
@@ -242,6 +341,7 @@
   // network callback to arrive before completion.
   recognizer_->StartRecognition(
       media::AudioDeviceDescription::kDefaultDeviceId);
+  WaitForAudioThreadToPostDeviceInfo();
   base::RunLoop().RunUntilIdle();
   TestAudioInputController* controller =
       audio_input_controller_factory_.controller();
@@ -304,6 +404,7 @@
   // Start recording, give some data and then cancel.
   recognizer_->StartRecognition(
       media::AudioDeviceDescription::kDefaultDeviceId);
+  WaitForAudioThreadToPostDeviceInfo();
   base::RunLoop().RunUntilIdle();
   TestAudioInputController* controller =
       audio_input_controller_factory_.controller();
@@ -325,6 +426,7 @@
   // with a connection error and verify that the recognizer bubbles the error up
   recognizer_->StartRecognition(
       media::AudioDeviceDescription::kDefaultDeviceId);
+  WaitForAudioThreadToPostDeviceInfo();
   base::RunLoop().RunUntilIdle();
   TestAudioInputController* controller =
       audio_input_controller_factory_.controller();
@@ -361,6 +463,7 @@
   // with a 500 error and verify that the recognizer bubbles the error up
   recognizer_->StartRecognition(
       media::AudioDeviceDescription::kDefaultDeviceId);
+  WaitForAudioThreadToPostDeviceInfo();
   base::RunLoop().RunUntilIdle();
   TestAudioInputController* controller =
       audio_input_controller_factory_.controller();
@@ -395,6 +498,7 @@
   // Check if things tear down properly if AudioInputController threw an error.
   recognizer_->StartRecognition(
       media::AudioDeviceDescription::kDefaultDeviceId);
+  WaitForAudioThreadToPostDeviceInfo();
   base::RunLoop().RunUntilIdle();
   TestAudioInputController* controller =
       audio_input_controller_factory_.controller();
@@ -414,6 +518,7 @@
   // after giving some audio data.
   recognizer_->StartRecognition(
       media::AudioDeviceDescription::kDefaultDeviceId);
+  WaitForAudioThreadToPostDeviceInfo();
   base::RunLoop().RunUntilIdle();
   TestAudioInputController* controller =
       audio_input_controller_factory_.controller();
@@ -435,6 +540,7 @@
   // This should trigger the no-speech detector and issue a callback.
   recognizer_->StartRecognition(
       media::AudioDeviceDescription::kDefaultDeviceId);
+  WaitForAudioThreadToPostDeviceInfo();
   base::RunLoop().RunUntilIdle();
   TestAudioInputController* controller =
       audio_input_controller_factory_.controller();
@@ -461,6 +567,7 @@
   // triggered.
   recognizer_->StartRecognition(
       media::AudioDeviceDescription::kDefaultDeviceId);
+  WaitForAudioThreadToPostDeviceInfo();
   base::RunLoop().RunUntilIdle();
   TestAudioInputController* controller =
       audio_input_controller_factory_.controller();
@@ -498,6 +605,7 @@
   // samples and proper volume for the loud audio.
   recognizer_->StartRecognition(
       media::AudioDeviceDescription::kDefaultDeviceId);
+  WaitForAudioThreadToPostDeviceInfo();
   base::RunLoop().RunUntilIdle();
   TestAudioInputController* controller =
       audio_input_controller_factory_.controller();
diff --git a/media/audio/BUILD.gn b/media/audio/BUILD.gn
index ff8d8f2..2952088 100644
--- a/media/audio/BUILD.gn
+++ b/media/audio/BUILD.gn
@@ -95,6 +95,9 @@
     "audio_source_diverter.h",
     "audio_streams_tracker.cc",
     "audio_streams_tracker.h",
+    "audio_system.h",
+    "audio_system_impl.cc",
+    "audio_system_impl.h",
     "clockless_audio_sink.cc",
     "clockless_audio_sink.h",
     "fake_audio_input_stream.cc",
@@ -309,6 +312,7 @@
     "audio_output_proxy_unittest.cc",
     "audio_power_monitor_unittest.cc",
     "audio_streams_tracker_unittest.cc",
+    "audio_system_impl_unittest.cc",
     "fake_audio_worker_unittest.cc",
     "simple_sources_unittest.cc",
     "virtual_audio_input_stream_unittest.cc",
diff --git a/media/audio/audio_system.h b/media/audio/audio_system.h
new file mode 100644
index 0000000..9db1931
--- /dev/null
+++ b/media/audio/audio_system.h
@@ -0,0 +1,37 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_SYSTEM_H_
+#define MEDIA_AUDIO_AUDIO_SYSTEM_H_
+
+#include "base/callback.h"
+#include "media/audio/audio_device_description.h"
+#include "media/base/audio_parameters.h"
+#include "media/base/media_export.h"
+
+namespace media {
+class AudioManager;
+
+// Work in progress: Provides asynchronous interface to AudioManager. All the
+// AudioManager clients will be switched to it, in preparation for moving
+// to Mojo audio service.
+class MEDIA_EXPORT AudioSystem {
+ public:
+  // Replies are asynchronously sent to the thread the call is issued on.
+  using OnAudioParamsCallback = base::Callback<void(const AudioParameters&)>;
+
+  virtual ~AudioSystem(){};
+
+  // Callback will receive invalid parameters if the device is not found.
+  virtual void GetInputStreamParameters(
+      const std::string& device_id,
+      OnAudioParamsCallback on_params_cb) const = 0;
+
+  // Must not be used for anything but stream creation.
+  virtual AudioManager* GetAudioManager() const = 0;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_SYSTEM_H_
diff --git a/media/audio/audio_system_impl.cc b/media/audio/audio_system_impl.cc
new file mode 100644
index 0000000..96300ba
--- /dev/null
+++ b/media/audio/audio_system_impl.cc
@@ -0,0 +1,70 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_system_impl.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/single_thread_task_runner.h"
+#include "base/task_runner_util.h"
+#include "media/audio/audio_manager.h"
+
+// Using base::Unretained for |audio_manager_| is safe since it is deleted after
+// its task runner, and AudioSystemImpl is deleted on the UI thread after the IO
+// thread has been stopped and before |audio_manager_| deletion is scheduled.
+namespace media {
+
+namespace {
+
+AudioParameters GetInputParametersOnDeviceThread(AudioManager* audio_manager,
+                                                 const std::string& device_id) {
+  DCHECK(audio_manager->GetTaskRunner()->BelongsToCurrentThread());
+
+  // TODO(olka): remove this when AudioManager::GetInputStreamParameters()
+  // works this way on all the platforms.
+  if (!audio_manager->HasAudioInputDevices())
+    return AudioParameters();
+
+  return audio_manager->GetInputStreamParameters(device_id);
+}
+
+}  // namespace
+
+AudioSystemImpl::AudioSystemImpl(AudioManager* audio_manager)
+    : audio_manager_(audio_manager) {
+  DCHECK(audio_manager_);
+}
+
+AudioSystemImpl::~AudioSystemImpl() {}
+
+// static
+std::unique_ptr<AudioSystem> AudioSystemImpl::Create(
+    AudioManager* audio_manager) {
+  return base::WrapUnique(new AudioSystemImpl(audio_manager));
+}
+
+void AudioSystemImpl::GetInputStreamParameters(
+    const std::string& device_id,
+    OnAudioParamsCallback on_params_cb) const {
+  if (GetTaskRunner()->BelongsToCurrentThread()) {
+    GetTaskRunner()->PostTask(
+        FROM_HERE, base::Bind(on_params_cb, GetInputParametersOnDeviceThread(
+                                                audio_manager_, device_id)));
+    return;
+  }
+  base::PostTaskAndReplyWithResult(
+      GetTaskRunner(), FROM_HERE,
+      base::Bind(&GetInputParametersOnDeviceThread,
+                 base::Unretained(audio_manager_), device_id),
+      std::move(on_params_cb));
+}
+
+AudioManager* AudioSystemImpl::GetAudioManager() const {
+  return audio_manager_;
+}
+
+base::SingleThreadTaskRunner* AudioSystemImpl::GetTaskRunner() const {
+  return audio_manager_->GetTaskRunner();
+}
+
+}  // namespace media
diff --git a/media/audio/audio_system_impl.h b/media/audio/audio_system_impl.h
new file mode 100644
index 0000000..e76fc590
--- /dev/null
+++ b/media/audio/audio_system_impl.h
@@ -0,0 +1,42 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_SYSTEM_IMPL_H_
+#define MEDIA_AUDIO_AUDIO_SYSTEM_IMPL_H_
+
+#include "media/audio/audio_system.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace media {
+class AudioManager;
+
+class MEDIA_EXPORT AudioSystemImpl : public AudioSystem {
+ public:
+  static std::unique_ptr<AudioSystem> Create(AudioManager* audio_manager);
+
+  ~AudioSystemImpl() override;
+
+  // AudioSystem implementation.
+  void GetInputStreamParameters(
+      const std::string& device_id,
+      OnAudioParamsCallback on_params_cb) const override;
+  AudioManager* GetAudioManager() const override;
+
+ protected:
+  AudioSystemImpl(AudioManager* audio_manager);
+
+ private:
+  base::SingleThreadTaskRunner* GetTaskRunner() const;
+
+  AudioManager* const audio_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(AudioSystemImpl);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_SYSTEM_IMPL_H_
diff --git a/media/audio/audio_system_impl_unittest.cc b/media/audio/audio_system_impl_unittest.cc
new file mode 100644
index 0000000..3247cdd
--- /dev/null
+++ b/media/audio/audio_system_impl_unittest.cc
@@ -0,0 +1,99 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_system_impl.h"
+#include "base/memory/ptr_util.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/task_runner_util.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_checker.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "media/audio/audio_device_description.h"
+#include "media/audio/mock_audio_manager.h"
+#include "media/base/test_helpers.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+class AudioSystemImplTest : public testing::TestWithParam<bool> {
+ public:
+  AudioSystemImplTest()
+      : use_audio_thread_(GetParam()), audio_thread_("AudioSystemThread") {
+    if (use_audio_thread_) {
+      audio_thread_.StartAndWaitForTesting();
+      audio_manager_.reset(
+          new media::MockAudioManager(audio_thread_.task_runner()));
+    } else {
+      audio_manager_.reset(new media::MockAudioManager(
+          base::ThreadTaskRunnerHandle::Get().get()));
+    }
+    audio_manager_->SetInputStreamParameters(
+        media::AudioParameters::UnavailableDeviceParams());
+    audio_system_ = media::AudioSystemImpl::Create(audio_manager_.get());
+  }
+
+  ~AudioSystemImplTest() override {
+    // Deleting |audio_manager_| on its thread.
+    audio_manager_.reset();
+    audio_thread_.Stop();
+  }
+
+  void OnAudioParams(const AudioParameters& expected,
+                     const AudioParameters& received) {
+    EXPECT_TRUE(thread_checker_.CalledOnValidThread());
+    EXPECT_EQ(expected.AsHumanReadableString(),
+              received.AsHumanReadableString());
+    AudioParametersReceived();
+  }
+
+  void WaitForCallback() {
+    if (!use_audio_thread_) {
+      base::RunLoop().RunUntilIdle();
+      return;
+    }
+    media::WaitableMessageLoopEvent event;
+    audio_thread_.task_runner()->PostTaskAndReply(
+        FROM_HERE, base::Bind(&base::DoNothing), event.GetClosure());
+    // Runs the loop and waits for the |audio_thread_| to call event's closure,
+    // which means AudioSystem reply containing device parameters is already
+    // queued on the main thread.
+    event.RunAndWait();
+    base::RunLoop().RunUntilIdle();
+  }
+
+  MOCK_METHOD0(AudioParametersReceived, void(void));
+
+ protected:
+  base::MessageLoop message_loop_;
+  base::ThreadChecker thread_checker_;
+  bool use_audio_thread_;
+  base::Thread audio_thread_;
+  MockAudioManager::UniquePtr audio_manager_;
+  std::unique_ptr<media::AudioSystem> audio_system_;
+};
+
+TEST_P(AudioSystemImplTest, GetInputStreamParameters) {
+  EXPECT_CALL(*this, AudioParametersReceived());
+  audio_system_->GetInputStreamParameters(
+      media::AudioDeviceDescription::kDefaultDeviceId,
+      base::Bind(&AudioSystemImplTest::OnAudioParams, base::Unretained(this),
+                 media::AudioParameters::UnavailableDeviceParams()));
+  WaitForCallback();
+}
+
+TEST_P(AudioSystemImplTest, GetInputStreamParametersNoDevice) {
+  audio_manager_->SetHasInputDevices(false);
+  EXPECT_CALL(*this, AudioParametersReceived());
+  audio_system_->GetInputStreamParameters(
+      media::AudioDeviceDescription::kDefaultDeviceId,
+      base::Bind(&AudioSystemImplTest::OnAudioParams, base::Unretained(this),
+                 media::AudioParameters()));
+  WaitForCallback();
+}
+
+INSTANTIATE_TEST_CASE_P(, AudioSystemImplTest, testing::Values(false, true));
+
+}  // namespace media
diff --git a/media/audio/mock_audio_manager.cc b/media/audio/mock_audio_manager.cc
index 6d15845..931cdbb7 100644
--- a/media/audio/mock_audio_manager.cc
+++ b/media/audio/mock_audio_manager.cc
@@ -10,6 +10,19 @@
 
 namespace media {
 
+void MockAudioManager::Deleter::operator()(
+    const MockAudioManager* instance) const {
+  CHECK(instance);
+  if (instance->GetTaskRunner()->BelongsToCurrentThread()) {
+    delete instance;
+    return;
+  }
+  // AudioManager must be destroyed on the audio thread.
+  if (!instance->GetTaskRunner()->DeleteSoon(FROM_HERE, instance)) {
+    LOG(WARNING) << "Failed to delete AudioManager instance.";
+  }
+}
+
 MockAudioManager::MockAudioManager(
     scoped_refptr<base::SingleThreadTaskRunner> task_runner)
     : AudioManager(task_runner, task_runner) {}
@@ -18,14 +31,17 @@
 }
 
 bool MockAudioManager::HasAudioOutputDevices() {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
   return true;
 }
 
 bool MockAudioManager::HasAudioInputDevices() {
-  return true;
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  return has_input_devices_;
 }
 
 base::string16 MockAudioManager::GetAudioInputDeviceModel() {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
   return base::string16();
 }
 
@@ -33,10 +49,14 @@
 }
 
 void MockAudioManager::GetAudioInputDeviceDescriptions(
-    AudioDeviceDescriptions* device_descriptions) {}
+    AudioDeviceDescriptions* device_descriptions) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+}
 
 void MockAudioManager::GetAudioOutputDeviceDescriptions(
-    AudioDeviceDescriptions* device_descriptions) {}
+    AudioDeviceDescriptions* device_descriptions) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+}
 
 media::AudioOutputStream* MockAudioManager::MakeAudioOutputStream(
     const media::AudioParameters& params,
@@ -75,16 +95,19 @@
 
 AudioParameters MockAudioManager::GetOutputStreamParameters(
       const std::string& device_id) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
   return AudioParameters();
 }
 
 AudioParameters MockAudioManager::GetInputStreamParameters(
     const std::string& device_id) {
-  return AudioParameters();
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  return input_params_;
 }
 
 std::string MockAudioManager::GetAssociatedOutputDeviceID(
     const std::string& input_device_id) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
   return std::string();
 }
 
@@ -97,4 +120,13 @@
   return nullptr;
 }
 
+void MockAudioManager::SetInputStreamParameters(
+    const AudioParameters& input_params) {
+  input_params_ = input_params;
+}
+
+void MockAudioManager::SetHasInputDevices(bool has_input_devices) {
+  has_input_devices_ = has_input_devices;
+}
+
 }  // namespace media.
diff --git a/media/audio/mock_audio_manager.h b/media/audio/mock_audio_manager.h
index 616f8c09..ae5bd50 100644
--- a/media/audio/mock_audio_manager.h
+++ b/media/audio/mock_audio_manager.h
@@ -6,6 +6,7 @@
 #define MEDIA_AUDIO_MOCK_AUDIO_MANAGER_H_
 
 #include "base/macros.h"
+#include "base/sequenced_task_runner_helpers.h"
 #include "media/audio/audio_manager.h"
 
 namespace media {
@@ -14,8 +15,15 @@
 // which avoids to use the actual (system and platform dependent) AudioManager.
 // Some bots does not have input devices, thus using the actual AudioManager
 // would causing failures on classes which expect that.
-class MockAudioManager : public media::AudioManager {
+class MockAudioManager : public AudioManager {
  public:
+  class Deleter {
+   public:
+    void operator()(const MockAudioManager* instance) const;
+  };
+
+  using UniquePtr = std::unique_ptr<MockAudioManager, Deleter>;
+
   explicit MockAudioManager(
       scoped_refptr<base::SingleThreadTaskRunner> task_runner);
 
@@ -33,16 +41,16 @@
   void GetAudioOutputDeviceDescriptions(
       media::AudioDeviceDescriptions* device_descriptions) override;
 
-  media::AudioOutputStream* MakeAudioOutputStream(
+  AudioOutputStream* MakeAudioOutputStream(
       const media::AudioParameters& params,
       const std::string& device_id,
       const LogCallback& log_callback) override;
 
-  media::AudioOutputStream* MakeAudioOutputStreamProxy(
+  AudioOutputStream* MakeAudioOutputStreamProxy(
       const media::AudioParameters& params,
       const std::string& device_id) override;
 
-  media::AudioInputStream* MakeAudioInputStream(
+  AudioInputStream* MakeAudioInputStream(
       const media::AudioParameters& params,
       const std::string& device_id,
       const LogCallback& log_callback) override;
@@ -63,10 +71,18 @@
 
   const char* GetName() override;
 
+  // Setters to emulate desired in-test behavior.
+  void SetInputStreamParameters(const AudioParameters& input_params);
+  void SetHasInputDevices(bool has_input_devices);
+
  protected:
   ~MockAudioManager() override;
 
  private:
+  friend class base::DeleteHelper<MockAudioManager>;
+  AudioParameters input_params_;
+  bool has_input_devices_ = true;
+
   DISALLOW_COPY_AND_ASSIGN(MockAudioManager);
 };