910e62b5创建于 1月15日历史提交
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/media/audio_output_stream_broker.h"

#include <memory>
#include <utility>

#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/sync_socket.h"
#include "base/test/mock_callback.h"
#include "base/test/task_environment.h"
#include "base/unguessable_token.h"
#include "content/browser/renderer_host/media/media_stream_manager.h"
#include "content/browser/renderer_host/media/mock_preferred_audio_output_device_manager.h"
#include "content/browser/renderer_host/media/preferred_audio_output_device_manager.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/browser_task_environment.h"
#include "media/audio/audio_system_impl.h"
#include "media/audio/fake_audio_log_factory.h"
#include "media/audio/fake_audio_manager.h"
#include "media/audio/test_audio_thread.h"
#include "media/base/audio_parameters.h"
#include "media/mojo/mojom/audio_data_pipe.mojom.h"
#include "media/mojo/mojom/audio_output_stream.mojom.h"
#include "mojo/public/cpp/bindings/pending_associated_remote.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/system/buffer.h"
#include "mojo/public/cpp/system/platform_handle.h"
#include "services/audio/public/cpp/fake_stream_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/tokens/tokens.h"

using ::testing::_;
using ::testing::InSequence;
using ::testing::Mock;
using ::testing::ReturnRef;
using ::testing::StrictMock;
using ::testing::Test;

namespace content {

namespace {

const int kRenderProcessId = 123;
const int kRenderFrameId = 234;
const GlobalRenderFrameHostToken kMainFrameHostToken{1,
                                                     blink::LocalFrameToken()};
const int kStreamId = 345;
const size_t kShMemSize = 456;
const char kDeviceId[] = "testdeviceid";

media::AudioParameters TestParams() {
  return media::AudioParameters::UnavailableDeviceParams();
}

using MockDeleterCallback = StrictMock<
    base::MockCallback<base::OnceCallback<void(AudioStreamBroker*)>>>;

class MockAudioOutputStreamProviderClient
    : public media::mojom::AudioOutputStreamProviderClient {
 public:
  MockAudioOutputStreamProviderClient() = default;

  MockAudioOutputStreamProviderClient(
      const MockAudioOutputStreamProviderClient&) = delete;
  MockAudioOutputStreamProviderClient& operator=(
      const MockAudioOutputStreamProviderClient&) = delete;

  ~MockAudioOutputStreamProviderClient() override {}

  void Created(mojo::PendingRemote<media::mojom::AudioOutputStream>,
               media::mojom::ReadWriteAudioDataPipePtr) override {
    OnCreated();
  }

  MOCK_METHOD0(OnCreated, void());

  MOCK_METHOD2(ConnectionError, void(uint32_t, const std::string&));

  mojo::PendingRemote<media::mojom::AudioOutputStreamProviderClient>
  MakePendingRemote() {
    mojo::PendingRemote<media::mojom::AudioOutputStreamProviderClient>
        pending_remote;
    receiver_.Bind(pending_remote.InitWithNewPipeAndPassReceiver());
    receiver_.set_disconnect_with_reason_handler(
        base::BindOnce(&MockAudioOutputStreamProviderClient::ConnectionError,
                       base::Unretained(this)));
    return pending_remote;
  }

  void CloseReceiver() { receiver_.reset(); }

 private:
  mojo::Receiver<media::mojom::AudioOutputStreamProviderClient> receiver_{this};
};

class MockDeviceSwitchInterface : public media::mojom::DeviceSwitchInterface {
 public:
  MockDeviceSwitchInterface() = default;

  MockDeviceSwitchInterface(const MockDeviceSwitchInterface&) = delete;
  MockDeviceSwitchInterface& operator=(const MockDeviceSwitchInterface&) =
      delete;

  ~MockDeviceSwitchInterface() override = default;

  MOCK_METHOD(void,
              SwitchAudioOutputDeviceId,
              (const std::string&),
              (override));
};

class MockStreamFactory final : public audio::FakeStreamFactory {
 public:
  MockStreamFactory() = default;

  MockStreamFactory(const MockStreamFactory&) = delete;
  MockStreamFactory& operator=(const MockStreamFactory&) = delete;

  ~MockStreamFactory() override = default;

  // State of an expected stream creation. |output_device_id|, |params|,
  // and |groups_id| are set ahead of time and verified during request.
  // The other fields are filled in when the request is received.
  struct StreamRequestData {
    StreamRequestData(const std::string& output_device_id,
                      const media::AudioParameters& params,
                      const base::UnguessableToken& group_id)
        : output_device_id(output_device_id),
          params(params),
          group_id(group_id) {}

    bool requested = false;
    mojo::PendingReceiver<media::mojom::AudioOutputStream> stream_receiver;
    mojo::PendingAssociatedRemote<media::mojom::AudioOutputStreamObserver>
        observer_remote;
    mojo::Remote<media::mojom::AudioLog> log;
    const std::string output_device_id;
    const media::AudioParameters params;
    const base::UnguessableToken group_id;
    CreateOutputStreamCallback created_callback;
  };

  void ExpectStreamCreation(StreamRequestData* ex) {
    stream_request_data_ = ex;
  }

 private:
  void CreateOutputStream(
      mojo::PendingReceiver<media::mojom::AudioOutputStream> stream_receiver,
      mojo::PendingAssociatedRemote<media::mojom::AudioOutputStreamObserver>
          observer,
      mojo::PendingRemote<media::mojom::AudioLog> log,
      const std::string& output_device_id,
      const media::AudioParameters& params,
      const base::UnguessableToken& group_id,
      CreateOutputStreamCallback created_callback) final {
    // No way to cleanly exit the test here in case of failure, so use CHECK.
    CHECK(stream_request_data_);
    EXPECT_EQ(stream_request_data_->output_device_id, output_device_id);
    EXPECT_TRUE(stream_request_data_->params.Equals(params));
    EXPECT_EQ(stream_request_data_->group_id, group_id);
    stream_request_data_->requested = true;
    stream_request_data_->stream_receiver = std::move(stream_receiver);
    stream_request_data_->observer_remote = std::move(observer);
    stream_request_data_->log.Bind(std ::move(log));
    stream_request_data_->created_callback = std::move(created_callback);
  }

  void CreateSwitchableOutputStream(
      mojo::PendingReceiver<media::mojom::AudioOutputStream> stream_receiver,
      mojo::PendingReceiver<media::mojom::DeviceSwitchInterface>
          device_switch_receiver,
      mojo::PendingAssociatedRemote<media::mojom::AudioOutputStreamObserver>
          observer,
      mojo::PendingRemote<media::mojom::AudioLog> log,
      const std::string& output_device_id,
      const media::AudioParameters& params,
      const base::UnguessableToken& group_id,
      CreateOutputStreamCallback created_callback) final {
    CreateOutputStream(std::move(stream_receiver), std::move(observer),
                       std::move(log), output_device_id, params, group_id,
                       std::move(created_callback));
  }

  raw_ptr<StreamRequestData> stream_request_data_;
};

// This struct collects test state we need without doing anything fancy.
struct TestEnvironment {
  explicit TestEnvironment(const std::string device_id = kDeviceId)
      : group(base::UnguessableToken::Create()),
        broker(std::make_unique<AudioOutputStreamBroker>(
            kRenderProcessId,
            kRenderFrameId,
            kMainFrameHostToken,
            kStreamId,
            device_id,
            TestParams(),
            group,
            deleter.Get(),
            provider_client.MakePendingRemote())) {
    audio_manager = std::make_unique<media::FakeAudioManager>(
        std::make_unique<media::TestAudioThread>(), &log_factory);
    audio_system =
        std::make_unique<media::AudioSystemImpl>(audio_manager.get());
    media_stream_manager =
        std::make_unique<MediaStreamManager>(audio_system.get());
  }

  ~TestEnvironment() { audio_manager->Shutdown(); }

  void RunUntilIdle() { env.RunUntilIdle(); }

  void BindAndSetDeviceSwitchInterface() {
    // Inject mock into AudioOutputStreamBroker
    mojo::PendingRemote<media::mojom::DeviceSwitchInterface> pending_remote;
    receiver =
        std::make_unique<mojo::Receiver<media::mojom::DeviceSwitchInterface>>(
            &device_switch_interface,
            pending_remote.InitWithNewPipeAndPassReceiver());
    mojo::Remote<media::mojom::DeviceSwitchInterface> remote(
        std::move(pending_remote));
    broker->SetDeviceSwichInterfaceForTesting(std::move(remote));
  }

  // MediaInternals RenderProcessHost observation setup asserts being run on the
  // UI thread.
  media::FakeAudioLogFactory log_factory;
  std::unique_ptr<media::FakeAudioManager> audio_manager;
  std::unique_ptr<media::AudioSystemImpl> audio_system;
  std::unique_ptr<MediaStreamManager> media_stream_manager;

  BrowserTaskEnvironment env;
  base::UnguessableToken group;
  MockDeleterCallback deleter;
  StrictMock<MockAudioOutputStreamProviderClient> provider_client;
  std::unique_ptr<AudioOutputStreamBroker> broker;
  MockStreamFactory stream_factory;
  MockDeviceSwitchInterface device_switch_interface;
  std::unique_ptr<mojo::Receiver<media::mojom::DeviceSwitchInterface>> receiver;
  mojo::Remote<media::mojom::AudioStreamFactory> factory_ptr{
      stream_factory.MakeRemote()};
};

}  // namespace

TEST(AudioOutputStreamBrokerTest, StoresProcessAndFrameId) {
  BrowserTaskEnvironment env;
  MockDeleterCallback deleter;
  StrictMock<MockAudioOutputStreamProviderClient> provider_client;

  AudioOutputStreamBroker broker(
      kRenderProcessId, kRenderFrameId, kMainFrameHostToken, kStreamId,
      kDeviceId, TestParams(), base::UnguessableToken::Create(), deleter.Get(),
      provider_client.MakePendingRemote());

  EXPECT_EQ(kRenderProcessId, broker.render_process_id());
  EXPECT_EQ(kRenderFrameId, broker.render_frame_id());
}

TEST(AudioOutputStreamBrokerTest, ClientDisconnect_CallsDeleter) {
  TestEnvironment env;

  EXPECT_CALL(env.deleter, Run(env.broker.release()))
      .WillOnce(testing::DeleteArg<0>());
  env.provider_client.CloseReceiver();
  env.RunUntilIdle();
}

TEST(AudioOutputStreamBrokerTest, StreamCreationSuccess_Propagates) {
  TestEnvironment env;
  MockStreamFactory::StreamRequestData stream_request_data(
      kDeviceId, TestParams(), env.group);
  env.stream_factory.ExpectStreamCreation(&stream_request_data);

  env.broker->CreateStream(env.factory_ptr.get());
  env.RunUntilIdle();

  EXPECT_TRUE(stream_request_data.requested);

  // Set up test IPC primitives.
  base::SyncSocket socket1, socket2;
  base::SyncSocket::CreatePair(&socket1, &socket2);
  std::move(stream_request_data.created_callback)
      .Run({std::in_place, base::UnsafeSharedMemoryRegion::Create(kShMemSize),
            mojo::PlatformHandle(socket1.Take())});

  EXPECT_CALL(env.provider_client, OnCreated());

  env.RunUntilIdle();

  Mock::VerifyAndClear(&env.provider_client);

  EXPECT_CALL(env.provider_client, ConnectionError(_, _));

  env.broker.reset();
}

TEST(AudioOutputStreamBrokerTest,
     StreamCreationFailure_PropagatesErrorAndCallsDeleter) {
  TestEnvironment env;
  MockStreamFactory::StreamRequestData stream_request_data(
      kDeviceId, TestParams(), env.group);
  env.stream_factory.ExpectStreamCreation(&stream_request_data);

  env.broker->CreateStream(env.factory_ptr.get());
  env.RunUntilIdle();

  EXPECT_TRUE(stream_request_data.requested);
  EXPECT_CALL(env.provider_client,
              ConnectionError(static_cast<uint32_t>(
                                  media::mojom::AudioOutputStreamObserver::
                                      DisconnectReason::kPlatformError),
                              std::string()));
  EXPECT_CALL(env.deleter, Run(env.broker.release()))
      .WillOnce(testing::DeleteArg<0>());

  std::move(stream_request_data.created_callback).Run(nullptr);

  env.RunUntilIdle();
}

TEST(AudioOutputStreamBrokerTest,
     ObserverDisconnect_PropagatesErrorAndCallsDeleter) {
  TestEnvironment env;
  MockStreamFactory::StreamRequestData stream_request_data(
      kDeviceId, TestParams(), env.group);
  env.stream_factory.ExpectStreamCreation(&stream_request_data);

  env.broker->CreateStream(env.factory_ptr.get());
  env.RunUntilIdle();

  EXPECT_TRUE(stream_request_data.requested);
  EXPECT_CALL(env.provider_client,
              ConnectionError(static_cast<uint32_t>(
                                  media::mojom::AudioOutputStreamObserver::
                                      DisconnectReason::kPlatformError),
                              std::string()));
  EXPECT_CALL(env.deleter, Run(env.broker.release()))
      .WillOnce(testing::DeleteArg<0>());

  // This results in a disconnect.
  stream_request_data.observer_remote.PassHandle();

  env.RunUntilIdle();
  env.stream_factory.ResetReceiver();
  env.RunUntilIdle();
}

TEST(AudioOutputStreamBrokerTest,
     FactoryDisconnectDuringConstruction_PropagatesErrorAndCallsDeleter) {
  TestEnvironment env;

  env.broker->CreateStream(env.factory_ptr.get());
  env.stream_factory.ResetReceiver();

  EXPECT_CALL(env.deleter, Run(env.broker.release()))
      .WillOnce(testing::DeleteArg<0>());
  EXPECT_CALL(env.provider_client,
              ConnectionError(static_cast<uint32_t>(
                                  media::mojom::AudioOutputStreamObserver::
                                      DisconnectReason::kPlatformError),
                              std::string()));

  env.RunUntilIdle();
}

TEST(AudioOutputStreamBrokerTest, SwitchableStreamCreationSuccess) {
  TestEnvironment env(media::AudioDeviceDescription::kDefaultDeviceId);
  MockStreamFactory::StreamRequestData stream_request_data(
      media::AudioDeviceDescription::kDefaultDeviceId, TestParams(), env.group);
  env.stream_factory.ExpectStreamCreation(&stream_request_data);

  auto mock_preferred_audio_output_device_manager =
      std::make_unique<MockPreferredAudioOutputDeviceManager>();
  MockPreferredAudioOutputDeviceManager* manager =
      mock_preferred_audio_output_device_manager.get();
  env.media_stream_manager->SetPreferredAudioOutputDeviceManagerForTesting(
      std::move(mock_preferred_audio_output_device_manager));

  EXPECT_CALL(*manager, AddSwitcher(_, _)).Times(1);

  env.broker->CreateStream(env.factory_ptr.get());
  env.RunUntilIdle();
  EXPECT_TRUE(env.broker->IsSwitchableStreamCreatedForTesting());

  EXPECT_TRUE(stream_request_data.requested);

  // Set up device switcher.
  env.BindAndSetDeviceSwitchInterface();
  constexpr char kRawDeviceId[] = "rawdeviceid";
  EXPECT_CALL(*manager,
              SetPreferredSinkId(kMainFrameHostToken, kRawDeviceId, _))
      .WillOnce([&env](const GlobalRenderFrameHostToken&,
                       const std::string& device_id,
                       base::OnceCallback<void(media::OutputDeviceStatus)>) {
        env.broker->SwitchAudioOutputDeviceId(device_id);
      });

  EXPECT_CALL(env.device_switch_interface,
              SwitchAudioOutputDeviceId(kRawDeviceId))
      .Times(1);
  manager->SetPreferredSinkId(kMainFrameHostToken, kRawDeviceId,
                              base::DoNothing());

  // Set up test IPC primitives.
  base::SyncSocket socket1, socket2;
  base::SyncSocket::CreatePair(&socket1, &socket2);
  std::move(stream_request_data.created_callback)
      .Run({std::in_place, base::UnsafeSharedMemoryRegion::Create(kShMemSize),
            mojo::PlatformHandle(socket1.Take())});

  EXPECT_CALL(env.provider_client, OnCreated());
  env.RunUntilIdle();
  Mock::VerifyAndClear(&env.provider_client);

  EXPECT_CALL(env.provider_client, ConnectionError(_, _));

  EXPECT_CALL(*manager, RemoveSwitcher(_, _)).Times(1);
  env.broker.reset();
}

TEST(AudioOutputStreamBrokerTest,
     NonSwitchableStreamCreationForNonPreferredManager) {
  // Do not call `AddSwitcher()` and `RemoveSwitcher` if
  // `PreferredAudioOutputDeviceManager` is not set.
  TestEnvironment env(media::AudioDeviceDescription::kDefaultDeviceId);
  MockStreamFactory::StreamRequestData stream_request_data(
      media::AudioDeviceDescription::kDefaultDeviceId, TestParams(), env.group);
  env.stream_factory.ExpectStreamCreation(&stream_request_data);

  auto mock_preferred_audio_output_device_manager =
      std::make_unique<MockPreferredAudioOutputDeviceManager>();
  MockPreferredAudioOutputDeviceManager* manager =
      mock_preferred_audio_output_device_manager.get();
  env.media_stream_manager->SetPreferredAudioOutputDeviceManagerForTesting(
      nullptr);

  EXPECT_CALL(*manager, AddSwitcher(_, _)).Times(0);

  env.broker->CreateStream(env.factory_ptr.get());
  env.RunUntilIdle();
  EXPECT_FALSE(env.broker->IsSwitchableStreamCreatedForTesting());

  EXPECT_TRUE(stream_request_data.requested);

  // Set up test IPC primitives.
  base::SyncSocket socket1, socket2;
  base::SyncSocket::CreatePair(&socket1, &socket2);
  std::move(stream_request_data.created_callback)
      .Run({std::in_place, base::UnsafeSharedMemoryRegion::Create(kShMemSize),
            mojo::PlatformHandle(socket1.Take())});

  EXPECT_CALL(env.provider_client, OnCreated());
  env.RunUntilIdle();
  Mock::VerifyAndClear(&env.provider_client);

  EXPECT_CALL(env.provider_client, ConnectionError(_, _));

  EXPECT_CALL(*manager, RemoveSwitcher(_, _)).Times(0);
  env.broker.reset();
}

TEST(AudioOutputStreamBrokerTest,
     UnSwitchableStreamCreationWhenNonDefaultDevice) {
  // Do not call `AddSwitcher()` if not default device id.
  TestEnvironment env(kDeviceId);
  MockStreamFactory::StreamRequestData stream_request_data(
      kDeviceId, TestParams(), env.group);
  env.stream_factory.ExpectStreamCreation(&stream_request_data);

  auto mock_preferred_audio_output_device_manager =
      std::make_unique<MockPreferredAudioOutputDeviceManager>();
  MockPreferredAudioOutputDeviceManager* manager =
      mock_preferred_audio_output_device_manager.get();
  env.media_stream_manager->SetPreferredAudioOutputDeviceManagerForTesting(
      std::move(mock_preferred_audio_output_device_manager));

  EXPECT_CALL(*manager, AddSwitcher(_, _)).Times(0);

  env.broker->CreateStream(env.factory_ptr.get());
  env.RunUntilIdle();
  EXPECT_FALSE(env.broker->IsSwitchableStreamCreatedForTesting());

  EXPECT_TRUE(stream_request_data.requested);

  // Set up test IPC primitives.
  base::SyncSocket socket1, socket2;
  base::SyncSocket::CreatePair(&socket1, &socket2);
  std::move(stream_request_data.created_callback)
      .Run({std::in_place, base::UnsafeSharedMemoryRegion::Create(kShMemSize),
            mojo::PlatformHandle(socket1.Take())});

  EXPECT_CALL(env.provider_client, OnCreated());
  env.RunUntilIdle();
  Mock::VerifyAndClear(&env.provider_client);

  EXPECT_CALL(env.provider_client, ConnectionError(_, _));

  EXPECT_CALL(*manager, RemoveSwitcher(_, _)).Times(1);
  env.broker.reset();
}

TEST(AudioOutputStreamBrokerTest, SwitchableStreamCreationWithPreferredSinkId) {
  // Switchable stream is created when preferred sink id.
  TestEnvironment env(media::AudioDeviceDescription::kDefaultDeviceId);
  MockStreamFactory::StreamRequestData stream_request_data(
      kDeviceId, TestParams(), env.group);
  env.stream_factory.ExpectStreamCreation(&stream_request_data);

  auto mock_preferred_audio_output_device_manager =
      std::make_unique<MockPreferredAudioOutputDeviceManager>();
  MockPreferredAudioOutputDeviceManager* manager =
      mock_preferred_audio_output_device_manager.get();
  env.media_stream_manager->SetPreferredAudioOutputDeviceManagerForTesting(
      std::move(mock_preferred_audio_output_device_manager));

  EXPECT_CALL(*manager, AddSwitcher(_, _))
      .WillOnce([](const GlobalRenderFrameHostToken&,
                   AudioOutputDeviceSwitcher* switcher) {
        switcher->SwitchAudioOutputDeviceId(kDeviceId);
      });

  env.broker->CreateStream(env.factory_ptr.get());
  env.RunUntilIdle();
  EXPECT_TRUE(env.broker->IsSwitchableStreamCreatedForTesting());

  EXPECT_TRUE(stream_request_data.requested);

  // Set up device switcher.
  env.BindAndSetDeviceSwitchInterface();
  EXPECT_CALL(*manager, SetPreferredSinkId(
                            kMainFrameHostToken,
                            media::AudioDeviceDescription::kDefaultDeviceId, _))
      .WillOnce([&env](const GlobalRenderFrameHostToken&,
                       const std::string& device_id,
                       base::OnceCallback<void(media::OutputDeviceStatus)>) {
        env.broker->SwitchAudioOutputDeviceId(device_id);
      });

  EXPECT_CALL(env.device_switch_interface,
              SwitchAudioOutputDeviceId(
                  media::AudioDeviceDescription::kDefaultDeviceId))
      .Times(1);
  manager->SetPreferredSinkId(kMainFrameHostToken,
                              media::AudioDeviceDescription::kDefaultDeviceId,
                              base::DoNothing());

  // Set up test IPC primitives.
  base::SyncSocket socket1, socket2;
  base::SyncSocket::CreatePair(&socket1, &socket2);
  std::move(stream_request_data.created_callback)
      .Run({std::in_place, base::UnsafeSharedMemoryRegion::Create(kShMemSize),
            mojo::PlatformHandle(socket1.Take())});

  EXPECT_CALL(env.provider_client, OnCreated());
  env.RunUntilIdle();
  Mock::VerifyAndClear(&env.provider_client);

  EXPECT_CALL(env.provider_client, ConnectionError(_, _));

  EXPECT_CALL(*manager, RemoveSwitcher(_, _)).Times(1);
  env.broker.reset();
}

}  // namespace content