chromium/chromeos/ash/components/audio/cras_audio_handler_unittest.cc

// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chromeos/ash/components/audio/cras_audio_handler.h"

#include <stddef.h>
#include <stdint.h>

#include <memory>
#include <optional>
#include <vector>

#include "ash/constants/ash_features.h"
#include "ash/strings/grit/ash_strings.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
#include "base/system/system_monitor.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chromeos/ash/components/audio/audio_devices_pref_handler.h"
#include "chromeos/ash/components/audio/audio_devices_pref_handler_stub.h"
#include "chromeos/ash/components/audio/audio_selection_notification_handler.h"
#include "chromeos/ash/components/dbus/audio/audio_node.h"
#include "chromeos/ash/components/dbus/audio/fake_cras_audio_client.h"
#include "media/base/video_facing.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "services/media_session/public/mojom/media_controller.mojom-test-utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/message_center/message_center.h"

namespace ash {

namespace {

const uint64_t kInternalSpeakerId = 10001;
const uint64_t kHeadphoneId = 10002;
const uint64_t kInternalMicId = 10003;
const uint64_t kUSBMicId1 = 10004;
const uint64_t kUSBMicId2 = 10005;
const uint64_t kBluetoothHeadsetId = 10006;
const uint64_t kHDMIOutputId = 10007;
const uint64_t kUSBHeadphoneId1 = 10008;
const uint64_t kUSBHeadphoneId2 = 10009;
const uint64_t kUSBHeadphoneId3 = 10010;
const uint64_t kMicJackId = 10011;
const uint64_t kKeyboardMicId = 10012;
const uint64_t kFrontMicId = 10013;
const uint64_t kRearMicId = 10014;
const uint64_t kBluetoothNbMicId = 10015;
const uint64_t kOtherId = 10016;
const uint64_t kUSBJabraSpeakerOutputId1 = 90003;
const uint64_t kUSBJabraSpeakerOutputId2 = 90004;
const uint64_t kUSBJabraSpeakerInputId1 = 90005;
const uint64_t kUSBJabraSpeakerInputId2 = 90006;
const uint64_t kUSBCameraInputId = 90007;

struct AudioNodeInfo {
  bool is_input;
  uint64_t id;
  const char* const device_name;
  const char* const type;
  const char* const name;
  const int32_t number_of_volume_steps;
};

const uint32_t kInputMaxSupportedChannels = 1;
const uint32_t kOutputMaxSupportedChannels = 2;

const uint32_t kInputAudioEffect = 1;
const uint32_t kOutputAudioEffect = 0;

const int kStepPercentage = 4;
const int kDefaultUnmuteVolumePercent = 4;

const AudioNodeInfo kInternalSpeaker[] = {{false, kInternalSpeakerId,
                                           "Fake Speaker", "INTERNAL_SPEAKER",
                                           "Speaker", 25}};

const AudioNodeInfo kHeadphone[] = {
    {false, kHeadphoneId, "Fake Headphone", "HEADPHONE", "Headphone", 25}};

const AudioNodeInfo kInternalMic[] = {
    {true, kInternalMicId, "Fake Mic", "INTERNAL_MIC", "Internal Mic", 0}};

const AudioNodeInfo kMicJack[] = {
    {true, kMicJackId, "Fake Mic Jack", "MIC", "Mic Jack", 0}};

const AudioNodeInfo kUSBMic1[] = {
    {true, kUSBMicId1, "Fake USB Mic 1", "USB", "USB Microphone 1", 0}};

const AudioNodeInfo kUSBMic2[] = {
    {true, kUSBMicId2, "Fake USB Mic 2", "USB", "USB Microphone 2", 0}};

const AudioNodeInfo kKeyboardMic[] = {{true, kKeyboardMicId,
                                       "Fake Keyboard Mic", "KEYBOARD_MIC",
                                       "Keyboard Mic", 0}};

const AudioNodeInfo kFrontMic[] = {
    {true, kFrontMicId, "Fake Front Mic", "FRONT_MIC", "Front Mic", 0}};

const AudioNodeInfo kRearMic[] = {
    {true, kRearMicId, "Fake Rear Mic", "REAR_MIC", "Rear Mic", 0}};

const AudioNodeInfo kOther[] = {
    {false, kOtherId, "Non Simple Output Device", "OTHER", "Other", 25}};

const AudioNodeInfo kBluetoothHeadset[] = {{false, kBluetoothHeadsetId,
                                            "Bluetooth Headset", "BLUETOOTH",
                                            "Bluetooth Headset 1", 25}};

const AudioNodeInfo kBluetoothNbMic[] = {{true, kBluetoothNbMicId,
                                          "Fake Bt Mic", "BLUETOOTH_NB_MIC",
                                          "Bluetooth Nb Mic", 0}};

const AudioNodeInfo kHDMIOutput[] = {
    {false, kHDMIOutputId, "HDMI output", "HDMI", "HDMI output", 25}};

const AudioNodeInfo kUSBHeadphone1[] = {
    {false, kUSBHeadphoneId1, "USB Headphone", "USB", "USB Headphone 1", 25}};

const AudioNodeInfo kUSBHeadphone2[] = {
    {false, kUSBHeadphoneId2, "USB Headphone", "USB", "USB Headphone 2", 16}};

const AudioNodeInfo kUSBHeadphone3[] = {
    {false, kUSBHeadphoneId3, "USB Headphone", "USB", "USB Headphone 3", 0}};

const AudioNodeInfo kUSBJabraSpeakerOutput1[] = {
    {false, kUSBJabraSpeakerOutputId1, "Jabra Speaker 1", "USB",
     "Jabra Speaker 1", 16}};

const AudioNodeInfo kUSBJabraSpeakerOutput2[] = {
    {false, kUSBJabraSpeakerOutputId2, "Jabra Speaker 2", "USB",
     "Jabra Speaker 2", 25}};

const AudioNodeInfo kUSBJabraSpeakerInput1[] = {{true, kUSBJabraSpeakerInputId1,
                                                 "Jabra Speaker 1", "USB",
                                                 "Jabra Speaker", 0}};

const AudioNodeInfo kUSBJabraSpeakerInput2[] = {{true, kUSBJabraSpeakerInputId2,
                                                 "Jabra Speaker 2", "USB",
                                                 "Jabra Speaker 2", 0}};

const AudioNodeInfo kUSBCameraInput[] = {
    {true, kUSBCameraInputId, "USB Camera", "USB", "USB Camera", 0}};

class FakeMediaControllerManager
    : public media_session::mojom::MediaControllerManagerInterceptorForTesting {
 public:
  FakeMediaControllerManager() = default;

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

  mojo::PendingRemote<media_session::mojom::MediaControllerManager>
  MakeRemote() {
    mojo::PendingRemote<media_session::mojom::MediaControllerManager> remote;
    receivers_.Add(this, remote.InitWithNewPipeAndPassReceiver());
    return remote;
  }

  void CreateActiveMediaController(
      mojo::PendingReceiver<media_session::mojom::MediaController> receiver)
      override {}

  MOCK_METHOD0(SuspendAllSessions, void());

 private:
  // media_session::mojom::MediaControllerManagerInterceptorForTesting:
  MediaControllerManager* GetForwardingInterface() override {
    NOTREACHED_IN_MIGRATION();
    return nullptr;
  }

  mojo::ReceiverSet<media_session::mojom::MediaControllerManager> receivers_;
};

class TestObserver : public CrasAudioHandler::AudioObserver {
 public:
  TestObserver() = default;

  int active_output_node_changed_count() const {
    return active_output_node_changed_count_;
  }

  void reset_active_output_node_changed_count() {
    active_output_node_changed_count_ = 0;
  }

  int active_input_node_changed_count() const {
    return active_input_node_changed_count_;
  }

  void reset_active_input_node_changed_count() {
    active_input_node_changed_count_ = 0;
  }

  int audio_nodes_changed_count() const { return audio_nodes_changed_count_; }

  int output_mute_changed_count() const { return output_mute_changed_count_; }

  void reset_output_mute_changed_count() { output_mute_changed_count_ = 0; }

  int input_mute_changed_count() const { return input_mute_changed_count_; }

  int output_volume_changed_count() const {
    return output_volume_changed_count_;
  }

  void reset_output_volume_changed_count() { output_volume_changed_count_ = 0; }

  int input_gain_changed_count() const { return input_gain_changed_count_; }

  int output_channel_remixing_changed_count() const {
    return output_channel_remixing_changed_count_;
  }

  int noise_cancellation_state_change_count() const {
    return noise_cancellation_state_change_count_;
  }

  int style_transfer_state_change_count() const {
    return style_transfer_state_change_count_;
  }

  int hfp_mic_sr_state_change_count() const {
    return hfp_mic_sr_state_change_count_;
  }

  int output_started_change_count() const {
    return output_started_change_count_;
  }

  int output_stopped_change_count() const {
    return output_stopped_change_count_;
  }

  int nonchrome_output_started_change_count() const {
    return nonchrome_output_started_change_count_;
  }

  int nonchrome_output_stopped_change_count() const {
    return nonchrome_output_stopped_change_count_;
  }

  int number_of_arc_stream_changed_latest_value() const {
    return number_of_arc_stream_changed_latest_value_;
  }

  int number_of_arc_stream_changed_count() const {
    return number_of_arc_stream_changed_count_;
  }

  int survey_triggerd_count() const { return survey_triggerd_count_; }

  int input_muted_by_security_curtain_changed_count() const {
    return input_muted_by_security_curtain_changed_count_;
  }

  const CrasAudioHandler::AudioSurvey& survey_triggerd_recv() const {
    return survey_triggerd_recv_;
  }

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

  ~TestObserver() override = default;

 protected:
  // CrasAudioHandler::AudioObserver overrides.
  void OnActiveOutputNodeChanged() override {
    ++active_output_node_changed_count_;
  }

  void OnActiveInputNodeChanged() override {
    ++active_input_node_changed_count_;
  }

  void OnAudioNodesChanged() override { ++audio_nodes_changed_count_; }

  void OnOutputMuteChanged(bool /* mute_on */) override {
    ++output_mute_changed_count_;
  }

  void OnInputMuteChanged(
      bool /* mute_on */,
      CrasAudioHandler::InputMuteChangeMethod /* method */) override {
    ++input_mute_changed_count_;
  }

  void OnOutputNodeVolumeChanged(uint64_t /* node_id */,
                                 int /* volume */) override {
    ++output_volume_changed_count_;
  }

  void OnInputNodeGainChanged(uint64_t /* node_id */, int /* gain */) override {
    ++input_gain_changed_count_;
  }

  void OnOutputChannelRemixingChanged(bool /* mono_on */) override {
    ++output_channel_remixing_changed_count_;
  }

  void OnNoiseCancellationStateChanged() override {
    ++noise_cancellation_state_change_count_;
  }

  void OnStyleTransferStateChanged() override {
    ++style_transfer_state_change_count_;
  }

  void OnHfpMicSrStateChanged() override { ++hfp_mic_sr_state_change_count_; }

  void OnOutputStarted() override { ++output_started_change_count_; }

  void OnOutputStopped() override { ++output_stopped_change_count_; }

  void OnNonChromeOutputStarted() override {
    ++nonchrome_output_started_change_count_;
  }

  void OnNonChromeOutputStopped() override {
    ++nonchrome_output_stopped_change_count_;
  }

  void OnForceRespectUiGainsStateChanged() override {}

  void OnSurveyTriggered(const CrasAudioHandler::AudioSurvey& survey) override {
    ++survey_triggerd_count_;
    survey_triggerd_recv_.set_type(survey.type());
    survey_triggerd_recv_.clear_data();

    for (const auto& it : survey.data()) {
      survey_triggerd_recv_.AddData(it.first, it.second);
    }
  }

  void OnNumberOfArcStreamsChanged(int32_t num) override {
    ++number_of_arc_stream_changed_count_;
    number_of_arc_stream_changed_latest_value_ = num;
  }

  void OnInputMutedBySecurityCurtainChanged(bool muted) override {
    ++input_muted_by_security_curtain_changed_count_;
  }

 private:
  int active_output_node_changed_count_ = 0;
  int active_input_node_changed_count_ = 0;
  int audio_nodes_changed_count_ = 0;
  int output_mute_changed_count_ = 0;
  int input_mute_changed_count_ = 0;
  int output_volume_changed_count_ = 0;
  int input_gain_changed_count_ = 0;
  int output_channel_remixing_changed_count_ = 0;
  int noise_cancellation_state_change_count_ = 0;
  int style_transfer_state_change_count_ = 0;
  int hfp_mic_sr_state_change_count_ = 0;
  int output_started_change_count_ = 0;
  int output_stopped_change_count_ = 0;
  int nonchrome_output_stopped_change_count_ = 0;
  int nonchrome_output_started_change_count_ = 0;
  int number_of_arc_stream_changed_latest_value_ = 0;
  int number_of_arc_stream_changed_count_ = 0;
  int survey_triggerd_count_ = 0;
  int input_muted_by_security_curtain_changed_count_ = 0;
  CrasAudioHandler::AudioSurvey survey_triggerd_recv_;
};

class SystemMonitorObserver
    : public base::SystemMonitor::DevicesChangedObserver {
 public:
  SystemMonitorObserver() : device_changes_received_(0) {}
  ~SystemMonitorObserver() override = default;

  void OnDevicesChanged(base::SystemMonitor::DeviceType device_type) override {
    if (device_type == base::SystemMonitor::DeviceType::DEVTYPE_AUDIO) {
      device_changes_received_++;
    }
  }

  int device_changes_received() const { return device_changes_received_; }

  void reset_count() { device_changes_received_ = 0; }

 private:
  int device_changes_received_;
};

class FakeVideoCaptureManager {
 public:
  FakeVideoCaptureManager() = default;

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

  virtual ~FakeVideoCaptureManager() = default;

  void AddObserver(media::VideoCaptureObserver* observer) {
    observers_.AddObserver(observer);
  }

  void RemoveAllObservers() { observers_.Clear(); }

  void NotifyVideoCaptureStarted(media::VideoFacingMode facing) {
    for (auto& observer : observers_) {
      observer.OnVideoCaptureStarted(facing);
    }
  }

  void NotifyVideoCaptureStopped(media::VideoFacingMode facing) {
    for (auto& observer : observers_) {
      observer.OnVideoCaptureStopped(facing);
    }
  }

 private:
  base::ObserverList<media::VideoCaptureObserver>::Unchecked observers_;
};

}  // namespace

// Test param is the version of stabel device id used by audio node.
class CrasAudioHandlerTest : public testing::TestWithParam<int> {
 public:
  CrasAudioHandlerTest()
      : task_environment_(
            base::test::SingleThreadTaskEnvironment::TimeSource::MOCK_TIME) {}

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

  ~CrasAudioHandlerTest() override = default;

  void SetUp() override {
    fake_manager_ = std::make_unique<FakeMediaControllerManager>();
    system_monitor_.AddDevicesChangedObserver(&system_monitor_observer_);
    video_capture_manager_ = std::make_unique<FakeVideoCaptureManager>();
    message_center::MessageCenter::Initialize();
  }

  void TearDown() override {
    message_center::MessageCenter::Shutdown();
    system_monitor_.RemoveDevicesChangedObserver(&system_monitor_observer_);
    cras_audio_handler_->RemoveAudioObserver(test_observer_.get());
    test_observer_.reset();
    video_capture_manager_->RemoveAllObservers();
    video_capture_manager_.reset();
    CrasAudioHandler::Shutdown();
    audio_pref_handler_ = nullptr;
    CrasAudioClient::Shutdown();

    // We can't delete the `MicrophoneMuteSwitchMonitor` singleton, so we
    // must reset it to a predictable state to prevent the tests from
    // influencing each other.
    ui::MicrophoneMuteSwitchMonitor::Get()->SetMicrophoneMuteSwitchValue(false);
  }

  AudioNode GenerateAudioNode(const AudioNodeInfo* node_info) {
    uint64_t stable_device_id_v2 = GetParam() == 1 ? 0 : (node_info->id ^ 0xFF);
    uint64_t stable_device_id_v1 = node_info->id;
    return AudioNode(
        node_info->is_input, node_info->id, GetParam() == 2,
        stable_device_id_v1, stable_device_id_v2, node_info->device_name,
        node_info->type, node_info->name, false /* is_active*/,
        0 /* pluged_time */,
        node_info->is_input ? kInputMaxSupportedChannels
                            : kOutputMaxSupportedChannels,
        node_info->is_input ? kInputAudioEffect : kOutputAudioEffect,
        node_info->number_of_volume_steps);
  }

  AudioNodeList GenerateAudioNodeList(
      const std::vector<const AudioNodeInfo*> nodes) {
    AudioNodeList node_list;
    for (auto* node_info : nodes) {
      node_list.push_back(GenerateAudioNode(node_info));
    }
    return node_list;
  }

  void SetUpCrasAudioHandler(const AudioNodeList& audio_nodes) {
    CrasAudioClient::InitializeFake();
    fake_cras_audio_client()->SetAudioNodesForTesting(audio_nodes);
    audio_pref_handler_ = base::MakeRefCounted<AudioDevicesPrefHandlerStub>();
    CrasAudioHandler::Initialize(fake_manager_->MakeRemote(),
                                 audio_pref_handler_);
    cras_audio_handler_ = CrasAudioHandler::Get();
    test_observer_ = std::make_unique<TestObserver>();
    cras_audio_handler_->AddAudioObserver(test_observer_.get());
    video_capture_manager_->AddObserver(cras_audio_handler_);
    base::RunLoop().RunUntilIdle();
  }

  // Set up cras audio handlers with |audio_nodes| and set the active state of
  // |active_device_in_pref| as active and |activate_by_user| in the pref,
  // and rest of nodes in |audio_nodes_in_pref| as inactive.
  void SetupCrasAudioHandlerWithActiveNodeInPref(
      const AudioNodeList& audio_nodes,
      const AudioNodeList& audio_nodes_in_pref,
      const AudioDevice& active_device_in_pref,
      bool activate_by_user) {
    CrasAudioClient::InitializeFake();
    audio_pref_handler_ = base::MakeRefCounted<AudioDevicesPrefHandlerStub>();
    bool active;
    for (const AudioNode& node : audio_nodes_in_pref) {
      active = node.id == active_device_in_pref.id;
      audio_pref_handler_->SetDeviceActive(AudioDevice(node), active,
                                           activate_by_user);
    }

    bool activate_by;
    EXPECT_TRUE(audio_pref_handler_->GetDeviceActive(active_device_in_pref,
                                                     &active, &activate_by));
    EXPECT_TRUE(active);
    EXPECT_EQ(activate_by, activate_by_user);

    fake_cras_audio_client()->SetAudioNodesForTesting(audio_nodes);
    CrasAudioHandler::Initialize(fake_manager_->MakeRemote(),
                                 audio_pref_handler_);

    cras_audio_handler_ = CrasAudioHandler::Get();
    test_observer_ = std::make_unique<TestObserver>();
    cras_audio_handler_->AddAudioObserver(test_observer_.get());
    base::RunLoop().RunUntilIdle();
  }

  void SetUpCrasAudioHandlerWithPrimaryActiveNode(
      const AudioNodeList& audio_nodes,
      const AudioNode& primary_active_node) {
    CrasAudioClient::InitializeFake();
    fake_cras_audio_client()->SetAudioNodesForTesting(audio_nodes);
    fake_cras_audio_client()->SetActiveOutputNode(primary_active_node.id);
    audio_pref_handler_ = base::MakeRefCounted<AudioDevicesPrefHandlerStub>();
    CrasAudioHandler::Initialize(fake_manager_->MakeRemote(),
                                 audio_pref_handler_);
    cras_audio_handler_ = CrasAudioHandler::Get();
    test_observer_ = std::make_unique<TestObserver>();
    cras_audio_handler_->AddAudioObserver(test_observer_.get());
    base::RunLoop().RunUntilIdle();
  }

  void SetUpCrasAudioHandlerWithPrimaryActiveNodeAndNoiseCancellationState(
      const AudioNodeList& audio_nodes,
      const AudioNode& primary_active_node,
      bool noise_cancellation_enabled) {
    CrasAudioClient::InitializeFake();
    fake_cras_audio_client()->SetAudioNodesForTesting(audio_nodes);
    fake_cras_audio_client()->SetActiveOutputNode(primary_active_node.id);
    fake_cras_audio_client()->SetNoiseCancellationSupported(
        /*noise_cancellation_supported=*/true);
    audio_pref_handler_ = base::MakeRefCounted<AudioDevicesPrefHandlerStub>();
    audio_pref_handler_->SetNoiseCancellationState(noise_cancellation_enabled);
    CrasAudioHandler::Initialize(fake_manager_->MakeRemote(),
                                 audio_pref_handler_);
    cras_audio_handler_ = CrasAudioHandler::Get();
    test_observer_ = std::make_unique<TestObserver>();
    cras_audio_handler_->AddAudioObserver(test_observer_.get());
    base::RunLoop().RunUntilIdle();
  }

  void SetUpCrasAudioHandlerWithPrimaryActiveNodeAndStyleTransferState(
      const AudioNodeList& audio_nodes,
      const AudioNode& primary_active_node,
      bool style_transfer_enabled) {
    CrasAudioClient::InitializeFake();
    fake_cras_audio_client()->SetAudioNodesForTesting(audio_nodes);
    fake_cras_audio_client()->SetActiveOutputNode(primary_active_node.id);
    fake_cras_audio_client()->SetStyleTransferSupported(
        /*style_transfer_supported=*/true);
    audio_pref_handler_ = base::MakeRefCounted<AudioDevicesPrefHandlerStub>();
    audio_pref_handler_->SetStyleTransferState(style_transfer_enabled);
    CrasAudioHandler::Initialize(fake_manager_->MakeRemote(),
                                 audio_pref_handler_);
    cras_audio_handler_ = CrasAudioHandler::Get();
    test_observer_ = std::make_unique<TestObserver>();
    cras_audio_handler_->AddAudioObserver(test_observer_.get());
    base::RunLoop().RunUntilIdle();
  }

  void SetUpCrasAudioHandlerWithPrimaryActiveNodeAndHfpMicSrState(
      const AudioNodeList& audio_nodes,
      const AudioNode& primary_active_node,
      bool hfp_mic_sr_enabled) {
    CrasAudioClient::InitializeFake();
    fake_cras_audio_client()->SetAudioNodesForTesting(audio_nodes);
    fake_cras_audio_client()->SetActiveOutputNode(primary_active_node.id);
    fake_cras_audio_client()->SetHfpMicSrSupported(
        /*hfp_mic_sr_supported=*/true);
    audio_pref_handler_ = base::MakeRefCounted<AudioDevicesPrefHandlerStub>();
    audio_pref_handler_->SetHfpMicSrState(hfp_mic_sr_enabled);
    CrasAudioHandler::Initialize(fake_manager_->MakeRemote(),
                                 audio_pref_handler_);
    cras_audio_handler_ = CrasAudioHandler::Get();
    test_observer_ = std::make_unique<TestObserver>();
    cras_audio_handler_->AddAudioObserver(test_observer_.get());
    base::RunLoop().RunUntilIdle();
  }

  void ChangeAudioNodes(const AudioNodeList& audio_nodes) {
    fake_cras_audio_client()->SetAudioNodesAndNotifyObserversForTesting(
        audio_nodes);
    base::RunLoop().RunUntilIdle();
  }

  void VerifySystemMonitorWasCalled() {
    // System monitor uses PostTask internally, so we have to process messages
    // before verifying the expectation.
    base::RunLoop().RunUntilIdle();
    EXPECT_LE(1, system_monitor_observer_.device_changes_received());
  }

  const AudioDevice* GetDeviceFromId(uint64_t id) {
    return cras_audio_handler_->GetDeviceFromId(id);
  }

  int GetActiveDeviceCount() const {
    int num_active_nodes = 0;
    AudioDeviceList audio_devices;
    cras_audio_handler_->GetAudioDevices(&audio_devices);
    for (size_t i = 0; i < audio_devices.size(); ++i) {
      if (audio_devices[i].active) {
        ++num_active_nodes;
      }
    }
    return num_active_nodes;
  }

  void SetActiveHDMIRediscover() {
    cras_audio_handler_->SetActiveHDMIOutoutRediscoveringIfNecessary(true);
  }

  void SetHDMIRediscoverGracePeriodDuration(int duration_in_ms) {
    cras_audio_handler_->SetHDMIRediscoverGracePeriodForTesting(duration_in_ms);
  }

  bool IsDuringHDMIRediscoverGracePeriod() {
    return cras_audio_handler_->hdmi_rediscovering();
  }

  void RestartAudioClient() {
    cras_audio_handler_->AudioClientRestarted();
    base::RunLoop().RunUntilIdle();
  }

  void StartFrontFacingCamera() {
    video_capture_manager_->NotifyVideoCaptureStarted(
        media::MEDIA_VIDEO_FACING_USER);
  }

  void StopFrontFacingCamera() {
    video_capture_manager_->NotifyVideoCaptureStopped(
        media::MEDIA_VIDEO_FACING_USER);
  }

  void StartRearFacingCamera() {
    video_capture_manager_->NotifyVideoCaptureStarted(
        media::MEDIA_VIDEO_FACING_ENVIRONMENT);
  }

  void StopRearFacingCamera() {
    video_capture_manager_->NotifyVideoCaptureStopped(
        media::MEDIA_VIDEO_FACING_ENVIRONMENT);
  }

  bool output_mono_enabled() const {
    return cras_audio_handler_->output_mono_enabled_;
  }

  const AudioDeviceMap& GetAudioDeviceMap(bool is_current_device) {
    return cras_audio_handler_->GetAudioDevicesMapForTesting(is_current_device);
  }

  // Initialize one or several audio nodes. Expect expected_active_input_node
  // and expected_active_output_node to be activated.
  void SetupAudioNodesAndExpectActiveNodes(
      const std::vector<const AudioNodeInfo*> initial_nodes,
      const AudioNodeInfo* expected_active_input_node,
      const AudioNodeInfo* expected_active_output_node,
      const std::optional<bool> expected_has_alternative_input,
      const std::optional<bool> expected_has_alternative_output) {
    AudioNodeList audio_nodes = GenerateAudioNodeList(initial_nodes);
    SetUpCrasAudioHandler(audio_nodes);

    // Verify the audio devices size.
    AudioDeviceList audio_devices;
    cras_audio_handler_->GetAudioDevices(&audio_devices);
    EXPECT_EQ(audio_nodes.size(), audio_devices.size());

    // Verify expected_active_input_node has been selected as the active output.
    if (expected_active_input_node) {
      AudioDevice active_input;
      EXPECT_TRUE(
          cras_audio_handler_->GetPrimaryActiveInputDevice(&active_input));
      EXPECT_EQ(expected_active_input_node->id, active_input.id);
      EXPECT_EQ(expected_active_input_node->id,
                cras_audio_handler_->GetPrimaryActiveInputNode());
    }
    if (expected_has_alternative_input.has_value()) {
      EXPECT_EQ(expected_has_alternative_input.value(),
                cras_audio_handler_->has_alternative_input());
    }

    // Verify expected_active_output_node has been selected as the active
    // output.
    if (expected_active_output_node) {
      AudioDevice active_output;
      EXPECT_TRUE(
          cras_audio_handler_->GetPrimaryActiveOutputDevice(&active_output));
      EXPECT_EQ(expected_active_output_node->id, active_output.id);
      EXPECT_EQ(expected_active_output_node->id,
                cras_audio_handler_->GetPrimaryActiveOutputNode());
    }
    if (expected_has_alternative_output.has_value()) {
      EXPECT_EQ(expected_has_alternative_output.value(),
                cras_audio_handler_->has_alternative_output());
    }

    EXPECT_EQ(0, test_observer_->audio_nodes_changed_count());
    EXPECT_EQ(0, test_observer_->active_input_node_changed_count());
    EXPECT_EQ(0, test_observer_->active_output_node_changed_count());
    system_monitor_observer_.reset_count();
  }

  // Expect that |expected_active_device| is the current activated device.
  // |has_alternative_device| checks if there is other alternative device.
  void ExpectActiveDevice(bool is_input,
                          const AudioNodeInfo* expected_active_device,
                          bool has_alternative_device) {
    AudioDevice active_device;
    if (is_input) {
      EXPECT_TRUE(
          cras_audio_handler_->GetPrimaryActiveInputDevice(&active_device));
      EXPECT_EQ(expected_active_device->id,
                cras_audio_handler_->GetPrimaryActiveInputNode());
      EXPECT_EQ(has_alternative_device,
                cras_audio_handler_->has_alternative_input());
    } else {
      EXPECT_TRUE(
          cras_audio_handler_->GetPrimaryActiveOutputDevice(&active_device));
      EXPECT_EQ(expected_active_device->id,
                cras_audio_handler_->GetPrimaryActiveOutputNode());
      EXPECT_EQ(has_alternative_device,
                cras_audio_handler_->has_alternative_output());
    }
    EXPECT_EQ(expected_active_device->id, active_device.id);
    EXPECT_TRUE(active_device.active);
  }

  // Get the count of audio selection notification.
  size_t GetNotificationCount() {
    auto* message_center = message_center::MessageCenter::Get();
    return message_center->NotificationCount();
  }

  // Gets the title of audio selection notification. If not found, return
  // std::nullopt.
  const std::optional<std::u16string> GetNotificationTitle() {
    auto* message_center = message_center::MessageCenter::Get();
    message_center::Notification* notification =
        message_center->FindNotificationById(
            AudioSelectionNotificationHandler::kAudioSelectionNotificationId);
    return notification ? std::make_optional(notification->title())
                        : std::nullopt;
  }

  // Helper function to call SyncDevicePrefSetMap.
  void SyncDevicePrefSetMap(bool is_input) {
    cras_audio_handler_->SyncDevicePrefSetMap(is_input);
  }

  // Retrieves input_device_pref_set_map_ or output_device_pref_set_map_.
  const std::map<std::string, std::string>& GetDevicePrefSetMap() {
    return audio_pref_handler_->GetDevicePreferenceSetMap();
  }

  // Mock time fast forward.
  void FastForwardBy(base::TimeDelta delta) {
    task_environment_.FastForwardBy(delta);
  }

  // Helper function to call GetDeviceFromStableDeviceId.
  std::optional<AudioDevice> GetDeviceFromStableDeviceId(
      bool is_input,
      uint64_t stable_device_id) {
    const AudioDevice* device =
        cras_audio_handler_->GetDeviceFromStableDeviceId(is_input,
                                                         stable_device_id);
    if (device) {
      return *device;
    } else {
      return std::nullopt;
    }
  }

 protected:
  FakeCrasAudioClient* fake_cras_audio_client() {
    return FakeCrasAudioClient::Get();
  }

  base::test::SingleThreadTaskEnvironment task_environment_;
  base::SystemMonitor system_monitor_;
  SystemMonitorObserver system_monitor_observer_;
  raw_ptr<CrasAudioHandler, DanglingUntriaged> cras_audio_handler_ =
      nullptr;  // Not owned.
  std::unique_ptr<TestObserver> test_observer_;
  scoped_refptr<AudioDevicesPrefHandlerStub> audio_pref_handler_;
  std::unique_ptr<FakeMediaControllerManager> fake_manager_;
  std::unique_ptr<FakeVideoCaptureManager> video_capture_manager_;
  base::HistogramTester histogram_tester_;
  base::test::ScopedFeatureList scoped_feature_list_;
};

class HDMIRediscoverWaiter {
 public:
  HDMIRediscoverWaiter(CrasAudioHandlerTest* cras_audio_handler_test,
                       int grace_period_duration_in_ms)
      : cras_audio_handler_test_(cras_audio_handler_test),
        grace_period_duration_in_ms_(grace_period_duration_in_ms) {}

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

  void WaitUntilTimeOut(int wait_duration_in_ms) {
    base::RunLoop run_loop;
    base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
        FROM_HERE, run_loop.QuitClosure(),
        base::Milliseconds(wait_duration_in_ms));
    run_loop.Run();
  }

  void CheckHDMIRediscoverGracePeriodEnd(base::OnceClosure quit_loop_func) {
    if (!cras_audio_handler_test_->IsDuringHDMIRediscoverGracePeriod()) {
      std::move(quit_loop_func).Run();
      return;
    }
    base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
        FROM_HERE,
        base::BindOnce(&HDMIRediscoverWaiter::CheckHDMIRediscoverGracePeriodEnd,
                       base::Unretained(this), std::move(quit_loop_func)),
        base::Milliseconds(grace_period_duration_in_ms_ / 4));
  }

  void WaitUntilHDMIRediscoverGracePeriodEnd() {
    base::RunLoop run_loop;
    CheckHDMIRediscoverGracePeriodEnd(run_loop.QuitClosure());
    run_loop.Run();
  }

 private:
  raw_ptr<CrasAudioHandlerTest> cras_audio_handler_test_;  // not owned
  int grace_period_duration_in_ms_;
};

INSTANTIATE_TEST_SUITE_P(StableIdV1, CrasAudioHandlerTest, testing::Values(1));
INSTANTIATE_TEST_SUITE_P(StableIdV2, CrasAudioHandlerTest, testing::Values(2));

TEST_P(CrasAudioHandlerTest, InitializeWithOnlyDefaultAudioDevices) {
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker, kInternalMic},
      /*expected_active_input_node=*/kInternalMic,
      /*expected_active_output_node=*/kInternalSpeaker,
      /*expected_has_alternative_input=*/false,
      /*expected_has_alternative_output=*/false);
}

TEST_P(CrasAudioHandlerTest, InitializeWithAlternativeAudioDevices) {
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker, kHeadphone, kInternalMic, kUSBMic1},
      /*expected_active_input_node=*/kUSBMic1,
      /*expected_active_output_node=*/kHeadphone,
      /*expected_has_alternative_input=*/true,
      /*expected_has_alternative_output=*/true);
}

TEST_P(CrasAudioHandlerTest, InitializeWithKeyboardMic) {
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker, kInternalMic, kKeyboardMic},
      /*expected_active_input_node=*/kInternalMic,
      /*expected_active_output_node=*/kInternalSpeaker,
      /*expected_has_alternative_input=*/false,
      /*expected_has_alternative_output=*/false);

  // Ensure keyboard_mic is not active.
  const AudioDevice* keyboard_mic = GetDeviceFromId(kKeyboardMic->id);
  EXPECT_FALSE(keyboard_mic->active);
}

TEST_P(CrasAudioHandlerTest, SetKeyboardMicActive) {
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalMic, kKeyboardMic},
      /*expected_active_input_node=*/kInternalMic,
      /*expected_active_output_node=*/nullptr,
      /*expected_has_alternative_input=*/false,
      /*expected_has_alternative_output=*/std::nullopt);

  // Ensure keyboard_mic is not active.
  const AudioDevice* keyboard_mic = GetDeviceFromId(kKeyboardMic->id);
  EXPECT_FALSE(keyboard_mic->active);

  // Make keyboard mic active.
  cras_audio_handler_->SetKeyboardMicActive(true);
  EXPECT_EQ(kInternalMic->id, cras_audio_handler_->GetPrimaryActiveInputNode());
  const AudioDevice* active_keyboard_mic = GetDeviceFromId(kKeyboardMic->id);
  EXPECT_TRUE(active_keyboard_mic->active);

  // Make keyboard mic inactive.
  cras_audio_handler_->SetKeyboardMicActive(false);
  EXPECT_EQ(kInternalMic->id, cras_audio_handler_->GetPrimaryActiveInputNode());
  const AudioDevice* inactive_keyboard_mic = GetDeviceFromId(kKeyboardMic->id);
  EXPECT_FALSE(inactive_keyboard_mic->active);
}

TEST_P(CrasAudioHandlerTest, KeyboardMicNotSetAsPrimaryActive) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({kKeyboardMic});
  SetUpCrasAudioHandler(audio_nodes);

  // Verify keyboard mic is not set as primary active input.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());
  EXPECT_TRUE(cras_audio_handler_->HasKeyboardMic());
  EXPECT_EQ(0u, cras_audio_handler_->GetPrimaryActiveInputNode());

  // Verify the internal mic is set as primary input.
  audio_nodes.push_back(GenerateAudioNode(kInternalMic));
  ChangeAudioNodes(audio_nodes);
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());
  EXPECT_TRUE(cras_audio_handler_->HasKeyboardMic());
  EXPECT_EQ(kInternalMic->id, cras_audio_handler_->GetPrimaryActiveInputNode());
}

TEST_P(CrasAudioHandlerTest, SwitchActiveOutputDevice) {
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker, kHeadphone},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kHeadphone,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/true);

  // Switch the active output to internal speaker.
  AudioDevice internal_speaker(GenerateAudioNode(kInternalSpeaker));
  cras_audio_handler_->SwitchToDevice(internal_speaker, true,
                                      DeviceActivateType::kActivateByUser);

  // Verify the active output is switched to internal speaker, and the
  // ActiveOutputNodeChanged event is fired.
  EXPECT_EQ(1, test_observer_->active_output_node_changed_count());
  VerifySystemMonitorWasCalled();
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kInternalSpeaker,
                     /*has_alternative_device=*/true);
}

TEST_P(CrasAudioHandlerTest, SwitchActiveInputDevice) {
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalMic, kUSBMic1},
      /*expected_active_input_node=*/kUSBMic1,
      /*expected_active_output_node=*/nullptr,
      /*expected_has_alternative_input=*/true,
      /*expected_has_alternative_output=*/std::nullopt);

  // Switch the active input to internal mic.
  AudioDevice internal_mic(GenerateAudioNode(kInternalMic));
  cras_audio_handler_->SwitchToDevice(internal_mic, true,
                                      DeviceActivateType::kActivateByUser);

  // Verify the active output is switched to internal speaker, and the active
  // ActiveInputNodeChanged event is fired.
  EXPECT_EQ(1, test_observer_->active_input_node_changed_count());
  EXPECT_EQ(kInternalMic->id, cras_audio_handler_->GetPrimaryActiveInputNode());
}

TEST_P(CrasAudioHandlerTest, PlugHeadphone) {
  // Set up initial audio devices, only with internal speaker.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kInternalSpeaker,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/false);

  // Plug the headphone.
  AudioNodeList audio_nodes;
  AudioNode internal_speaker = GenerateAudioNode(kInternalSpeaker);
  internal_speaker.active = true;
  audio_nodes.push_back(internal_speaker);
  audio_nodes.push_back(GenerateAudioNode(kHeadphone));
  ChangeAudioNodes(audio_nodes);

  // Verify the AudioNodesChanged event is fired and new audio device is added.
  EXPECT_EQ(1, test_observer_->audio_nodes_changed_count());
  VerifySystemMonitorWasCalled();
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(2u, audio_devices.size());

  // Verify the active output device is switched to headphone and
  // ActiveOutputChanged event is fired.
  EXPECT_EQ(1, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false, /*expected_active_device=*/kHeadphone,
                     /*has_alternative_device=*/true);
}

TEST_P(CrasAudioHandlerTest, UnplugHeadphone) {
  // Set up initial audio devices, with internal speaker and headphone.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker, kHeadphone},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kHeadphone,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/true);

  // Unplug the headphone.
  AudioNodeList audio_nodes;
  audio_nodes.push_back(GenerateAudioNode(kInternalSpeaker));
  ChangeAudioNodes(audio_nodes);

  // Verify the AudioNodesChanged event is fired and one audio device is
  // removed.
  EXPECT_EQ(1, test_observer_->audio_nodes_changed_count());
  VerifySystemMonitorWasCalled();
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(1u, audio_devices.size());

  // Verify the active output device is switched to internal speaker and
  // ActiveOutputChanged event is fired.
  EXPECT_EQ(1, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kInternalSpeaker,
                     /*has_alternative_device=*/false);
}

TEST_P(CrasAudioHandlerTest, InitializeWithBluetoothHeadset) {
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker, kBluetoothHeadset},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kBluetoothHeadset,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/true);
}

TEST_P(CrasAudioHandlerTest, ConnectAndDisconnectBluetoothHeadset) {
  // Initialize with internal speaker and headphone.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker, kHeadphone},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kHeadphone,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/true);

  // Connect to bluetooth headset. Since it is plugged in later than
  // headphone, active output should be switched to it.
  AudioNodeList audio_nodes;
  audio_nodes.push_back(GenerateAudioNode(kInternalSpeaker));
  AudioNode headphone = GenerateAudioNode(kHeadphone);
  headphone.plugged_time = 80000000;
  headphone.active = true;
  audio_nodes.push_back(headphone);
  AudioNode bluetooth_headset = GenerateAudioNode(kBluetoothHeadset);
  bluetooth_headset.plugged_time = 90000000;
  audio_nodes.push_back(bluetooth_headset);
  ChangeAudioNodes(audio_nodes);

  // Verify the AudioNodesChanged event is fired and new audio device is added.
  EXPECT_EQ(1, test_observer_->audio_nodes_changed_count());
  VerifySystemMonitorWasCalled();
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(3u, audio_devices.size());

  // Verify the active output device is switched to bluetooth headset, and
  // ActiveOutputChanged event is fired.
  EXPECT_EQ(1, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kBluetoothHeadset,
                     /*has_alternative_device=*/true);

  // Disconnect bluetooth headset.
  system_monitor_observer_.reset_count();
  audio_nodes.clear();
  audio_nodes.push_back(GenerateAudioNode(kInternalSpeaker));
  headphone.active = false;
  audio_nodes.push_back(headphone);
  ChangeAudioNodes(audio_nodes);

  // Verify the AudioNodesChanged event is fired and one audio device is
  // removed.
  EXPECT_EQ(2, test_observer_->audio_nodes_changed_count());
  VerifySystemMonitorWasCalled();
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(2u, audio_devices.size());

  // Verify the active output device is switched to headphone, and
  // ActiveOutputChanged event is fired.
  EXPECT_EQ(2, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false, /*expected_active_device=*/kHeadphone,
                     /*has_alternative_device=*/true);
}

TEST_P(CrasAudioHandlerTest, NumberNonChromeOutputs) {
  AudioNodeList audio_nodes =
      GenerateAudioNodeList({kInternalSpeaker, kHDMIOutput});
  SetUpCrasAudioHandler(audio_nodes);
  // start at 0.
  EXPECT_EQ(test_observer_->nonchrome_output_started_change_count(), 0);
  EXPECT_EQ(test_observer_->nonchrome_output_stopped_change_count(), 0);
  EXPECT_EQ(cras_audio_handler_->NumberOfNonChromeOutputStreams(), 0);
  fake_cras_audio_client()->SetNumberOfNonChromeOutputStreams(1);
  EXPECT_EQ(test_observer_->nonchrome_output_started_change_count(), 1);
  EXPECT_EQ(test_observer_->nonchrome_output_stopped_change_count(), 0);
  EXPECT_EQ(cras_audio_handler_->NumberOfNonChromeOutputStreams(), 1);
  // And again, to 2. No change expected.
  fake_cras_audio_client()->SetNumberOfNonChromeOutputStreams(2);
  EXPECT_EQ(test_observer_->nonchrome_output_started_change_count(), 1);
  EXPECT_EQ(test_observer_->nonchrome_output_stopped_change_count(), 0);
  EXPECT_EQ(cras_audio_handler_->NumberOfNonChromeOutputStreams(), 2);
  // Down to 0? it gets stopped.
  fake_cras_audio_client()->SetNumberOfNonChromeOutputStreams(0);
  EXPECT_EQ(test_observer_->nonchrome_output_started_change_count(), 1);
  EXPECT_EQ(test_observer_->nonchrome_output_stopped_change_count(), 1);
  EXPECT_EQ(cras_audio_handler_->NumberOfNonChromeOutputStreams(), 0);
  // Down to 0 again for some reason: already stopped.
  fake_cras_audio_client()->SetNumberOfNonChromeOutputStreams(0);
  EXPECT_EQ(test_observer_->nonchrome_output_started_change_count(), 1);
  EXPECT_EQ(test_observer_->nonchrome_output_stopped_change_count(), 1);
  EXPECT_EQ(cras_audio_handler_->NumberOfNonChromeOutputStreams(), 0);
  // And again, to 2. Up we go.
  fake_cras_audio_client()->SetNumberOfNonChromeOutputStreams(2);
  EXPECT_EQ(test_observer_->nonchrome_output_started_change_count(), 2);
  EXPECT_EQ(test_observer_->nonchrome_output_stopped_change_count(), 1);
  EXPECT_EQ(cras_audio_handler_->NumberOfNonChromeOutputStreams(), 2);
}

TEST_P(CrasAudioHandlerTest, NumberArcStreams) {
  AudioNodeList audio_nodes =
      GenerateAudioNodeList({kInternalSpeaker, kHDMIOutput});
  SetUpCrasAudioHandler(audio_nodes);
  // start at 0.
  EXPECT_EQ(test_observer_->number_of_arc_stream_changed_latest_value(), 0);
  EXPECT_EQ(test_observer_->number_of_arc_stream_changed_count(), 0);
  EXPECT_EQ(cras_audio_handler_->NumberOfArcStreams(), 0);
  // Go up to 1.
  fake_cras_audio_client()->SetNumberOfArcStreams(1);
  EXPECT_EQ(test_observer_->number_of_arc_stream_changed_latest_value(), 1);
  EXPECT_EQ(test_observer_->number_of_arc_stream_changed_count(), 1);
  EXPECT_EQ(cras_audio_handler_->NumberOfArcStreams(), 1);
  // Go up to 2.
  fake_cras_audio_client()->SetNumberOfArcStreams(2);
  EXPECT_EQ(test_observer_->number_of_arc_stream_changed_latest_value(), 2);
  EXPECT_EQ(test_observer_->number_of_arc_stream_changed_count(), 2);
  EXPECT_EQ(cras_audio_handler_->NumberOfArcStreams(), 2);
  // Stay at 2, no callback expected.
  fake_cras_audio_client()->SetNumberOfArcStreams(2);
  EXPECT_EQ(test_observer_->number_of_arc_stream_changed_latest_value(), 2);
  EXPECT_EQ(test_observer_->number_of_arc_stream_changed_count(), 2);
  EXPECT_EQ(cras_audio_handler_->NumberOfArcStreams(), 2);
  // Down to 0
  fake_cras_audio_client()->SetNumberOfArcStreams(0);
  EXPECT_EQ(test_observer_->number_of_arc_stream_changed_latest_value(), 0);
  EXPECT_EQ(test_observer_->number_of_arc_stream_changed_count(), 3);
  EXPECT_EQ(cras_audio_handler_->NumberOfArcStreams(), 0);
}

TEST_P(CrasAudioHandlerTest, InitializeWithHDMIOutput) {
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker, kHDMIOutput},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kHDMIOutput,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/true);
}

TEST_P(CrasAudioHandlerTest, ConnectAndDisconnectHDMIOutput) {
  // Initialize with internal speaker.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kInternalSpeaker,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/false);

  // Connect to HDMI output.
  AudioNodeList audio_nodes;
  AudioNode internal_speaker = GenerateAudioNode(kInternalSpeaker);
  internal_speaker.active = true;
  internal_speaker.plugged_time = 80000000;
  audio_nodes.push_back(internal_speaker);
  AudioNode hdmi = GenerateAudioNode(kHDMIOutput);
  hdmi.plugged_time = 90000000;
  audio_nodes.push_back(hdmi);
  ChangeAudioNodes(audio_nodes);

  // Verify the AudioNodesChanged event is fired and new audio device is added.
  EXPECT_EQ(1, test_observer_->audio_nodes_changed_count());
  VerifySystemMonitorWasCalled();
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(2u, audio_devices.size());

  // Verify the active output device is switched to hdmi output, and
  // ActiveOutputChanged event is fired.
  EXPECT_EQ(1, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false, /*expected_active_device=*/kHDMIOutput,
                     /*has_alternative_device=*/true);
  system_monitor_observer_.reset_count();

  // Disconnect hdmi headset.
  audio_nodes.clear();
  audio_nodes.push_back(GenerateAudioNode(kInternalSpeaker));
  ChangeAudioNodes(audio_nodes);

  // Verify the AudioNodesChanged event is fired and one audio device is
  // removed.
  EXPECT_EQ(2, test_observer_->audio_nodes_changed_count());
  VerifySystemMonitorWasCalled();
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(1u, audio_devices.size());

  // Verify the active output device is switched to internal speaker, and
  // ActiveOutputChanged event is fired.
  EXPECT_EQ(2, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kInternalSpeaker,
                     /*has_alternative_device=*/false);
}

TEST_P(CrasAudioHandlerTest,
       ConnectAndDisconnectHDMIOutput_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);

  // Initialize with internal speaker.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kInternalSpeaker,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/false);

  // Expect that there is no notification when initializing the audio nodes.
  EXPECT_EQ(0u, GetNotificationCount());

  // Connect to HDMI output.
  AudioNodeList audio_nodes;
  AudioNode internal_speaker = GenerateAudioNode(kInternalSpeaker);
  internal_speaker.active = true;
  internal_speaker.plugged_time = 80000000;
  audio_nodes.push_back(internal_speaker);
  AudioNode hdmi = GenerateAudioNode(kHDMIOutput);
  hdmi.plugged_time = 90000000;
  audio_nodes.push_back(hdmi);
  ChangeAudioNodes(audio_nodes);

  // Verify the AudioNodesChanged event is fired and new audio device is added.
  EXPECT_EQ(1, test_observer_->audio_nodes_changed_count());
  VerifySystemMonitorWasCalled();
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(2u, audio_devices.size());

  // Verify the active output device is not switched to hdmi output, and
  // ActiveOutputChanged event is not fired.
  EXPECT_EQ(0, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kInternalSpeaker,
                     /*has_alternative_device=*/true);
  system_monitor_observer_.reset_count();

  // Verify notification shows up for unseen new connected HDMI device.
  FastForwardBy(AudioSelectionNotificationHandler::kDebounceTime);
  EXPECT_EQ(1u, GetNotificationCount());

  // Disconnect hdmi headset.
  audio_nodes.clear();
  audio_nodes.push_back(GenerateAudioNode(kInternalSpeaker));
  ChangeAudioNodes(audio_nodes);

  // Verify the AudioNodesChanged event is fired and one audio device is
  // removed.
  EXPECT_EQ(2, test_observer_->audio_nodes_changed_count());
  VerifySystemMonitorWasCalled();
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(1u, audio_devices.size());

  // Verify the active output device is still internal speaker, and
  // ActiveOutputChanged event is not fired.
  EXPECT_EQ(1, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kInternalSpeaker,
                     /*has_alternative_device=*/false);

  // Verify that the notification is removed because the hotplugged HDMI output
  // is disconnected.
  FastForwardBy(CrasAudioHandler::kRemoveNotificationDelay);
  EXPECT_EQ(0u, GetNotificationCount());
}

TEST_P(CrasAudioHandlerTest, HandleHeadphoneAndHDMIOutput) {
  // Initialize with internal speaker, headphone and HDMI output.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker, kHeadphone, kHDMIOutput},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kHeadphone,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/true);

  // Disconnect HDMI output.
  AudioNodeList audio_nodes;
  audio_nodes.push_back(GenerateAudioNode(kInternalSpeaker));
  audio_nodes.push_back(GenerateAudioNode(kHDMIOutput));
  ChangeAudioNodes(audio_nodes);

  // Verify the AudioNodesChanged event is fired and one audio device is
  // removed.
  EXPECT_EQ(1, test_observer_->audio_nodes_changed_count());
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(2u, audio_devices.size());

  // Verify the active output device is switched to HDMI output, and
  // ActiveOutputChanged event is fired.
  EXPECT_EQ(1, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false, /*expected_active_device=*/kHDMIOutput,
                     /*has_alternative_device=*/true);
}

TEST_P(CrasAudioHandlerTest, InitializeWithUSBHeadphone) {
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker, kUSBHeadphone1},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kUSBHeadphone1,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/true);
}

TEST_P(CrasAudioHandlerTest, PlugAndUnplugUSBHeadphone) {
  // Initialize with internal speaker.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kInternalSpeaker,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/false);

  // Plug in usb headphone
  AudioNodeList audio_nodes;
  AudioNode internal_speaker = GenerateAudioNode(kInternalSpeaker);
  internal_speaker.active = true;
  internal_speaker.plugged_time = 80000000;
  audio_nodes.push_back(internal_speaker);
  AudioNode usb_headphone = GenerateAudioNode(kUSBHeadphone1);
  usb_headphone.plugged_time = 90000000;
  audio_nodes.push_back(usb_headphone);
  ChangeAudioNodes(audio_nodes);

  // Verify the AudioNodesChanged event is fired and new audio device is added.
  EXPECT_EQ(1, test_observer_->audio_nodes_changed_count());
  VerifySystemMonitorWasCalled();
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(2u, audio_devices.size());

  // Verify the active output device is switched to usb headphone, and
  // ActiveOutputChanged event is fired.
  EXPECT_EQ(1, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kUSBHeadphone1,
                     /*has_alternative_device=*/true);
  system_monitor_observer_.reset_count();

  // Unplug usb headphone.
  audio_nodes.clear();
  audio_nodes.push_back(GenerateAudioNode(kInternalSpeaker));
  ChangeAudioNodes(audio_nodes);

  // Verify the AudioNodesChanged event is fired and one audio device is
  // removed.
  EXPECT_EQ(2, test_observer_->audio_nodes_changed_count());
  VerifySystemMonitorWasCalled();
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(1u, audio_devices.size());

  // Verify the active output device is switched to internal speaker, and
  // ActiveOutputChanged event is fired.
  EXPECT_EQ(2, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kInternalSpeaker,
                     /*has_alternative_device=*/false);
}

TEST_P(CrasAudioHandlerTest,
       PlugAndUnplugUSBHeadphone_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);

  // Initialize with internal speaker.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kInternalSpeaker,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/false);

  // Expect that there is no notification when initializing the audio nodes.
  EXPECT_EQ(0u, GetNotificationCount());

  // Plug in usb headphone
  AudioNodeList audio_nodes;
  AudioNode internal_speaker = GenerateAudioNode(kInternalSpeaker);
  internal_speaker.active = true;
  internal_speaker.plugged_time = 80000000;
  audio_nodes.push_back(internal_speaker);
  AudioNode usb_headphone = GenerateAudioNode(kUSBHeadphone1);
  usb_headphone.plugged_time = 90000000;
  audio_nodes.push_back(usb_headphone);
  ChangeAudioNodes(audio_nodes);

  // Verify the AudioNodesChanged event is fired and new audio device is added.
  EXPECT_EQ(1, test_observer_->audio_nodes_changed_count());
  VerifySystemMonitorWasCalled();
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(2u, audio_devices.size());

  // Verify the active output device is not switched to usb headphone, and
  // ActiveOutputChanged event is not fired.
  EXPECT_EQ(0, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kInternalSpeaker,
                     /*has_alternative_device=*/true);
  system_monitor_observer_.reset_count();

  // Verify notification shows up for unseen new connected USB headphone device.
  FastForwardBy(AudioSelectionNotificationHandler::kDebounceTime);
  EXPECT_EQ(1u, GetNotificationCount());

  // Unplug usb headphone.
  audio_nodes.clear();
  audio_nodes.push_back(internal_speaker);
  ChangeAudioNodes(audio_nodes);

  // Verify the AudioNodesChanged event is fired and one audio device is
  // removed.
  EXPECT_EQ(2, test_observer_->audio_nodes_changed_count());
  VerifySystemMonitorWasCalled();
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(1u, audio_devices.size());

  // Verify the active output device is still internal speaker, and
  // ActiveOutputChanged event is not fired.
  EXPECT_EQ(0, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kInternalSpeaker,
                     /*has_alternative_device=*/false);

  // Verify that the notification is removed because the hotplugged USB output
  // is disconnected.
  FastForwardBy(CrasAudioHandler::kRemoveNotificationDelay);
  EXPECT_EQ(0u, GetNotificationCount());
}

TEST_P(CrasAudioHandlerTest, HandleMultipleUSBHeadphones) {
  // Initialize with internal speaker and one usb headphone.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker, kUSBHeadphone1},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kUSBHeadphone1,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/true);

  // Plug in another usb headphone.
  AudioNodeList audio_nodes;
  audio_nodes.push_back(GenerateAudioNode(kInternalSpeaker));
  AudioNode usb_headphone_1 = GenerateAudioNode(kUSBHeadphone1);
  usb_headphone_1.active = true;
  usb_headphone_1.plugged_time = 80000000;
  audio_nodes.push_back(usb_headphone_1);
  AudioNode usb_headphone_2 = GenerateAudioNode(kUSBHeadphone2);
  usb_headphone_2.plugged_time = 90000000;
  audio_nodes.push_back(usb_headphone_2);
  ChangeAudioNodes(audio_nodes);

  // Verify the AudioNodesChanged event is fired and new audio device is added.
  EXPECT_EQ(1, test_observer_->audio_nodes_changed_count());
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(3u, audio_devices.size());

  // Verify the active output device is switched to the 2nd usb headphone, which
  // is plugged later, and ActiveOutputChanged event is fired.
  EXPECT_EQ(1, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kUSBHeadphone2,
                     /*has_alternative_device=*/true);

  // Unplug the 2nd usb headphone.
  audio_nodes.clear();
  audio_nodes.push_back(GenerateAudioNode(kInternalSpeaker));
  audio_nodes.push_back(GenerateAudioNode(kUSBHeadphone1));
  ChangeAudioNodes(audio_nodes);

  // Verify the AudioNodesChanged event is fired and one audio device is
  // removed.
  EXPECT_EQ(2, test_observer_->audio_nodes_changed_count());
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(2u, audio_devices.size());

  // Verify the active output device is switched to the first usb headphone, and
  // ActiveOutputChanged event is fired.
  EXPECT_EQ(2, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kUSBHeadphone1,
                     /*has_alternative_device=*/true);
}

TEST_P(CrasAudioHandlerTest,
       HandleMultipleUSBHeadphones_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);

  // Initialize with internal speaker and one usb headphone.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker, kUSBHeadphone1},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kInternalSpeaker,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/true);

  // Expect that notification is displayed since the device set was unseen
  // before.
  FastForwardBy(AudioSelectionNotificationHandler::kDebounceTime);
  EXPECT_EQ(1u, GetNotificationCount());

  // Plug in another usb headphone.
  AudioNodeList audio_nodes;
  AudioNode internal_speaker = GenerateAudioNode(kInternalSpeaker);
  internal_speaker.active = true;
  audio_nodes.push_back(internal_speaker);
  AudioNode usb_headphone_1 = GenerateAudioNode(kUSBHeadphone1);
  usb_headphone_1.plugged_time = 80000000;
  audio_nodes.push_back(usb_headphone_1);
  AudioNode usb_headphone_2 = GenerateAudioNode(kUSBHeadphone2);
  usb_headphone_2.plugged_time = 90000000;
  audio_nodes.push_back(usb_headphone_2);
  ChangeAudioNodes(audio_nodes);

  // Verify the AudioNodesChanged event is fired and new audio device is added.
  EXPECT_EQ(1, test_observer_->audio_nodes_changed_count());
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(3u, audio_devices.size());

  // Verify the active output device is not switched to the 2nd usb headphone,
  // which is plugged later, and ActiveOutputChanged event is not fired.
  EXPECT_EQ(0, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kInternalSpeaker,
                     /*has_alternative_device=*/true);

  // Verify notification shows up for unseen new connected USB headphone device.
  FastForwardBy(AudioSelectionNotificationHandler::kDebounceTime);
  EXPECT_EQ(1u, GetNotificationCount());

  // Unplug the 2nd usb headphone.
  audio_nodes.clear();
  audio_nodes.push_back(internal_speaker);
  audio_nodes.push_back(usb_headphone_1);
  ChangeAudioNodes(audio_nodes);

  // Verify the AudioNodesChanged event is fired and one audio device is
  // removed.
  EXPECT_EQ(2, test_observer_->audio_nodes_changed_count());
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(2u, audio_devices.size());

  // Verify the active output device is not changed, and
  // ActiveOutputChanged event is not fired.
  EXPECT_EQ(0, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kInternalSpeaker,
                     /*has_alternative_device=*/true);

  // Verify that the notification is removed because the hotplugged USB output
  // is disconnected.
  FastForwardBy(CrasAudioHandler::kRemoveNotificationDelay);
  EXPECT_EQ(0u, GetNotificationCount());
}

TEST_P(CrasAudioHandlerTest, UnplugUSBHeadphonesWithActiveSpeaker) {
  // Initialize with internal speaker and one usb headphone.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker, kUSBHeadphone1},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kUSBHeadphone1,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/true);

  // Plug in the headphone jack.
  AudioNodeList audio_nodes;
  audio_nodes.push_back(GenerateAudioNode(kInternalSpeaker));
  AudioNode usb_headphone_1 = GenerateAudioNode(kUSBHeadphone1);
  usb_headphone_1.active = true;
  usb_headphone_1.plugged_time = 80000000;
  audio_nodes.push_back(usb_headphone_1);
  AudioNode headphone_jack = GenerateAudioNode(kHeadphone);
  headphone_jack.plugged_time = 90000000;
  audio_nodes.push_back(headphone_jack);
  ChangeAudioNodes(audio_nodes);

  // Verify the AudioNodesChanged event is fired and new audio device is added.
  EXPECT_EQ(1, test_observer_->audio_nodes_changed_count());
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(3u, audio_devices.size());

  // Verify the active output device is switched to the headphone jack, which
  // is plugged later, and ActiveOutputChanged event is fired.
  EXPECT_EQ(1, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false, /*expected_active_device=*/kHeadphone,
                     /*has_alternative_device=*/true);

  // Select the speaker to be the active output device.
  AudioDevice internal_speaker(GenerateAudioNode(kInternalSpeaker));
  cras_audio_handler_->SwitchToDevice(internal_speaker, true,
                                      DeviceActivateType::kActivateByUser);

  // Verify the active output is switched to internal speaker, and the
  // ActiveOutputNodeChanged event is fired.
  EXPECT_EQ(2, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kInternalSpeaker,
                     /*has_alternative_device=*/true);

  // Unplug the usb headphone.
  audio_nodes.clear();
  AudioNode internal_speaker_node(GenerateAudioNode(kInternalSpeaker));
  internal_speaker_node.active = true;
  internal_speaker_node.plugged_time = 70000000;
  audio_nodes.push_back(internal_speaker_node);
  headphone_jack.active = false;
  audio_nodes.push_back(headphone_jack);
  ChangeAudioNodes(audio_nodes);

  // Verify the AudioNodesChanged event is fired and one audio device is
  // removed.
  EXPECT_EQ(2, test_observer_->audio_nodes_changed_count());
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(2u, audio_devices.size());

  // Verify the active output device remains to be speaker.
  EXPECT_EQ(2, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kInternalSpeaker,
                     /*has_alternative_device=*/true);
}

TEST_P(CrasAudioHandlerTest, OneActiveAudioOutputAfterLoginNewUserSession) {
  // This tests the case found with crbug.com/273271.
  // Initialize with internal speaker, bluetooth headphone and headphone jack
  // for a new chrome session after user signs out from the previous session.
  // Headphone jack is plugged in later than bluetooth headphone, but bluetooth
  // headphone is selected as the active output by user from previous user
  // session.
  AudioNodeList audio_nodes = GenerateAudioNodeList({kInternalSpeaker});
  AudioNode bluetooth_headphone = GenerateAudioNode(kBluetoothHeadset);
  bluetooth_headphone.active = true;
  bluetooth_headphone.plugged_time = 70000000;
  audio_nodes.push_back(bluetooth_headphone);
  AudioNode headphone_jack = GenerateAudioNode(kHeadphone);
  headphone_jack.plugged_time = 80000000;
  audio_nodes.push_back(headphone_jack);
  SetUpCrasAudioHandlerWithPrimaryActiveNode(audio_nodes, bluetooth_headphone);
  const size_t init_nodes_size = audio_nodes.size();

  // Verify the audio devices size.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(init_nodes_size, audio_devices.size());
  EXPECT_EQ(0, test_observer_->audio_nodes_changed_count());

  // Verify the headphone jack is selected as the active output and all other
  // audio devices are not active.
  EXPECT_EQ(0, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false, /*expected_active_device=*/kHeadphone,
                     /*has_alternative_device=*/true);
  for (size_t i = 0; i < audio_devices.size(); ++i) {
    if (audio_devices[i].id != kHeadphone->id) {
      EXPECT_FALSE(audio_devices[i].active);
    }
  }
}

TEST_P(CrasAudioHandlerTest, NoiseCancellationRefreshPrefEnabledNoNC) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({});
  // Set up initial audio devices, only with internal mic.
  AudioNode internalMic = GenerateAudioNode(kInternalMic);
  // Clear the audio effect, no Noise Cancellation supported.
  internalMic.audio_effect = 0u;
  audio_nodes.push_back(internalMic);
  // Simulate enable pref for noise cancellation.
  SetUpCrasAudioHandlerWithPrimaryActiveNodeAndNoiseCancellationState(
      audio_nodes, internalMic, /*noise_cancellation_enabled=*/true);

  // Noise cancellation should still be disabled despite the pref being enabled
  // since the audio_effect of the internal mic is unavailable.
  EXPECT_FALSE(fake_cras_audio_client()->noise_cancellation_enabled());
  EXPECT_TRUE(audio_pref_handler_->GetNoiseCancellationState());
}

TEST_P(CrasAudioHandlerTest, NoiseCancellationRefreshPrefEnabledWithNC) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({});
  // Set up initial audio devices, only with internal mic.
  AudioNode internalMic = GenerateAudioNode(kInternalMic);
  // Enable noise cancellation effect.
  internalMic.audio_effect = cras::EFFECT_TYPE_NOISE_CANCELLATION;
  audio_nodes.push_back(internalMic);
  // Simulate enable pref for noise cancellation.
  SetUpCrasAudioHandlerWithPrimaryActiveNodeAndNoiseCancellationState(
      audio_nodes, internalMic, /*noise_cancellation_enabled=*/true);

  // Noise Cancellation is enabled.
  EXPECT_TRUE(fake_cras_audio_client()->noise_cancellation_enabled());
  EXPECT_TRUE(audio_pref_handler_->GetNoiseCancellationState());
}

TEST_P(CrasAudioHandlerTest, NoiseCancellationRefreshPrefDisableNoNC) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({});
  // Set up initial audio devices, only with internal mic.
  AudioNode internalMic = GenerateAudioNode(kInternalMic);
  // Clear audio effect, no noise cancellation.
  internalMic.audio_effect = 0u;
  audio_nodes.push_back(internalMic);
  // Simulate enable pref for noise cancellation.
  SetUpCrasAudioHandlerWithPrimaryActiveNodeAndNoiseCancellationState(
      audio_nodes, internalMic, /*noise_cancellation_enabled=*/false);

  // Noise cancellation should still be disabled since the pref is disabled.
  EXPECT_FALSE(fake_cras_audio_client()->noise_cancellation_enabled());
  EXPECT_FALSE(audio_pref_handler_->GetNoiseCancellationState());
}

TEST_P(CrasAudioHandlerTest, NoiseCancellationRefreshPrefDisableWithNC) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({});
  // Set up initial audio devices, only with internal mic.
  AudioNode internalMic = GenerateAudioNode(kInternalMic);
  // Enable noise cancellation effect.
  internalMic.audio_effect = cras::EFFECT_TYPE_NOISE_CANCELLATION;
  audio_nodes.push_back(internalMic);
  // Simulate enable pref for noise cancellation.
  SetUpCrasAudioHandlerWithPrimaryActiveNodeAndNoiseCancellationState(
      audio_nodes, internalMic, /*noise_cancellation_enabled=*/false);

  // Noise cancellation should still be disabled since the pref is disabled.
  EXPECT_FALSE(fake_cras_audio_client()->noise_cancellation_enabled());
  EXPECT_FALSE(audio_pref_handler_->GetNoiseCancellationState());
}

TEST_P(CrasAudioHandlerTest, StyleTransferRefreshPrefEnabledNoStyleTransfer) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({});
  // Set up initial audio devices, only with internal mic.
  AudioNode internalMic = GenerateAudioNode(kInternalMic);
  // Clear the audio effect, no style transfer supported.
  internalMic.audio_effect = 0u;
  audio_nodes.push_back(internalMic);
  // Simulate enable pref for style transfer.
  SetUpCrasAudioHandlerWithPrimaryActiveNodeAndStyleTransferState(
      audio_nodes, internalMic, /*style_transfer_enabled=*/true);

  // Style transfer should still be disabled despite the pref being enabled
  // since the audio_effect of the internal mic is unavailable.
  EXPECT_FALSE(fake_cras_audio_client()->style_transfer_enabled());
  EXPECT_TRUE(audio_pref_handler_->GetStyleTransferState());
}

TEST_P(CrasAudioHandlerTest, StyleTransferRefreshPrefEnabledWithStyleTransfer) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({});
  // Set up initial audio devices, only with internal mic.
  AudioNode internalMic = GenerateAudioNode(kInternalMic);
  // Enable style transfer effect.
  internalMic.audio_effect = cras::EFFECT_TYPE_STYLE_TRANSFER;
  audio_nodes.push_back(internalMic);
  // Simulate enable pref for style transfer.
  SetUpCrasAudioHandlerWithPrimaryActiveNodeAndStyleTransferState(
      audio_nodes, internalMic, /*style_transfer_enabled=*/true);

  // Style transfer is enabled.
  EXPECT_TRUE(fake_cras_audio_client()->style_transfer_enabled());
  EXPECT_TRUE(audio_pref_handler_->GetStyleTransferState());
}

TEST_P(CrasAudioHandlerTest, StyleTransferRefreshPrefDisableNoStyleTransfer) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({});
  // Set up initial audio devices, only with internal mic.
  AudioNode internalMic = GenerateAudioNode(kInternalMic);
  // Clear audio effect, no style transfer.
  internalMic.audio_effect = 0u;
  audio_nodes.push_back(internalMic);
  // Simulate enable pref for style transfer.
  SetUpCrasAudioHandlerWithPrimaryActiveNodeAndStyleTransferState(
      audio_nodes, internalMic, /*style_transfer_enabled=*/false);

  // Style transfer should still be disabled since the pref is disabled.
  EXPECT_FALSE(fake_cras_audio_client()->style_transfer_enabled());
  EXPECT_FALSE(audio_pref_handler_->GetStyleTransferState());
}

TEST_P(CrasAudioHandlerTest, StyleTransferRefreshPrefDisableWithStyleTransfer) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({});
  // Set up initial audio devices, only with internal mic.
  AudioNode internalMic = GenerateAudioNode(kInternalMic);
  // Enable style transfer effect.
  internalMic.audio_effect = cras::EFFECT_TYPE_STYLE_TRANSFER;
  audio_nodes.push_back(internalMic);
  // Simulate enable pref for style transfer.
  SetUpCrasAudioHandlerWithPrimaryActiveNodeAndStyleTransferState(
      audio_nodes, internalMic, /*style_transfer_enabled=*/false);

  // Style transfer should still be disabled since the pref is disabled.
  EXPECT_FALSE(fake_cras_audio_client()->style_transfer_enabled());
  EXPECT_FALSE(audio_pref_handler_->GetStyleTransferState());
}

TEST_P(CrasAudioHandlerTest, HfpMicSrRefreshPrefEnabledNoHfpMicSr) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({});
  // Set up initial audio devices, only with bluetooth mic.
  AudioNode bt_nb_mic = GenerateAudioNode(kBluetoothNbMic);
  // Clear the audio effect, no hfp_mic_sr supported.
  bt_nb_mic.audio_effect = 0u;
  audio_nodes.push_back(bt_nb_mic);
  // Simulate enable pref for hfp_mic_sr.
  SetUpCrasAudioHandlerWithPrimaryActiveNodeAndHfpMicSrState(
      audio_nodes, bt_nb_mic, /*hfp_mic_sr_enabled=*/true);

  // hfp_mic_sr should still be disabled despite the pref being enabled
  // since the audio_effect of the hfp mic sr is unavailable.
  EXPECT_FALSE(fake_cras_audio_client()->hfp_mic_sr_enabled());
  EXPECT_TRUE(audio_pref_handler_->GetHfpMicSrState());
}

TEST_P(CrasAudioHandlerTest, HfpMicSrRefreshPrefEnabledWithHfpMicSr) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({});
  // Set up initial audio devices, only with bluetooth mic.
  AudioNode bt_nb_mic = GenerateAudioNode(kBluetoothNbMic);
  // Enable hfp_mic_sr effect.
  bt_nb_mic.audio_effect = cras::EFFECT_TYPE_HFP_MIC_SR;
  audio_nodes.push_back(bt_nb_mic);
  // Simulate enable pref for hfp_mic_sr.
  SetUpCrasAudioHandlerWithPrimaryActiveNodeAndHfpMicSrState(
      audio_nodes, bt_nb_mic, /*hfp_mic_sr_enabled=*/true);

  // hfp_mic_sr is enabled.
  EXPECT_TRUE(fake_cras_audio_client()->hfp_mic_sr_enabled());
  EXPECT_TRUE(audio_pref_handler_->GetHfpMicSrState());
}

TEST_P(CrasAudioHandlerTest, HfpMicSrRefreshPrefDisableNoHfpMicSr) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({});
  // Set up initial audio devices, only with bluetooth mic.
  AudioNode bt_nb_mic = GenerateAudioNode(kBluetoothNbMic);
  // Clear audio effect, no hfp_mic_sr.
  bt_nb_mic.audio_effect = 0u;
  audio_nodes.push_back(bt_nb_mic);
  // Simulate enable pref for hfp_mic_sr.
  SetUpCrasAudioHandlerWithPrimaryActiveNodeAndHfpMicSrState(
      audio_nodes, bt_nb_mic, /*hfp_mic_sr_enabled=*/false);

  // hfp_mic_sr should still be disabled since the pref is disabled.
  EXPECT_FALSE(fake_cras_audio_client()->hfp_mic_sr_enabled());
  EXPECT_FALSE(audio_pref_handler_->GetHfpMicSrState());
}

TEST_P(CrasAudioHandlerTest, HfpMicSrRefreshPrefDisableWithHfpMicSr) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({});
  // Set up initial audio devices, only with bluetooth mic.
  AudioNode bt_nb_mic = GenerateAudioNode(kBluetoothNbMic);
  // Enable hfp_mic_sr effect.
  bt_nb_mic.audio_effect = cras::EFFECT_TYPE_HFP_MIC_SR;
  audio_nodes.push_back(bt_nb_mic);
  // Simulate enable pref for hfp_mic_sr.
  SetUpCrasAudioHandlerWithPrimaryActiveNodeAndHfpMicSrState(
      audio_nodes, bt_nb_mic, /*hfp_mic_sr_enabled=*/false);

  // hfp_mic_sr should still be disabled since the pref is disabled.
  EXPECT_FALSE(fake_cras_audio_client()->hfp_mic_sr_enabled());
  EXPECT_FALSE(audio_pref_handler_->GetHfpMicSrState());
}

TEST_P(CrasAudioHandlerTest, BluetoothSpeakerIdChangedOnFly) {
  // Initialize with internal speaker and bluetooth headset.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker, kBluetoothHeadset},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kBluetoothHeadset,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/true);

  // Cras changes the bluetooth headset's id on the fly.
  AudioNodeList audio_nodes;
  AudioNode internal_speaker(GenerateAudioNode(kInternalSpeaker));
  internal_speaker.active = false;
  audio_nodes.push_back(internal_speaker);
  AudioNode bluetooth_headphone = GenerateAudioNode(kBluetoothHeadset);
  // Change bluetooth headphone id.
  bluetooth_headphone.id = kBluetoothHeadsetId + 20000;
  bluetooth_headphone.active = false;
  audio_nodes.push_back(bluetooth_headphone);
  ChangeAudioNodes(audio_nodes);

  // Verify NodesChanged event is fired, and the audio devices size is not
  // changed.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(2u, audio_devices.size());
  EXPECT_EQ(1, test_observer_->audio_nodes_changed_count());

  // Verify ActiveOutputNodeChanged event is fired, and active device should be
  // bluetooth headphone.
  EXPECT_EQ(1, test_observer_->active_output_node_changed_count());
  AudioDevice active_output;
  EXPECT_TRUE(
      cras_audio_handler_->GetPrimaryActiveOutputDevice(&active_output));
  EXPECT_EQ(bluetooth_headphone.id, active_output.id);
}

TEST_P(CrasAudioHandlerTest, PlugUSBMic) {
  // Set up initial audio devices, only with internal mic.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalMic},
      /*expected_active_input_node=*/kInternalMic,
      /*expected_active_output_node=*/nullptr,
      /*expected_has_alternative_input=*/false,
      /*expected_has_alternative_output=*/std::nullopt);

  // Plug the USB Mic.
  AudioNodeList audio_nodes;
  AudioNode internal_mic(GenerateAudioNode(kInternalMic));
  internal_mic.active = true;
  audio_nodes.push_back(internal_mic);
  audio_nodes.push_back(GenerateAudioNode(kUSBMic1));
  ChangeAudioNodes(audio_nodes);

  // Verify the AudioNodesChanged event is fired and new audio device is added.
  EXPECT_EQ(1, test_observer_->audio_nodes_changed_count());
  VerifySystemMonitorWasCalled();
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(2u, audio_devices.size());

  // Verify the active input device is switched to USB mic and
  // and ActiveInputChanged event is fired.
  EXPECT_EQ(1, test_observer_->active_input_node_changed_count());
  EXPECT_EQ(kUSBMicId1, cras_audio_handler_->GetPrimaryActiveInputNode());
  EXPECT_TRUE(cras_audio_handler_->has_alternative_input());
}

TEST_P(CrasAudioHandlerTest, PlugUSBMic_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);

  // Set up initial audio devices, only with internal mic.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalMic},
      /*expected_active_input_node=*/kInternalMic,
      /*expected_active_output_node=*/nullptr,
      /*expected_has_alternative_input=*/false,
      /*expected_has_alternative_output=*/std::nullopt);

  // Expect that there is no notification when initializing the audio nodes.
  EXPECT_EQ(0u, GetNotificationCount());

  // Plug the USB Mic.
  AudioNodeList audio_nodes;
  AudioNode internal_mic(GenerateAudioNode(kInternalMic));
  internal_mic.active = true;
  audio_nodes.push_back(internal_mic);
  audio_nodes.push_back(GenerateAudioNode(kUSBMic1));
  ChangeAudioNodes(audio_nodes);

  // Verify the AudioNodesChanged event is fired and new audio device is added.
  EXPECT_EQ(1, test_observer_->audio_nodes_changed_count());
  VerifySystemMonitorWasCalled();
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(2u, audio_devices.size());

  // Verify the active input device is not switched to USB mic and
  // and ActiveInputChanged event is not fired.
  EXPECT_EQ(0, test_observer_->active_input_node_changed_count());
  EXPECT_NE(kUSBMicId1, cras_audio_handler_->GetPrimaryActiveInputNode());
  EXPECT_TRUE(cras_audio_handler_->has_alternative_input());

  // Verify notification shows up for unseen new connected USB mic device.
  FastForwardBy(AudioSelectionNotificationHandler::kDebounceTime);
  EXPECT_EQ(1u, GetNotificationCount());
}

TEST_P(CrasAudioHandlerTest, UnplugUSBMic) {
  // Set up initial audio devices, with internal mic and USB Mic.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalMic, kUSBMic1},
      /*expected_active_input_node=*/kUSBMic1,
      /*expected_active_output_node=*/nullptr,
      /*expected_has_alternative_input=*/true,
      /*expected_has_alternative_output=*/std::nullopt);

  // Unplug the USB Mic.
  AudioNodeList audio_nodes;
  audio_nodes.push_back(GenerateAudioNode(kInternalMic));
  ChangeAudioNodes(audio_nodes);

  // Verify the AudioNodesChanged event is fired, and one audio device is
  // removed.
  EXPECT_EQ(1, test_observer_->audio_nodes_changed_count());
  VerifySystemMonitorWasCalled();
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(1u, audio_devices.size());

  // Verify the active input device is switched to internal mic, and
  // and ActiveInputChanged event is fired.
  EXPECT_EQ(1, test_observer_->active_input_node_changed_count());
  EXPECT_EQ(kInternalMic->id, cras_audio_handler_->GetPrimaryActiveInputNode());
  EXPECT_FALSE(cras_audio_handler_->has_alternative_input());
}

TEST_P(CrasAudioHandlerTest, PlugUSBMicNotAffectActiveOutput) {
  // Set up initial audio devices.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker, kHeadphone, kInternalMic},
      /*expected_active_input_node=*/kInternalMic,
      /*expected_active_output_node=*/kHeadphone,
      /*expected_has_alternative_input=*/false,
      /*expected_has_alternative_output=*/true);

  // Switch the active output to internal speaker.
  AudioDevice internal_speaker(GenerateAudioNode(kInternalSpeaker));
  cras_audio_handler_->SwitchToDevice(internal_speaker, true,
                                      DeviceActivateType::kActivateByUser);

  // Verify the active output is switched to internal speaker, and the
  // ActiveOutputNodeChanged event is fired.
  EXPECT_EQ(1, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kInternalSpeaker,
                     /*has_alternative_device=*/true);

  // Plug the USB Mic.
  AudioNodeList audio_nodes;
  AudioNode internal_speaker_node = GenerateAudioNode(kInternalSpeaker);
  internal_speaker_node.active = true;
  audio_nodes.push_back(internal_speaker_node);
  audio_nodes.push_back(GenerateAudioNode(kHeadphone));
  AudioNode internal_mic = GenerateAudioNode(kInternalMic);
  internal_mic.active = true;
  audio_nodes.push_back(internal_mic);
  audio_nodes.push_back(GenerateAudioNode(kUSBMic1));
  system_monitor_observer_.reset_count();
  ChangeAudioNodes(audio_nodes);
  LOG(INFO) << system_monitor_observer_.device_changes_received();

  // Verify the AudioNodesChanged event is fired, one new device is added.
  EXPECT_EQ(1, test_observer_->audio_nodes_changed_count());
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(4u, audio_devices.size());

  // Verify the active input device is switched to USB mic, and
  // and ActiveInputChanged event is fired.
  EXPECT_EQ(1, test_observer_->active_input_node_changed_count());
  EXPECT_EQ(kUSBMic1->id, cras_audio_handler_->GetPrimaryActiveInputNode());
  EXPECT_TRUE(cras_audio_handler_->has_alternative_input());

  // Verify the active output device is not changed.
  EXPECT_EQ(1, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kInternalSpeaker,
                     /*has_alternative_device=*/true);
}

TEST_P(CrasAudioHandlerTest,
       PlugUSBMicNotAffectActiveOutput_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);

  // Set up initial audio devices.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker, kHeadphone, kInternalMic},
      /*expected_active_input_node=*/kInternalMic,
      /*expected_active_output_node=*/kHeadphone,
      /*expected_has_alternative_input=*/false,
      /*expected_has_alternative_output=*/true);

  // Switch the active output to internal speaker.
  AudioDevice internal_speaker(GenerateAudioNode(kInternalSpeaker));
  cras_audio_handler_->SwitchToDevice(internal_speaker, true,
                                      DeviceActivateType::kActivateByUser);

  // Verify the active output is switched to internal speaker, and the
  // ActiveOutputNodeChanged event is fired.
  EXPECT_EQ(1, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kInternalSpeaker,
                     /*has_alternative_device=*/true);

  // Expect that there is no notification when initializing the audio nodes.
  EXPECT_EQ(0u, GetNotificationCount());

  // Plug the USB Mic.
  AudioNodeList audio_nodes;
  AudioNode internal_speaker_node = GenerateAudioNode(kInternalSpeaker);
  internal_speaker_node.active = true;
  audio_nodes.push_back(internal_speaker_node);
  audio_nodes.push_back(GenerateAudioNode(kHeadphone));
  AudioNode internal_mic = GenerateAudioNode(kInternalMic);
  internal_mic.active = true;
  audio_nodes.push_back(internal_mic);
  audio_nodes.push_back(GenerateAudioNode(kUSBMic1));
  system_monitor_observer_.reset_count();
  ChangeAudioNodes(audio_nodes);
  LOG(INFO) << system_monitor_observer_.device_changes_received();

  // Verify the AudioNodesChanged event is fired, one new device is added.
  EXPECT_EQ(1, test_observer_->audio_nodes_changed_count());
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(4u, audio_devices.size());

  // Verify the active input device is not switched to USB mic, and
  // and ActiveInputChanged event is not fired.
  EXPECT_EQ(0, test_observer_->active_input_node_changed_count());
  EXPECT_NE(kUSBMic1->id, cras_audio_handler_->GetPrimaryActiveInputNode());
  EXPECT_TRUE(cras_audio_handler_->has_alternative_input());

  // Verify notification shows up for unseen new connected USB mic device.
  FastForwardBy(AudioSelectionNotificationHandler::kDebounceTime);
  EXPECT_EQ(1u, GetNotificationCount());

  // Verify the active output device is not changed.
  EXPECT_EQ(1, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kInternalSpeaker,
                     /*has_alternative_device=*/true);
}

TEST_P(CrasAudioHandlerTest, PlugHeadphoneAutoUnplugSpeakerWithActiveUSB) {
  // Set up initial audio devices.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kUSBHeadphone1, kInternalSpeaker, kInternalMic},
      /*expected_active_input_node=*/kInternalMic,
      /*expected_active_output_node=*/kUSBHeadphone1,
      /*expected_has_alternative_input=*/false,
      /*expected_has_alternative_output=*/true);

  // Plug the headphone and auto-unplug internal speaker.
  AudioNodeList audio_nodes;
  AudioNode usb_headphone_node = GenerateAudioNode(kUSBHeadphone1);
  usb_headphone_node.active = true;
  audio_nodes.push_back(usb_headphone_node);
  AudioNode headphone_node = GenerateAudioNode(kHeadphone);
  headphone_node.plugged_time = 1000;
  audio_nodes.push_back(headphone_node);
  AudioNode internal_mic = GenerateAudioNode(kInternalMic);
  internal_mic.active = true;
  audio_nodes.push_back(internal_mic);
  ChangeAudioNodes(audio_nodes);

  // Verify the AudioNodesChanged event is fired, with nodes count unchanged.
  EXPECT_EQ(1, test_observer_->audio_nodes_changed_count());
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(3u, audio_devices.size());

  // Verify the active output device is switched to headphone, and
  // an ActiveOutputChanged event is fired.
  EXPECT_EQ(1, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false, /*expected_active_device=*/kHeadphone,
                     /*has_alternative_device=*/true);

  // Unplug the headphone and internal speaker auto-plugs back.
  audio_nodes.clear();
  audio_nodes.push_back(GenerateAudioNode(kUSBHeadphone1));
  AudioNode internal_speaker_node = GenerateAudioNode(kInternalSpeaker);
  internal_speaker_node.plugged_time = 2000;
  audio_nodes.push_back(internal_speaker_node);
  audio_nodes.push_back(internal_mic);
  ChangeAudioNodes(audio_nodes);

  // Verify the AudioNodesChanged event is fired, with nodes count unchanged.
  EXPECT_EQ(2, test_observer_->audio_nodes_changed_count());
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(3u, audio_devices.size());

  // Verify the active output device is switched back to USB, and
  // an ActiveOutputChanged event is fired.
  EXPECT_EQ(2, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kUSBHeadphone1,
                     /*has_alternative_device=*/true);

  // Verify the active input device is not changed.
  EXPECT_EQ(0, test_observer_->active_input_node_changed_count());
  ExpectActiveDevice(/*is_input=*/true, /*expected_active_device=*/kInternalMic,
                     /*has_alternative_device=*/false);
}

TEST_P(CrasAudioHandlerTest, PlugMicAutoUnplugInternalMicWithActiveUSB) {
  // Set up initial audio devices.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kUSBHeadphone1, kInternalSpeaker, kUSBMic1,
                         kInternalMic},
      /*expected_active_input_node=*/kUSBMic1,
      /*expected_active_output_node=*/kUSBHeadphone1,
      /*expected_has_alternative_input=*/true,
      /*expected_has_alternative_output=*/true);

  // Plug the headphone and mic, auto-unplug internal mic and speaker.
  AudioNodeList audio_nodes;
  AudioNode usb_headphone_node = GenerateAudioNode(kUSBHeadphone1);
  usb_headphone_node.active = true;
  audio_nodes.push_back(usb_headphone_node);
  AudioNode headphone_node = GenerateAudioNode(kHeadphone);
  headphone_node.plugged_time = 1000;
  audio_nodes.push_back(headphone_node);
  AudioNode usb_mic = GenerateAudioNode(kUSBMic1);
  usb_mic.active = true;
  audio_nodes.push_back(usb_mic);
  AudioNode mic_jack = GenerateAudioNode(kMicJack);
  mic_jack.plugged_time = 1000;
  audio_nodes.push_back(mic_jack);
  ChangeAudioNodes(audio_nodes);

  // Verify the AudioNodesChanged event is fired, with nodes count unchanged.
  EXPECT_EQ(1, test_observer_->audio_nodes_changed_count());
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(4u, audio_devices.size());

  // Verify the active output device is switched to headphone, and
  // an ActiveOutputChanged event is fired.
  EXPECT_EQ(1, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false, /*expected_active_device=*/kHeadphone,
                     /*has_alternative_device=*/true);

  // Verify the active input device is switched to mic jack, and
  // an ActiveInputChanged event is fired.
  EXPECT_EQ(1, test_observer_->active_input_node_changed_count());
  EXPECT_EQ(kMicJack->id, cras_audio_handler_->GetPrimaryActiveInputNode());
  EXPECT_TRUE(cras_audio_handler_->has_alternative_input());

  // Unplug the headphone and internal speaker auto-plugs back.
  audio_nodes.clear();
  audio_nodes.push_back(GenerateAudioNode(kUSBHeadphone1));
  AudioNode internal_speaker_node = GenerateAudioNode(kInternalSpeaker);
  internal_speaker_node.plugged_time = 2000;
  audio_nodes.push_back(internal_speaker_node);
  audio_nodes.push_back(GenerateAudioNode(kUSBMic1));
  AudioNode internal_mic = GenerateAudioNode(kInternalMic);
  internal_mic.plugged_time = 2000;
  audio_nodes.push_back(internal_mic);
  ChangeAudioNodes(audio_nodes);

  // Verify the AudioNodesChanged event is fired, with nodes count unchanged.
  EXPECT_EQ(2, test_observer_->audio_nodes_changed_count());
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(4u, audio_devices.size());

  // Verify the active output device is switched back to USB, and
  // an ActiveOutputChanged event is fired.
  EXPECT_EQ(2, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kUSBHeadphone1,
                     /*has_alternative_device=*/true);

  // Verify the active input device is switched back to USB mic, and
  // an ActiveInputChanged event is fired.
  EXPECT_EQ(2, test_observer_->active_input_node_changed_count());
  ExpectActiveDevice(/*is_input=*/true, /*expected_active_device=*/kUSBMic1,
                     /*has_alternative_device=*/true);
}

TEST_P(CrasAudioHandlerTest, MultipleNodesChangedSignalsOnPlugInHeadphone) {
  // Set up initial audio devices.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker, kBluetoothHeadset},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kBluetoothHeadset,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/true);

  // Plug in headphone, but fire NodesChanged signal twice.
  AudioNodeList audio_nodes;
  audio_nodes.push_back(GenerateAudioNode(kInternalSpeaker));
  AudioNode bluetooth_headset = GenerateAudioNode(kBluetoothHeadset);
  bluetooth_headset.plugged_time = 1000;
  bluetooth_headset.active = true;
  audio_nodes.push_back(bluetooth_headset);
  AudioNode headphone = GenerateAudioNode(kHeadphone);
  headphone.active = false;
  headphone.plugged_time = 2000;
  audio_nodes.push_back(headphone);
  ChangeAudioNodes(audio_nodes);
  ChangeAudioNodes(audio_nodes);

  // Verify the active output device is set to headphone.
  EXPECT_EQ(2, test_observer_->audio_nodes_changed_count());
  EXPECT_LE(1, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false, /*expected_active_device=*/kHeadphone,
                     /*has_alternative_device=*/true);

  // Verify the audio devices data is consistent, i.e., the active output device
  // should be headphone.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(3u, audio_devices.size());
  for (size_t i = 0; i < audio_devices.size(); ++i) {
    if (audio_devices[i].id == kInternalSpeaker->id) {
      EXPECT_FALSE(audio_devices[i].active);
    } else if (audio_devices[i].id == bluetooth_headset.id) {
      EXPECT_FALSE(audio_devices[i].active);
    } else if (audio_devices[i].id == headphone.id) {
      EXPECT_TRUE(audio_devices[i].active);
    } else {
      NOTREACHED_IN_MIGRATION();
    }
  }
}

TEST_P(CrasAudioHandlerTest, MultipleNodesChangedSignalsOnPlugInUSBMic) {
  // Set up initial audio devices.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalMic},
      /*expected_active_input_node=*/kInternalMic,
      /*expected_active_output_node=*/nullptr,
      /*expected_has_alternative_input=*/false,
      /*expected_has_alternative_output=*/std::nullopt);

  // Plug in usb mic, but fire NodesChanged signal twice.
  AudioNodeList audio_nodes;
  AudioNode internal_mic = GenerateAudioNode(kInternalMic);
  internal_mic.active = true;
  internal_mic.plugged_time = 1000;
  audio_nodes.push_back(internal_mic);
  AudioNode usb_mic = GenerateAudioNode(kUSBMic1);
  usb_mic.active = false;
  usb_mic.plugged_time = 2000;
  audio_nodes.push_back(usb_mic);
  ChangeAudioNodes(audio_nodes);
  ChangeAudioNodes(audio_nodes);

  // Verify the active output device is set to headphone.
  EXPECT_EQ(2, test_observer_->audio_nodes_changed_count());
  EXPECT_LE(1, test_observer_->active_input_node_changed_count());
  EXPECT_EQ(usb_mic.id, cras_audio_handler_->GetPrimaryActiveInputNode());
  EXPECT_TRUE(cras_audio_handler_->has_alternative_input());

  // Verify the audio devices data is consistent, i.e., the active input device
  // should be usb mic.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(2u, audio_devices.size());
  for (size_t i = 0; i < audio_devices.size(); ++i) {
    if (audio_devices[i].id == kInternalMic->id) {
      EXPECT_FALSE(audio_devices[i].active);
    } else if (audio_devices[i].id == usb_mic.id) {
      EXPECT_TRUE(audio_devices[i].active);
    } else {
      NOTREACHED_IN_MIGRATION();
    }
  }
}

TEST_P(
    CrasAudioHandlerTest,
    MultipleNodesChangedSignalsOnPlugInUSBMic_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);

  // Set up initial audio devices.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalMic},
      /*expected_active_input_node=*/kInternalMic,
      /*expected_active_output_node=*/nullptr,
      /*expected_has_alternative_input=*/false,
      /*expected_has_alternative_output=*/std::nullopt);

  // Plug in usb mic, but fire NodesChanged signal twice.
  AudioNodeList audio_nodes;
  AudioNode internal_mic = GenerateAudioNode(kInternalMic);
  internal_mic.active = true;
  internal_mic.plugged_time = 1000;
  audio_nodes.push_back(internal_mic);
  AudioNode usb_mic = GenerateAudioNode(kUSBMic1);
  usb_mic.active = false;
  usb_mic.plugged_time = 2000;
  audio_nodes.push_back(usb_mic);
  ChangeAudioNodes(audio_nodes);
  ChangeAudioNodes(audio_nodes);

  // Verify the active output device is not set to usb mic.
  EXPECT_EQ(2, test_observer_->audio_nodes_changed_count());
  EXPECT_EQ(0, test_observer_->active_input_node_changed_count());
  EXPECT_NE(usb_mic.id, cras_audio_handler_->GetPrimaryActiveInputNode());
  EXPECT_TRUE(cras_audio_handler_->has_alternative_input());

  // Verify the audio devices data is consistent, i.e., the active input device
  // should be internal mic.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(2u, audio_devices.size());
  for (size_t i = 0; i < audio_devices.size(); ++i) {
    if (audio_devices[i].id == kInternalMic->id) {
      EXPECT_TRUE(audio_devices[i].active);
    } else if (audio_devices[i].id == usb_mic.id) {
      EXPECT_FALSE(audio_devices[i].active);
    } else {
      NOTREACHED_IN_MIGRATION();
    }
  }
}

// This is the case of crbug.com/291303.
TEST_P(CrasAudioHandlerTest, MultipleNodesChangedSignalsOnSystemBoot) {
  // Set up audio handler with empty audio_nodes.
  AudioNodeList audio_nodes;
  SetUpCrasAudioHandler(audio_nodes);

  AudioNode internal_speaker = GenerateAudioNode(kInternalSpeaker);
  internal_speaker.active = false;
  AudioNode headphone = GenerateAudioNode(kHeadphone);
  headphone.active = false;
  AudioNode internal_mic = GenerateAudioNode(kInternalMic);
  internal_mic.active = false;
  audio_nodes.push_back(internal_speaker);
  audio_nodes.push_back(headphone);
  audio_nodes.push_back(internal_mic);
  const size_t init_nodes_size = audio_nodes.size();

  // Simulate AudioNodesChanged signal being fired twice during system boot.
  ChangeAudioNodes(audio_nodes);
  ChangeAudioNodes(audio_nodes);

  // Verify the active output device is set to headphone.
  EXPECT_EQ(2, test_observer_->audio_nodes_changed_count());
  EXPECT_LE(1, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false, /*expected_active_device=*/kHeadphone,
                     /*has_alternative_device=*/true);

  // Verify the active input device id is set to internal mic.
  EXPECT_EQ(internal_mic.id, cras_audio_handler_->GetPrimaryActiveInputNode());

  // Verify the audio devices data is consistent, i.e., the active output device
  // should be headphone, and the active input device should internal mic.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(init_nodes_size, audio_devices.size());
  for (size_t i = 0; i < audio_devices.size(); ++i) {
    if (audio_devices[i].id == internal_speaker.id) {
      EXPECT_FALSE(audio_devices[i].active);
    } else if (audio_devices[i].id == headphone.id) {
      EXPECT_TRUE(audio_devices[i].active);
    } else if (audio_devices[i].id == internal_mic.id) {
      EXPECT_TRUE(audio_devices[i].active);
    } else {
      NOTREACHED_IN_MIGRATION();
    }
  }
}

// This is the case of crbug.com/448924.
TEST_P(CrasAudioHandlerTest,
       TwoNodesChangedSignalsForLosingTowNodesOnOneUnplug) {
  // Set up audio handler with 4 audio_nodes.
  AudioNodeList audio_nodes;
  AudioNode internal_speaker = GenerateAudioNode(kInternalSpeaker);
  internal_speaker.active = false;
  AudioNode headphone = GenerateAudioNode(kHeadphone);
  headphone.active = false;
  AudioNode internal_mic = GenerateAudioNode(kInternalMic);
  internal_mic.active = false;
  AudioNode micJack = GenerateAudioNode(kMicJack);
  micJack.active = false;
  audio_nodes.push_back(internal_speaker);
  audio_nodes.push_back(headphone);
  audio_nodes.push_back(internal_mic);
  audio_nodes.push_back(micJack);
  SetUpCrasAudioHandler(audio_nodes);

  // Verify the audio devices size.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());

  // Verify the headphone has been selected as the active output.
  ExpectActiveDevice(/*is_input=*/false, /*expected_active_device=*/kHeadphone,
                     /*has_alternative_device=*/true);

  // Verify the mic Jack has been selected as the active input.
  EXPECT_EQ(micJack.id, cras_audio_handler_->GetPrimaryActiveInputNode());
  const AudioDevice* active_input = GetDeviceFromId(micJack.id);
  EXPECT_TRUE(active_input->active);
  EXPECT_TRUE(cras_audio_handler_->has_alternative_input());

  // Simulate the nodes list in first NodesChanged signal, only headphone is
  // removed, other nodes remains the same.
  AudioNodeList changed_nodes_1;
  internal_speaker.active = false;
  changed_nodes_1.push_back(internal_speaker);
  internal_mic.active = false;
  changed_nodes_1.push_back(internal_mic);
  micJack.active = true;
  changed_nodes_1.push_back(micJack);

  // Simulate the nodes list in second NodesChanged signal, the micJac is
  // removed, but the internal_mic is inactive, which does not reflect the
  // active status set from the first NodesChanged signal since this was sent
  // before cras receives the SetActiveOutputNode from the first NodesChanged
  // handling.
  AudioNodeList changed_nodes_2;
  changed_nodes_2.push_back(internal_speaker);
  changed_nodes_2.push_back(internal_mic);

  // Simulate AudioNodesChanged signal being fired twice for unplug an audio
  // device with both input and output nodes on it.
  ChangeAudioNodes(changed_nodes_1);
  ChangeAudioNodes(changed_nodes_2);

  AudioDeviceList changed_devices;
  cras_audio_handler_->GetAudioDevices(&changed_devices);
  EXPECT_EQ(2u, changed_devices.size());

  // Verify the active output device is set to internal speaker.
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kInternalSpeaker,
                     /*has_alternative_device=*/false);

  // Verify the active input device id is set to internal mic.
  EXPECT_EQ(internal_mic.id, cras_audio_handler_->GetPrimaryActiveInputNode());
  const AudioDevice* changed_active_input = GetDeviceFromId(internal_mic.id);
  EXPECT_TRUE(changed_active_input->active);
}

TEST_P(CrasAudioHandlerTest, SetOutputMonoEnabled) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({kHeadphone});
  SetUpCrasAudioHandler(audio_nodes);
  EXPECT_EQ(0, test_observer_->output_channel_remixing_changed_count());

  // Set output mono
  cras_audio_handler_->SetOutputMonoEnabled(true);

  // Verify the output is in mono mode, OnOuputChannelRemixingChanged event
  // is fired.
  EXPECT_TRUE(output_mono_enabled());
  EXPECT_EQ(1, test_observer_->output_channel_remixing_changed_count());

  // Set output stereo
  cras_audio_handler_->SetOutputMonoEnabled(false);
  EXPECT_FALSE(output_mono_enabled());
  EXPECT_EQ(2, test_observer_->output_channel_remixing_changed_count());
}

TEST_P(CrasAudioHandlerTest, SetOutputMute) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({kInternalSpeaker});
  SetUpCrasAudioHandler(audio_nodes);
  EXPECT_EQ(0, test_observer_->output_mute_changed_count());

  // Mute the device.
  cras_audio_handler_->SetOutputMute(true);

  // Verify the output is muted, OnOutputMuteChanged event is fired,
  // and mute value is saved in the preferences.
  EXPECT_TRUE(cras_audio_handler_->IsOutputMuted());
  EXPECT_EQ(1, test_observer_->output_mute_changed_count());
  AudioDevice speaker(GenerateAudioNode(kInternalSpeaker));
  EXPECT_TRUE(audio_pref_handler_->GetMuteValue(speaker));

  // Unmute the device.
  cras_audio_handler_->SetOutputMute(false);

  // Verify the output is unmuted, OnOutputMuteChanged event is fired,
  // and mute value is saved in the preferences.
  EXPECT_FALSE(cras_audio_handler_->IsOutputMuted());
  EXPECT_EQ(2, test_observer_->output_mute_changed_count());
  EXPECT_FALSE(audio_pref_handler_->GetMuteValue(speaker));
}

TEST_P(CrasAudioHandlerTest, SetOutputMuteWithSource) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({kInternalSpeaker});
  SetUpCrasAudioHandler(audio_nodes);
  histogram_tester_.ExpectBucketCount(
      CrasAudioHandler::kOutputVolumeMuteSourceHistogramName,
      CrasAudioHandler::AudioSettingsChangeSource::kSystemTray,
      /*expected_count=*/0);

  // Mute the device.
  cras_audio_handler_->SetOutputMute(
      true, CrasAudioHandler::AudioSettingsChangeSource::kSystemTray);

  // Verify mute source is recorded.
  histogram_tester_.ExpectBucketCount(
      CrasAudioHandler::kOutputVolumeMuteSourceHistogramName,
      CrasAudioHandler::AudioSettingsChangeSource::kSystemTray,
      /*expected_count=*/1);
}

TEST_P(CrasAudioHandlerTest, SetInputMute) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({kInternalMic});
  SetUpCrasAudioHandler(audio_nodes);
  EXPECT_EQ(0, test_observer_->input_mute_changed_count());

  // Mute the device.
  cras_audio_handler_->SetInputMute(
      true, CrasAudioHandler::InputMuteChangeMethod::kOther);

  // Verify the input is muted, OnInputMuteChanged event is fired.
  EXPECT_TRUE(cras_audio_handler_->IsInputMuted());
  EXPECT_EQ(1, test_observer_->input_mute_changed_count());

  // Unmute the device.
  cras_audio_handler_->SetInputMute(
      false, CrasAudioHandler::InputMuteChangeMethod::kOther);

  // Verify the input is unmuted, OnInputMuteChanged event is fired.
  EXPECT_FALSE(cras_audio_handler_->IsInputMuted());
  EXPECT_EQ(2, test_observer_->input_mute_changed_count());
}

TEST_P(CrasAudioHandlerTest, SetInputMuteWithSource) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({kInternalMic});
  SetUpCrasAudioHandler(audio_nodes);
  histogram_tester_.ExpectBucketCount(
      CrasAudioHandler::kInputGainMuteSourceHistogramName,
      CrasAudioHandler::AudioSettingsChangeSource::kSystemTray,
      /*expected_count=*/0);

  // Mute the device.
  cras_audio_handler_->SetInputMute(
      true, CrasAudioHandler::InputMuteChangeMethod::kOther,
      CrasAudioHandler::AudioSettingsChangeSource::kSystemTray);

  // Verify mute source is recorded.
  histogram_tester_.ExpectBucketCount(
      CrasAudioHandler::kInputGainMuteSourceHistogramName,
      CrasAudioHandler::AudioSettingsChangeSource::kSystemTray,
      /*expected_count=*/1);
}

TEST_P(CrasAudioHandlerTest, SetOutputVolumePercent) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({kInternalSpeaker});
  SetUpCrasAudioHandler(audio_nodes);
  EXPECT_EQ(0, test_observer_->output_volume_changed_count());

  const int kVolume = 60;
  cras_audio_handler_->SetOutputVolumePercent(kVolume);

  // Verify the output volume is changed to the designated value,
  // OnOutputNodeVolumeChanged event is fired, and the device volume value
  // is saved in the preferences.
  EXPECT_EQ(kVolume, cras_audio_handler_->GetOutputVolumePercent());
  EXPECT_EQ(1, test_observer_->output_volume_changed_count());
  AudioDevice device;
  EXPECT_TRUE(cras_audio_handler_->GetPrimaryActiveOutputDevice(&device));
  EXPECT_EQ(device.id, kInternalSpeaker->id);
  EXPECT_EQ(kVolume, audio_pref_handler_->GetOutputVolumeValue(&device));
}

TEST_P(CrasAudioHandlerTest, IncreaseOutputVolumeByOneStepOther) {
  AudioNodeList audio_nodes = GenerateAudioNodeList(
      {kInternalSpeaker, kHeadphone, kOther, kBluetoothHeadset, kHDMIOutput});
  SetUpCrasAudioHandler(audio_nodes);
  for (const auto& audio_node : audio_nodes) {
    cras_audio_handler_->ChangeActiveNodes({audio_node.id});
    cras_audio_handler_->SetOutputVolumePercent(50);
    cras_audio_handler_->IncreaseOutputVolumeByOneStep(kStepPercentage);
    EXPECT_EQ(50 + kStepPercentage,
              cras_audio_handler_->GetOutputVolumePercent());
  }
}

TEST_P(CrasAudioHandlerTest, IncreaseOutputVolumeByOneStepUSB) {
  AudioNodeList audio_nodes =
      GenerateAudioNodeList({kUSBHeadphone1, kUSBHeadphone2});
  AudioNode invalid_steps_auido_node = GenerateAudioNode(kUSBHeadphone3);
  invalid_steps_auido_node.number_of_volume_steps = 0;
  audio_nodes.push_back(invalid_steps_auido_node);
  SetUpCrasAudioHandler(audio_nodes);
  // USB 1 have 25 steps, mean we increase 100/25=4 % of volume per step.
  // USB 1 start from volume 0 and increase one step expect increase to 4.
  cras_audio_handler_->ChangeActiveNodes({kUSBHeadphone1->id});
  cras_audio_handler_->SetOutputVolumePercent(0);
  cras_audio_handler_->IncreaseOutputVolumeByOneStep(kStepPercentage);
  EXPECT_EQ(4, cras_audio_handler_->GetOutputVolumePercent());
  // 0   -> step0
  // 1-4 -> step1
  // 5-8 -> step2
  // Inorder to let user feel volume change, increase step1 to step2.
  // USB 1 start from volume 2 and increase one step, expect increase to 8.
  cras_audio_handler_->SetOutputVolumePercent(2);
  cras_audio_handler_->IncreaseOutputVolumeByOneStep(kStepPercentage);
  EXPECT_EQ(8, cras_audio_handler_->GetOutputVolumePercent());
  // 100 is max volume
  cras_audio_handler_->SetOutputVolumePercent(100);
  cras_audio_handler_->IncreaseOutputVolumeByOneStep(kStepPercentage);
  EXPECT_EQ(100, cras_audio_handler_->GetOutputVolumePercent());
  // can increase from 0 to 100
  cras_audio_handler_->SetOutputVolumePercent(0);
  for (int32_t i = 0; i < kUSBHeadphone1->number_of_volume_steps; ++i) {
    cras_audio_handler_->IncreaseOutputVolumeByOneStep(kStepPercentage);
  }
  EXPECT_EQ(100, cras_audio_handler_->GetOutputVolumePercent());
  // USB 2 have 16 steps, mean we increase 100/16=6.25 % of volume per step.
  // USB 2 start from volume 0 and increase one step expect increase to 6;
  cras_audio_handler_->ChangeActiveNodes({kUSBHeadphone2->id});
  cras_audio_handler_->SetOutputVolumePercent(0);
  cras_audio_handler_->IncreaseOutputVolumeByOneStep(kStepPercentage);
  EXPECT_EQ(6, cras_audio_handler_->GetOutputVolumePercent());
  // 0    -> step0
  // 1-6  -> step1
  // 7-12 -> step2
  // Inorder to let user feel volume change, increase step1 to step2.
  // USB 2 start from volume 4 and increase one step, expect increase to 12
  cras_audio_handler_->SetOutputVolumePercent(4);
  cras_audio_handler_->IncreaseOutputVolumeByOneStep(kStepPercentage);
  EXPECT_EQ(12, cras_audio_handler_->GetOutputVolumePercent());
  // USB 2 start from volume 0 and increase 4 step, expect increase to
  // 25(6.25*4=25)
  cras_audio_handler_->SetOutputVolumePercent(0);
  for (int32_t i = 0; i < 4; ++i) {
    cras_audio_handler_->IncreaseOutputVolumeByOneStep(kStepPercentage);
  }
  EXPECT_EQ(25, cras_audio_handler_->GetOutputVolumePercent());
  // 100 is max
  cras_audio_handler_->SetOutputVolumePercent(100);
  cras_audio_handler_->IncreaseOutputVolumeByOneStep(kStepPercentage);
  EXPECT_EQ(100, cras_audio_handler_->GetOutputVolumePercent());
  // can increase from 0 to 100
  cras_audio_handler_->SetOutputVolumePercent(0);
  for (uint32_t i = 0; i < kUSBHeadphone2->number_of_volume_steps; ++i) {
    cras_audio_handler_->IncreaseOutputVolumeByOneStep(kStepPercentage);
  }
  EXPECT_EQ(100, cras_audio_handler_->GetOutputVolumePercent());
  // USB 3 have 0 steps, this is invalid case, so we will fallback to use 25
  // steps. USB 3 start from volume 0 and increase one step expect increase
  // to 4.
  cras_audio_handler_->ChangeActiveNodes({kUSBHeadphone3->id});
  cras_audio_handler_->SetOutputVolumePercent(0);
  cras_audio_handler_->IncreaseOutputVolumeByOneStep(kStepPercentage);
  EXPECT_EQ(4, cras_audio_handler_->GetOutputVolumePercent());
  // 0   -> step0
  // 1-4 -> step1
  // 5-8 -> step2
  // Inorder to let user feel volume change, increase step1 to step2.
  // USB 1 start from volume 2 and increase one step, expect increase to 8.
  cras_audio_handler_->SetOutputVolumePercent(2);
  cras_audio_handler_->IncreaseOutputVolumeByOneStep(kStepPercentage);
  EXPECT_EQ(8, cras_audio_handler_->GetOutputVolumePercent());
  // 100 is max volume
  cras_audio_handler_->SetOutputVolumePercent(100);
  cras_audio_handler_->IncreaseOutputVolumeByOneStep(kStepPercentage);
  EXPECT_EQ(100, cras_audio_handler_->GetOutputVolumePercent());
  // can increase from 0 to 100
  cras_audio_handler_->SetOutputVolumePercent(0);
  for (int32_t i = 0; i < NUMBER_OF_VOLUME_STEPS_DEFAULT; ++i) {
    cras_audio_handler_->IncreaseOutputVolumeByOneStep(kStepPercentage);
  }
  EXPECT_EQ(100, cras_audio_handler_->GetOutputVolumePercent());
}

TEST_P(CrasAudioHandlerTest, DecreaseOutputVolumeByOneStepOther) {
  AudioNodeList audio_nodes = GenerateAudioNodeList(
      {kInternalSpeaker, kHeadphone, kOther, kBluetoothHeadset, kHDMIOutput});
  SetUpCrasAudioHandler(audio_nodes);
  for (const auto& audio_node : audio_nodes) {
    cras_audio_handler_->ChangeActiveNodes({audio_node.id});
    cras_audio_handler_->SetOutputVolumePercent(50);
    cras_audio_handler_->DecreaseOutputVolumeByOneStep(kStepPercentage);
    EXPECT_EQ(50 - kStepPercentage,
              cras_audio_handler_->GetOutputVolumePercent());
  }
}

TEST_P(CrasAudioHandlerTest, DecreaseOutputVolumeByOneStepUSB) {
  AudioNodeList audio_nodes =
      GenerateAudioNodeList({kUSBHeadphone1, kUSBHeadphone2});
  AudioNode invalid_steps_auido_node = GenerateAudioNode(kUSBHeadphone3);
  invalid_steps_auido_node.number_of_volume_steps = 0;
  audio_nodes.push_back(invalid_steps_auido_node);
  SetUpCrasAudioHandler(audio_nodes);
  // USB 1 have 25 steps, mean we decrease 100/25=4 % of volume per step.
  // USB 1 start from volume 4 and decrease one step expect decrease to 0;
  cras_audio_handler_->ChangeActiveNodes({kUSBHeadphone1->id});
  cras_audio_handler_->SetOutputVolumePercent(4);
  cras_audio_handler_->DecreaseOutputVolumeByOneStep(kStepPercentage);
  EXPECT_EQ(0, cras_audio_handler_->GetOutputVolumePercent());
  // 0   -> step0
  // 1-4 -> step1
  // 4-8 -> step2
  // Inorder to let user feel volume change, decrease step2 to step1.
  // USB 1 start from volume 6 and decrease one step, expect decrease to 4
  cras_audio_handler_->SetOutputVolumePercent(6);
  cras_audio_handler_->DecreaseOutputVolumeByOneStep(kStepPercentage);
  EXPECT_EQ(4, cras_audio_handler_->GetOutputVolumePercent());
  // 0 is min volume
  cras_audio_handler_->SetOutputVolumePercent(0);
  cras_audio_handler_->DecreaseOutputVolumeByOneStep(kStepPercentage);
  EXPECT_EQ(0, cras_audio_handler_->GetOutputVolumePercent());
  // can decrease from 100 to 0
  cras_audio_handler_->SetOutputVolumePercent(100);
  for (int32_t i = 0; i < kUSBHeadphone1->number_of_volume_steps; ++i) {
    cras_audio_handler_->DecreaseOutputVolumeByOneStep(kStepPercentage);
  }
  EXPECT_EQ(0, cras_audio_handler_->GetOutputVolumePercent());
  // USB 2 have 16 steps, mean we decrease 100/16=6.25 % of volume per step.
  // USB 2 start from volume 12 and decrease one step expect decrease to 6;
  cras_audio_handler_->ChangeActiveNodes({kUSBHeadphone2->id});
  cras_audio_handler_->SetOutputVolumePercent(12);
  cras_audio_handler_->DecreaseOutputVolumeByOneStep(kStepPercentage);
  EXPECT_EQ(6, cras_audio_handler_->GetOutputVolumePercent());
  // 0    -> step0
  // 1-6  -> step1
  // 7-12 -> step2
  // Inorder to let user feel volume change, decrease step2 to step1.
  // USB 2 start from volume 10 and decrease one step, expect decrease to 6
  cras_audio_handler_->SetOutputVolumePercent(10);
  cras_audio_handler_->DecreaseOutputVolumeByOneStep(kStepPercentage);
  EXPECT_EQ(6, cras_audio_handler_->GetOutputVolumePercent());
  // USB 2 start from volume 25 and decrease 4 step, expect decrease to 0
  cras_audio_handler_->SetOutputVolumePercent(25);
  for (int32_t i = 0; i < 4; ++i) {
    cras_audio_handler_->DecreaseOutputVolumeByOneStep(kStepPercentage);
  }
  EXPECT_EQ(0, cras_audio_handler_->GetOutputVolumePercent());
  // 0 is min volume
  cras_audio_handler_->SetOutputVolumePercent(0);
  cras_audio_handler_->DecreaseOutputVolumeByOneStep(kStepPercentage);
  EXPECT_EQ(0, cras_audio_handler_->GetOutputVolumePercent());
  // can decrease from 100 to 0
  cras_audio_handler_->SetOutputVolumePercent(100);
  for (int32_t i = 0; i < kUSBHeadphone2->number_of_volume_steps; ++i) {
    cras_audio_handler_->DecreaseOutputVolumeByOneStep(kStepPercentage);
  }
  EXPECT_EQ(0, cras_audio_handler_->GetOutputVolumePercent());
  // USB 3 have 0 steps, this is invalid case, so we will fallback to use 25
  // steps. USB 3 start from volume 4 and decrease one step expect decrease to
  // 0;
  cras_audio_handler_->ChangeActiveNodes({kUSBHeadphone3->id});
  cras_audio_handler_->SetOutputVolumePercent(4);
  cras_audio_handler_->DecreaseOutputVolumeByOneStep(kStepPercentage);
  EXPECT_EQ(0, cras_audio_handler_->GetOutputVolumePercent());
  // 0   -> step0
  // 1-4 -> step1
  // 4-8 -> step2
  // Inorder to let user feel volume change, decrease step2 to step1.
  // USB 3 start from volume 6 and decrease one step, expect decrease to 4
  cras_audio_handler_->SetOutputVolumePercent(6);
  cras_audio_handler_->DecreaseOutputVolumeByOneStep(kStepPercentage);
  EXPECT_EQ(4, cras_audio_handler_->GetOutputVolumePercent());
  // 0 is min volume
  cras_audio_handler_->SetOutputVolumePercent(0);
  cras_audio_handler_->DecreaseOutputVolumeByOneStep(kStepPercentage);
  EXPECT_EQ(0, cras_audio_handler_->GetOutputVolumePercent());
  // can decrease from 100 to 0
  cras_audio_handler_->SetOutputVolumePercent(100);
  for (int32_t i = 0; i < NUMBER_OF_VOLUME_STEPS_DEFAULT; ++i) {
    cras_audio_handler_->DecreaseOutputVolumeByOneStep(kStepPercentage);
  }
  EXPECT_EQ(0, cras_audio_handler_->GetOutputVolumePercent());
}

TEST_P(CrasAudioHandlerTest, AdjustOutputVolumeToAudibleLevelUSB) {
  AudioNodeList audio_nodes =
      GenerateAudioNodeList({kUSBHeadphone1, kUSBHeadphone2});
  SetUpCrasAudioHandler(audio_nodes);
  cras_audio_handler_->ChangeActiveNodes({kUSBHeadphone1->id});
  cras_audio_handler_->SetOutputVolumePercent(0);
  cras_audio_handler_->AdjustOutputVolumeToAudibleLevel();
  EXPECT_EQ(4, cras_audio_handler_->GetOutputVolumePercent());

  cras_audio_handler_->ChangeActiveNodes({kUSBHeadphone2->id});
  cras_audio_handler_->SetOutputVolumePercent(0);
  cras_audio_handler_->AdjustOutputVolumeToAudibleLevel();
  EXPECT_EQ(6, cras_audio_handler_->GetOutputVolumePercent());
  // If volume is not under audible level, don't change the volume.
  cras_audio_handler_->ChangeActiveNodes({kUSBHeadphone1->id});
  cras_audio_handler_->SetOutputVolumePercent(50);
  cras_audio_handler_->AdjustOutputVolumeToAudibleLevel();
  EXPECT_EQ(50, cras_audio_handler_->GetOutputVolumePercent());

  cras_audio_handler_->ChangeActiveNodes({kUSBHeadphone2->id});
  cras_audio_handler_->SetOutputVolumePercent(50);
  cras_audio_handler_->AdjustOutputVolumeToAudibleLevel();
  EXPECT_EQ(50, cras_audio_handler_->GetOutputVolumePercent());
}

TEST_P(CrasAudioHandlerTest, AdjustOutputVolumeToAudibleLevelOther) {
  AudioNodeList audio_nodes = GenerateAudioNodeList(
      {kInternalSpeaker, kHeadphone, kOther, kBluetoothHeadset, kHDMIOutput});
  SetUpCrasAudioHandler(audio_nodes);
  for (const auto& audio_node : audio_nodes) {
    cras_audio_handler_->ChangeActiveNodes({audio_node.id});
    cras_audio_handler_->SetOutputVolumePercent(0);
    cras_audio_handler_->AdjustOutputVolumeToAudibleLevel();
    EXPECT_EQ(kDefaultUnmuteVolumePercent,
              cras_audio_handler_->GetOutputVolumePercent());
  }
  // If volume is not under audible level, don't change the volume.
  for (const auto& audio_node : audio_nodes) {
    cras_audio_handler_->ChangeActiveNodes({audio_node.id});
    cras_audio_handler_->SetOutputVolumePercent(50);
    cras_audio_handler_->AdjustOutputVolumeToAudibleLevel();
    EXPECT_EQ(50, cras_audio_handler_->GetOutputVolumePercent());
  }
}

TEST_P(CrasAudioHandlerTest, RejectInvalidForOutputNodeVolumeChanged) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({kInternalSpeaker});
  SetUpCrasAudioHandler(audio_nodes);
  EXPECT_EQ(0, test_observer_->output_volume_changed_count());
  const std::vector<int> invalid_volumes = {-1, 101};

  for (const int kVolume : invalid_volumes) {
    fake_cras_audio_client()->NotifyOutputNodeVolumeChangedForTesting(
        kInternalSpeaker->id, kVolume);
    EXPECT_EQ(0, test_observer_->output_volume_changed_count());
  }
}

TEST_P(CrasAudioHandlerTest, RestartAudioClientWithCrasReady) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({kInternalSpeaker});
  SetUpCrasAudioHandler(audio_nodes);
  EXPECT_EQ(0, test_observer_->output_volume_changed_count());

  const int kDefaultVolume = cras_audio_handler_->GetOutputVolumePercent();
  // Disable the auto OutputNodeVolumeChanged signal.
  fake_cras_audio_client()->disable_volume_change_events();

  fake_cras_audio_client()->SetAudioNodesForTesting(audio_nodes);
  RestartAudioClient();
  EXPECT_EQ(0, test_observer_->output_volume_changed_count());
  EXPECT_EQ(kDefaultVolume, cras_audio_handler_->GetOutputVolumePercent());

  // The correct initialization OutputNodeVolumeChanged event is fired. We
  // should avoid notifying observers.
  fake_cras_audio_client()->NotifyOutputNodeVolumeChangedForTesting(
      kInternalSpeaker->id, kDefaultVolume);
  EXPECT_EQ(0, test_observer_->output_volume_changed_count());
  EXPECT_EQ(kDefaultVolume, cras_audio_handler_->GetOutputVolumePercent());

  // The later OutputNodeVolumeChanged event after initialization should notify
  // observers.
  const int kVolume = 60;
  fake_cras_audio_client()->NotifyOutputNodeVolumeChangedForTesting(
      kInternalSpeaker->id, kVolume);
  EXPECT_EQ(1, test_observer_->output_volume_changed_count());
  EXPECT_EQ(kVolume, cras_audio_handler_->GetOutputVolumePercent());
}

TEST_P(CrasAudioHandlerTest, RestartAudioClientWithCrasDropRequest) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({kInternalSpeaker});
  SetUpCrasAudioHandler(audio_nodes);
  EXPECT_EQ(0, test_observer_->output_volume_changed_count());

  const int kDefaultVolume = cras_audio_handler_->GetOutputVolumePercent();
  // Disable the auto OutputNodeVolumeChanged signal.
  fake_cras_audio_client()->disable_volume_change_events();

  fake_cras_audio_client()->SetAudioNodesForTesting(audio_nodes);
  RestartAudioClient();
  EXPECT_EQ(0, test_observer_->output_volume_changed_count());
  EXPECT_EQ(kDefaultVolume, cras_audio_handler_->GetOutputVolumePercent());

  // A wrong initialization OutputNodeVolumeChanged event is fired. This may
  // happen when Cras is not ready and drops request. The approach we use is
  // to log warning message, clear the pending automated volume change reasons,
  // and notify observers about this change.
  const int kVolume1 = 30;
  fake_cras_audio_client()->NotifyOutputNodeVolumeChangedForTesting(
      kInternalSpeaker->id, kVolume1);
  EXPECT_EQ(1, test_observer_->output_volume_changed_count());
  EXPECT_EQ(kVolume1, cras_audio_handler_->GetOutputVolumePercent());

  // The later OutputNodeVolumeChanged event should notify observers.
  const int kVolume2 = 60;
  fake_cras_audio_client()->NotifyOutputNodeVolumeChangedForTesting(
      kInternalSpeaker->id, kVolume2);
  EXPECT_EQ(2, test_observer_->output_volume_changed_count());
  EXPECT_EQ(kVolume2, cras_audio_handler_->GetOutputVolumePercent());
}

TEST_P(CrasAudioHandlerTest, SetOutputVolumeWithDelayedSignal) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({kInternalSpeaker});
  SetUpCrasAudioHandler(audio_nodes);
  EXPECT_EQ(0, test_observer_->output_volume_changed_count());

  const int kDefaultVolume = 75;
  EXPECT_EQ(kDefaultVolume, cras_audio_handler_->GetOutputVolumePercent());

  // Disable the auto OutputNodeVolumeChanged signal.
  fake_cras_audio_client()->disable_volume_change_events();

  // Verify the volume state is not changed before OutputNodeVolumeChanged
  // signal fires.
  const int kVolume = 60;
  cras_audio_handler_->SetOutputVolumePercent(kVolume);
  EXPECT_EQ(0, test_observer_->output_volume_changed_count());
  EXPECT_EQ(kDefaultVolume, cras_audio_handler_->GetOutputVolumePercent());

  // Verify the output volume is changed to the designated value after
  // OnOutputNodeVolumeChanged cras signal fires, and the volume change event
  // has been fired to notify the observers.
  fake_cras_audio_client()->NotifyOutputNodeVolumeChangedForTesting(
      kInternalSpeaker->id, kVolume);
  EXPECT_EQ(1, test_observer_->output_volume_changed_count());
  EXPECT_EQ(kVolume, cras_audio_handler_->GetOutputVolumePercent());
  AudioDevice device;
  EXPECT_TRUE(cras_audio_handler_->GetPrimaryActiveOutputDevice(&device));
  EXPECT_EQ(device.id, kInternalSpeaker->id);
  EXPECT_EQ(kVolume, audio_pref_handler_->GetOutputVolumeValue(&device));
}

TEST_P(CrasAudioHandlerTest,
       ChangeOutputVolumesWithDelayedSignalForSingleActiveDevice) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({kInternalSpeaker});
  SetUpCrasAudioHandler(audio_nodes);
  EXPECT_EQ(0, test_observer_->output_volume_changed_count());

  const int kDefaultVolume = 75;
  EXPECT_EQ(kDefaultVolume, cras_audio_handler_->GetOutputVolumePercent());

  // Disable the auto OutputNodeVolumeChanged signal.
  fake_cras_audio_client()->disable_volume_change_events();

  // Verify the volume state is not changed before OutputNodeVolumeChanged
  // signal fires.
  const int kVolume1 = 50;
  const int kVolume2 = 60;
  cras_audio_handler_->SetOutputVolumePercent(kVolume1);
  cras_audio_handler_->SetOutputVolumePercent(kVolume2);
  EXPECT_EQ(0, test_observer_->output_volume_changed_count());
  EXPECT_EQ(kDefaultVolume, cras_audio_handler_->GetOutputVolumePercent());

  // Simulate OutputNodeVolumeChanged signal fired with big latency that
  // it lags behind the SetOutputNodeVolume requests. Chrome sets the volume
  // to 50 then 60, but the volume changed signal for 50 comes back after
  // chrome sets the volume to 60. Verify chrome will sync to the designated
  // volume level after all signals arrive.
  fake_cras_audio_client()->NotifyOutputNodeVolumeChangedForTesting(
      kInternalSpeaker->id, kVolume1);
  EXPECT_EQ(1, test_observer_->output_volume_changed_count());
  EXPECT_EQ(kVolume1, cras_audio_handler_->GetOutputVolumePercent());

  fake_cras_audio_client()->NotifyOutputNodeVolumeChangedForTesting(
      kInternalSpeaker->id, kVolume2);
  EXPECT_EQ(2, test_observer_->output_volume_changed_count());
  EXPECT_EQ(kVolume2, cras_audio_handler_->GetOutputVolumePercent());
}

TEST_P(CrasAudioHandlerTest,
       ChangeOutputVolumeFromNonChromeSourceSingleActiveDevice) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({kInternalSpeaker});
  SetUpCrasAudioHandler(audio_nodes);
  EXPECT_EQ(0, test_observer_->output_volume_changed_count());

  const int kDefaultVolume = 75;
  EXPECT_EQ(0, test_observer_->output_volume_changed_count());
  EXPECT_EQ(kDefaultVolume, cras_audio_handler_->GetOutputVolumePercent());

  // Simulate OutputNodeVolumeChanged signal fired by a non-chrome source.
  // Verify chrome will sync its volume state to the volume from the signal,
  // and notify its observers for the volume change event.
  const int kVolume = 20;
  fake_cras_audio_client()->NotifyOutputNodeVolumeChangedForTesting(
      kInternalSpeaker->id, kVolume);
  EXPECT_EQ(1, test_observer_->output_volume_changed_count());
  EXPECT_EQ(kVolume, cras_audio_handler_->GetOutputVolumePercent());
  AudioDevice device;
  EXPECT_TRUE(cras_audio_handler_->GetPrimaryActiveOutputDevice(&device));
  EXPECT_EQ(device.id, kInternalSpeaker->id);
  EXPECT_EQ(kVolume, audio_pref_handler_->GetOutputVolumeValue(&device));
}

TEST_P(CrasAudioHandlerTest,
       ChangeOutputVolumeFromNonChromeSourceNonActiveDevice) {
  AudioNodeList audio_nodes =
      GenerateAudioNodeList({kInternalSpeaker, kHeadphone});
  SetupCrasAudioHandlerWithActiveNodeInPref(
      audio_nodes, audio_nodes,
      AudioDevice(GenerateAudioNode(kInternalSpeaker)), true);
  EXPECT_EQ(0, test_observer_->output_volume_changed_count());

  const AudioDevice* device = GetDeviceFromId(kHeadphone->id);
  const int kDefaultVolume = 75;
  EXPECT_EQ(0, test_observer_->output_volume_changed_count());
  EXPECT_EQ(kDefaultVolume, audio_pref_handler_->GetOutputVolumeValue(device));

  const int kVolume = 20;
  fake_cras_audio_client()->NotifyOutputNodeVolumeChangedForTesting(
      kHeadphone->id, kVolume);
  EXPECT_EQ(1, test_observer_->output_volume_changed_count());
  // Since the device is not active,
  EXPECT_EQ(kDefaultVolume, cras_audio_handler_->GetOutputVolumePercent());
  EXPECT_EQ(kVolume, audio_pref_handler_->GetOutputVolumeValue(device));
}

TEST_P(CrasAudioHandlerTest, SetInputGainPercent) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({kInternalMic});
  SetUpCrasAudioHandler(audio_nodes);
  EXPECT_EQ(0, test_observer_->input_gain_changed_count());

  cras_audio_handler_->SetInputGainPercent(60);

  // Verify the input gain changed to the designated value,
  // OnInputNodeGainChanged event is fired, and the device gain value
  // is saved in the preferences.
  const int kGain = 60;
  EXPECT_EQ(kGain, cras_audio_handler_->GetInputGainPercent());
  EXPECT_EQ(1, test_observer_->input_gain_changed_count());
  AudioDevice internal_mic(GenerateAudioNode(kInternalMic));
  EXPECT_EQ(kGain, audio_pref_handler_->GetInputGainValue(&internal_mic));
}

TEST_P(CrasAudioHandlerTest, SetInputGainWithDelayedSignal) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({kInternalMic});
  SetUpCrasAudioHandler(audio_nodes);
  EXPECT_EQ(0, test_observer_->input_gain_changed_count());

  const int kDefaultGain = cras_audio_handler_->GetInputGainPercent();

  // Disable the auto InputNodeGainChanged signal.
  fake_cras_audio_client()->disable_gain_change_events();

  // Verify the gain state is not changed before InputNodeGainChanged
  // signal fires.
  const int kGain = 60;
  cras_audio_handler_->SetInputGainPercent(kGain);
  EXPECT_EQ(0, test_observer_->input_gain_changed_count());
  EXPECT_EQ(kDefaultGain, cras_audio_handler_->GetInputGainPercent());

  // Verify the output gain is changed to the designated value after
  // OnInputNodeGainChanged cras signal fires, and the gain change event
  // has been fired to notify the observers.
  fake_cras_audio_client()->NotifyInputNodeGainChangedForTesting(
      kInternalMic->id, kGain);
  EXPECT_EQ(1, test_observer_->input_gain_changed_count());
  EXPECT_EQ(kGain, cras_audio_handler_->GetInputGainPercent());
  AudioDevice device;
  EXPECT_TRUE(cras_audio_handler_->GetPrimaryActiveInputDevice(&device));
  EXPECT_EQ(device.id, kInternalMic->id);
  EXPECT_EQ(kGain, audio_pref_handler_->GetInputGainValue(&device));
}

TEST_P(CrasAudioHandlerTest,
       ChangeInputGainsWithDelayedSignalForSingleActiveDevice) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({kInternalMic});
  SetUpCrasAudioHandler(audio_nodes);
  EXPECT_EQ(0, test_observer_->input_gain_changed_count());

  const int kDefaultGain = cras_audio_handler_->GetInputGainPercent();

  // Disable the auto InputNodeGainChanged signal.
  fake_cras_audio_client()->disable_gain_change_events();

  // Verify the gain state is not changed before InputNodeGainChanged
  // signal fires.
  EXPECT_EQ(kDefaultGain, cras_audio_handler_->GetInputGainPercent());
  const int kGain1 = 10;
  const int kGain2 = 90;
  cras_audio_handler_->SetInputGainPercent(kGain1);
  EXPECT_EQ(0, test_observer_->input_gain_changed_count());
  EXPECT_EQ(kDefaultGain, cras_audio_handler_->GetInputGainPercent());
  cras_audio_handler_->SetInputGainPercent(kGain2);
  EXPECT_EQ(0, test_observer_->input_gain_changed_count());
  EXPECT_EQ(kDefaultGain, cras_audio_handler_->GetInputGainPercent());

  // Simulate InputNodeGainChanged signal fired with big latency that
  // it lags behind the SetInputNodeGain requests. Chrome sets the gain
  // to 50 then 60, but the gain changed signal for 50 comes back after
  // chrome sets the gain to 60. Verify chrome will sync to the designated
  // gain level after all signals arrive.
  fake_cras_audio_client()->NotifyInputNodeGainChangedForTesting(
      kInternalMic->id, kGain1);
  EXPECT_EQ(1, test_observer_->input_gain_changed_count());
  EXPECT_EQ(kGain1, cras_audio_handler_->GetInputGainPercent());

  fake_cras_audio_client()->NotifyInputNodeGainChangedForTesting(
      kInternalMic->id, kGain2);
  EXPECT_EQ(2, test_observer_->input_gain_changed_count());
  EXPECT_EQ(kGain2, cras_audio_handler_->GetInputGainPercent());
}

TEST_P(CrasAudioHandlerTest,
       ChangeInputGainFromNonChromeSourceSingleActiveDevice) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({kInternalMic});
  SetUpCrasAudioHandler(audio_nodes);
  EXPECT_EQ(0, test_observer_->input_gain_changed_count());

  // Simulate InputNodeGainChanged signal fired by a non-chrome source.
  // Verify chrome will sync its gain state to the gain from the signal,
  // and notify its observers for the gain change event.
  const int kGain = 20;
  fake_cras_audio_client()->NotifyInputNodeGainChangedForTesting(
      kInternalMic->id, kGain);
  EXPECT_EQ(1, test_observer_->input_gain_changed_count());
  EXPECT_EQ(kGain, cras_audio_handler_->GetInputGainPercent());
  AudioDevice device;
  EXPECT_TRUE(cras_audio_handler_->GetPrimaryActiveInputDevice(&device));
  EXPECT_EQ(device.id, kInternalMic->id);
  EXPECT_EQ(kGain, audio_pref_handler_->GetInputGainValue(&device));
}

TEST_P(CrasAudioHandlerTest, SetMuteForDevice) {
  AudioNodeList audio_nodes = GenerateAudioNodeList(
      {kInternalSpeaker, kHeadphone, kInternalMic, kUSBMic1});
  SetUpCrasAudioHandler(audio_nodes);

  // Mute the active output device.
  ExpectActiveDevice(/*is_input=*/false, /*expected_active_device=*/kHeadphone,
                     /*has_alternative_device=*/true);
  cras_audio_handler_->SetMuteForDevice(kHeadphone->id, true);

  // Verify the headphone is muted and mute value is saved in the preferences.
  EXPECT_TRUE(cras_audio_handler_->IsOutputMutedForDevice(kHeadphone->id));
  AudioDevice headphone(GenerateAudioNode(kHeadphone));
  EXPECT_TRUE(audio_pref_handler_->GetMuteValue(headphone));

  // Mute the non-active output device.
  cras_audio_handler_->SetMuteForDevice(kInternalSpeaker->id, true);

  // Verify the internal speaker is muted and mute value is saved in the
  // preferences.
  EXPECT_TRUE(
      cras_audio_handler_->IsOutputMutedForDevice(kInternalSpeaker->id));
  AudioDevice internal_speaker(GenerateAudioNode(kInternalSpeaker));
  EXPECT_TRUE(audio_pref_handler_->GetMuteValue(internal_speaker));

  // Mute the active input device.
  EXPECT_EQ(kUSBMic1->id, cras_audio_handler_->GetPrimaryActiveInputNode());
  cras_audio_handler_->SetMuteForDevice(kUSBMic1->id, true);

  // Verify the USB Mic is muted.
  EXPECT_TRUE(cras_audio_handler_->IsInputMutedForDevice(kUSBMic1->id));

  // Mute the non-active input device should be a no-op, see crbug.com/365050.
  cras_audio_handler_->SetMuteForDevice(kInternalMic->id, true);

  // Verify IsInputMutedForDevice returns false for non-active input device.
  EXPECT_FALSE(cras_audio_handler_->IsInputMutedForDevice(kInternalMic->id));
}

TEST_P(CrasAudioHandlerTest, SetVolumeGainPercentForDevice) {
  AudioNodeList audio_nodes = GenerateAudioNodeList(
      {kInternalSpeaker, kHeadphone, kInternalMic, kUSBMic1});
  SetUpCrasAudioHandler(audio_nodes);

  // Set volume percent for active output device.
  const int kHeadphoneVolume = 30;
  ExpectActiveDevice(/*is_input=*/false, /*expected_active_device=*/kHeadphone,
                     /*has_alternative_device=*/true);
  cras_audio_handler_->SetVolumeGainPercentForDevice(kHeadphone->id,
                                                     kHeadphoneVolume);

  // Verify the volume percent of headphone is set, and saved in preferences.
  EXPECT_EQ(
      kHeadphoneVolume,
      cras_audio_handler_->GetOutputVolumePercentForDevice(kHeadphone->id));
  AudioDevice headphone(GenerateAudioNode(kHeadphone));
  EXPECT_EQ(kHeadphoneVolume,
            audio_pref_handler_->GetOutputVolumeValue(&headphone));

  // Set volume percent for non-active output device.
  const int kSpeakerVolume = 60;
  cras_audio_handler_->SetVolumeGainPercentForDevice(kInternalSpeaker->id,
                                                     kSpeakerVolume);

  // Verify the volume percent of speaker is set, and saved in preferences.
  EXPECT_EQ(kSpeakerVolume,
            cras_audio_handler_->GetOutputVolumePercentForDevice(
                kInternalSpeaker->id));
  AudioDevice speaker(GenerateAudioNode(kInternalSpeaker));
  EXPECT_EQ(kSpeakerVolume,
            audio_pref_handler_->GetOutputVolumeValue(&speaker));

  // Set gain percent for active input device.
  const int kUSBMicGain = 30;
  EXPECT_EQ(kUSBMic1->id, cras_audio_handler_->GetPrimaryActiveInputNode());
  cras_audio_handler_->SetVolumeGainPercentForDevice(kUSBMic1->id, kUSBMicGain);

  // Verify the gain percent of USB mic is set, and saved in preferences.
  EXPECT_EQ(kUSBMicGain,
            cras_audio_handler_->GetOutputVolumePercentForDevice(kUSBMic1->id));
  AudioDevice usb_mic(GenerateAudioNode(kHeadphone));
  EXPECT_EQ(kUSBMicGain, audio_pref_handler_->GetInputGainValue(&usb_mic));

  // Set gain percent for non-active input device.
  const int kInternalMicGain = 60;
  cras_audio_handler_->SetVolumeGainPercentForDevice(kInternalMic->id,
                                                     kInternalMicGain);

  // Verify the gain percent of internal mic is set, and saved in preferences.
  EXPECT_EQ(
      kInternalMicGain,
      cras_audio_handler_->GetOutputVolumePercentForDevice(kInternalMic->id));
  AudioDevice internal_mic(GenerateAudioNode(kInternalMic));
  EXPECT_EQ(kInternalMicGain,
            audio_pref_handler_->GetInputGainValue(&internal_mic));
}

TEST_P(CrasAudioHandlerTest, TreatDualInternalMicNotAsAlternativeDevice) {
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker, kFrontMic, kRearMic},
      /*expected_active_input_node=*/kFrontMic,
      /*expected_active_output_node=*/kInternalSpeaker,
      /*expected_has_alternative_input=*/false,
      /*expected_has_alternative_output=*/false);
}

TEST_P(CrasAudioHandlerTest, ActiveDeviceSelectionWithStableDeviceId) {
  AudioNodeList audio_nodes;
  AudioNode internal_speaker = GenerateAudioNode(kInternalSpeaker);
  audio_nodes.push_back(internal_speaker);
  AudioNode usb_headset = GenerateAudioNode(kUSBHeadphone1);
  usb_headset.plugged_time = 80000000;
  audio_nodes.push_back(usb_headset);
  SetUpCrasAudioHandler(audio_nodes);

  // Verify the audio devices size.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());

  // Initially active node is selected base on priority, so USB headphone
  // is selected.
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kUSBHeadphone1,
                     /*has_alternative_device=*/true);

  // Change the active device to internal speaker, now internal speaker has
  // higher preference priority than USB headphone.
  AudioDevice speaker(GenerateAudioNode(kInternalSpeaker));
  cras_audio_handler_->SwitchToDevice(speaker, true,
                                      DeviceActivateType::kActivateByUser);
  EXPECT_NE(kUSBHeadphone1->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());

  // Unplug USB headset.
  audio_nodes.clear();
  internal_speaker.active = true;
  audio_nodes.push_back(internal_speaker);
  ChangeAudioNodes(audio_nodes);
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kInternalSpeaker,
                     /*has_alternative_device=*/false);

  // Plug the same USB headset back, id is different, but stable_device_id
  // remains the same.
  usb_headset.active = false;
  usb_headset.id = 98765;
  audio_nodes.push_back(usb_headset);
  ChangeAudioNodes(audio_nodes);

  // Since internal speaker has higher preference priority than USB headphone,
  // it won't be selected as active after it's plugged in again.
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kInternalSpeaker,
                     /*has_alternative_device=*/true);

  // Plug the second USB headset.
  AudioNode usb_headset2 = GenerateAudioNode(kUSBHeadphone2);
  usb_headset2.plugged_time = 80000001;
  audio_nodes.push_back(usb_headset2);
  ChangeAudioNodes(audio_nodes);

  // Since the second USB device is new, it's selected as the active device
  // by its priority.
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kUSBHeadphone2,
                     /*has_alternative_device=*/true);

  // Unplug the second USB headset.
  audio_nodes.clear();
  internal_speaker.active = false;
  audio_nodes.push_back(internal_speaker);
  audio_nodes.push_back(usb_headset);
  ChangeAudioNodes(audio_nodes);

  // There is no active node after USB2 unplugged, the internal speaker got
  // selected by its preference priority.
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kInternalSpeaker,
                     /*has_alternative_device=*/true);

  audio_nodes.clear();
  internal_speaker.active = false;
  audio_nodes.push_back(internal_speaker);
  usb_headset.active = true;
  audio_nodes.push_back(usb_headset);
  usb_headset2.active = false;
  usb_headset2.plugged_time = 80000002;
  audio_nodes.push_back(usb_headset2);
  ChangeAudioNodes(audio_nodes);

  // Plug the second USB again. Since it was the active node before it got
  // unplugged, it is now selected as the active node.
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kUSBHeadphone2,
                     /*has_alternative_device=*/true);
}

TEST_P(
    CrasAudioHandlerTest,
    ActiveDeviceSelectionWithStableDeviceId_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);

  AudioNodeList audio_nodes;
  AudioNode internal_speaker = GenerateAudioNode(kInternalSpeaker);
  audio_nodes.push_back(internal_speaker);
  AudioNode usb_headset = GenerateAudioNode(kUSBHeadphone1);
  usb_headset.plugged_time = 80000000;
  audio_nodes.push_back(usb_headset);
  SetUpCrasAudioHandler(audio_nodes);

  // Verify the audio devices size.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());

  // The device set of kInternalSpeaker and kUSBHeadphone1 was un seen before.
  // No most recently active device list is available. Activate the
  // kInternalSpeaker first.
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kInternalSpeaker,
                     /*has_alternative_device=*/true);

  // Change the active device to kInternalSpeaker, now kInternalSpeaker is the
  // preferred device in the device set of kInternalSpeaker and kUSBHeadphone1.
  AudioDevice speaker(GenerateAudioNode(kInternalSpeaker));
  cras_audio_handler_->SwitchToDevice(speaker, true,
                                      DeviceActivateType::kActivateByUser);
  EXPECT_NE(kUSBHeadphone1->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());

  // Unplug USB headset.
  audio_nodes.clear();
  internal_speaker.active = true;
  audio_nodes.push_back(internal_speaker);
  ChangeAudioNodes(audio_nodes);
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kInternalSpeaker,
                     /*has_alternative_device=*/false);

  // Plug the same USB headset back, id is different, but stable_device_id
  // remains the same.
  usb_headset.active = false;
  usb_headset.id = 98765;
  audio_nodes.push_back(usb_headset);
  ChangeAudioNodes(audio_nodes);

  // Since kInternalSpeaker is the preferred device,
  // kUSBHeadphone1 won't be selected as active after it's plugged in again.
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kInternalSpeaker,
                     /*has_alternative_device=*/true);

  // Plug the second USB headset.
  AudioNode usb_headset2 = GenerateAudioNode(kUSBHeadphone2);
  usb_headset2.plugged_time = 80000001;
  audio_nodes.push_back(usb_headset2);
  ChangeAudioNodes(audio_nodes);

  // Since the second USB device is new, it's not selected as the active device.
  EXPECT_NE(kUSBHeadphone2->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());

  // Change the active device to the second USB device, now the second USB
  // device has higher preference priority.
  AudioDevice usb_headset2_device(GenerateAudioNode(kUSBHeadphone2));
  cras_audio_handler_->SwitchToDevice(usb_headset2_device, true,
                                      DeviceActivateType::kActivateByUser);
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kUSBHeadphone2,
                     /*has_alternative_device=*/true);

  // Unplug the second USB headset.
  audio_nodes.clear();
  internal_speaker.active = false;
  audio_nodes.push_back(internal_speaker);
  audio_nodes.push_back(usb_headset);
  ChangeAudioNodes(audio_nodes);

  // The internal speaker gets selected based on previous priority.
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kInternalSpeaker,
                     /*has_alternative_device=*/true);

  audio_nodes.clear();
  internal_speaker.active = false;
  audio_nodes.push_back(internal_speaker);
  usb_headset.active = true;
  audio_nodes.push_back(usb_headset);
  usb_headset2.active = false;
  usb_headset2.plugged_time = 80000002;
  audio_nodes.push_back(usb_headset2);
  ChangeAudioNodes(audio_nodes);

  // Plug the second USB again. Since it was the active node before it got
  // unplugged, it is now selected as the active node.
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kUSBHeadphone2,
                     /*has_alternative_device=*/true);
}

// Test the device new session case, either via reboot or logout, if there
// is an active device in the previous session, that device should still
// be set as active after the new session starts.
TEST_P(CrasAudioHandlerTest, PersistActiveDeviceAcrossSession) {
  // Set the active device to internal speaker before the session starts.
  AudioNodeList audio_nodes =
      GenerateAudioNodeList({kInternalSpeaker, kHeadphone});
  SetupCrasAudioHandlerWithActiveNodeInPref(
      audio_nodes, audio_nodes,
      AudioDevice(GenerateAudioNode(kInternalSpeaker)), true);

  // Verify the audio devices size.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());

  // Verify the active device is the internal speaker, which is of a lower
  // priority, but selected as active since it was the active device previously.
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kInternalSpeaker,
                     /*has_alternative_device=*/true);
}

TEST_P(CrasAudioHandlerTest, PersistActiveSpeakerAcrossReboot) {
  // Simulates the device was shut down with three audio devices, and
  // internal speaker being the active one selected by user.
  AudioNodeList audio_nodes_in_pref =
      GenerateAudioNodeList({kInternalSpeaker, kHeadphone, kUSBHeadphone1});

  // Simulate the first NodesChanged signal coming with only one node.
  AudioNodeList audio_nodes = GenerateAudioNodeList({kUSBHeadphone1});

  SetupCrasAudioHandlerWithActiveNodeInPref(
      audio_nodes, audio_nodes_in_pref,
      AudioDevice(GenerateAudioNode(kInternalSpeaker)), true);

  // Verify the usb headphone has been made active.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kUSBHeadphone1,
                     /*has_alternative_device=*/false);

  // Simulate another NodesChanged signal coming later with all ndoes.
  audio_nodes.push_back(GenerateAudioNode(kInternalSpeaker));
  audio_nodes.push_back(GenerateAudioNode(kHeadphone));
  ChangeAudioNodes(audio_nodes);

  // Verify the active output has been restored to internal speaker.
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kInternalSpeaker,
                     /*has_alternative_device=*/true);
}

// crbug.com/698809. User plug in USB speaker, then unplug it, leave
// internal speaker as active device. Power down, plug in USB speaker again.
// When the device powers up again, the first NodesChanged signal comes with
// only USB speaker; followed by another NodesChanged signal with internal
// speaker added.
TEST_P(CrasAudioHandlerTest, USBShouldBeActiveAfterReboot) {
  // Start with both interanl speaker and USB speaker.
  AudioNodeList audio_nodes =
      GenerateAudioNodeList({kInternalSpeaker, kUSBHeadphone1});

  SetUpCrasAudioHandler(audio_nodes);

  // Verify the usb headphone has been made active by priority.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());
  EXPECT_EQ(kUSBHeadphone1->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());

  // Remove USB headphone.
  audio_nodes.clear();
  audio_nodes.push_back(GenerateAudioNode(kInternalSpeaker));
  ChangeAudioNodes(audio_nodes);
  // Verify the internal speaker becomes the active device by priority.
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());
  EXPECT_EQ(kInternalSpeaker->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());

  // Simulate after power off, plug in usb header phone, then power on.
  // The first NodesChanged signal sends usb headphone only.
  audio_nodes.clear();
  audio_nodes.push_back(GenerateAudioNode(kUSBHeadphone1));
  ChangeAudioNodes(audio_nodes);
  // Verify the usb headerphone becomes the active device by priority.
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());
  EXPECT_EQ(kUSBHeadphone1->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());

  // Simulate the second NodesChanged signal comes with internal speaker added.
  audio_nodes.clear();
  AudioNode usb_headphone = GenerateAudioNode(kUSBHeadphone1);
  usb_headphone.active = true;
  audio_nodes.push_back(usb_headphone);
  audio_nodes.push_back(GenerateAudioNode(kInternalSpeaker));
  ChangeAudioNodes(audio_nodes);
  // Verify the usb headerphone is still the active device.
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());
  EXPECT_EQ(kUSBHeadphone1->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());
}

// Test the corner case that headphone is plugged in for the first time on
// a cros device after the device is shutdown.
// crbug.com/622045.
TEST_P(CrasAudioHandlerTest, PlugInHeadphoneFirstTimeAfterPowerDown) {
  // Simulate plugging headphone for the first on a cros device after it is
  // powered down. Internal speaker is set up in audio prefs as active
  // before the new cros session starts.
  AudioNodeList audio_nodes_in_pref = GenerateAudioNodeList({kInternalSpeaker});

  AudioNodeList audio_nodes =
      GenerateAudioNodeList({kInternalSpeaker, kHeadphone});

  SetupCrasAudioHandlerWithActiveNodeInPref(
      audio_nodes, audio_nodes_in_pref,
      AudioDevice(GenerateAudioNode(kInternalSpeaker)), false);

  // Verify the audio devices size.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());

  // Verify headphone becomes the active output.
  EXPECT_EQ(kHeadphone->id, cras_audio_handler_->GetPrimaryActiveOutputNode());
}

TEST_P(CrasAudioHandlerTest,
       PersistActiveUsbHeadphoneAcrossRebootUsbComeFirst) {
  // Simulates the device was shut down with three audio devices, and
  // usb headphone being the active one selected by priority.
  AudioNodeList audio_nodes_in_pref =
      GenerateAudioNodeList({kInternalSpeaker, kHeadphone, kUSBHeadphone1});

  // Simulate the first NodesChanged signal coming with only internal speaker
  // and the USB headphone.
  AudioNodeList audio_nodes =
      GenerateAudioNodeList({kInternalSpeaker, kUSBHeadphone1});

  SetupCrasAudioHandlerWithActiveNodeInPref(
      audio_nodes, audio_nodes_in_pref,
      AudioDevice(GenerateAudioNode(kUSBHeadphone1)), false);

  // Verify the USB headphone has been made active.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());
  EXPECT_EQ(kUSBHeadphone1->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());

  // Simulate another NodesChanged signal coming later with all ndoes.
  AudioNode headphone_node = GenerateAudioNode(kHeadphone);
  headphone_node.plugged_time = 80000000;
  audio_nodes.push_back(headphone_node);
  ChangeAudioNodes(audio_nodes);

  // Verify the active output has been restored to USB headphone.
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());
  EXPECT_EQ(kUSBHeadphone1->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());
}

// This covers the crbug.com/586026. Cras lost the active state of the internal
// speaker when user unplugs the headphone, which is a bug in cras. However,
// chrome code is still resilient and set the internal speaker back to active.
TEST_P(CrasAudioHandlerTest, UnplugHeadphoneLostActiveInternalSpeakerByCras) {
  // Set up with three nodes.
  AudioNodeList audio_nodes =
      GenerateAudioNodeList({kInternalSpeaker, kHeadphone, kUSBHeadphone1});
  SetUpCrasAudioHandler(audio_nodes);

  // Switch the active output to internal speaker.
  cras_audio_handler_->SwitchToDevice(
      AudioDevice(GenerateAudioNode(kInternalSpeaker)), true,
      DeviceActivateType::kActivateByUser);

  // Verify internal speaker has been made active.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());
  EXPECT_EQ(kInternalSpeaker->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());

  // Simulate unplug the headphone. Cras sends NodesChanged signal with
  // both internal speaker and usb headphone being inactive.
  audio_nodes.clear();
  audio_nodes.push_back(GenerateAudioNode(kInternalSpeaker));
  audio_nodes.push_back(GenerateAudioNode(kUSBHeadphone1));
  for (const auto& node : audio_nodes) {
    ASSERT_FALSE(node.active) << node.id << " expexted to be inactive";
  }
  ChangeAudioNodes(audio_nodes);

  // Verify the active output is set back to internal speaker.
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());
  EXPECT_EQ(kInternalSpeaker->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());
}

TEST_P(CrasAudioHandlerTest, RemoveNonActiveDevice) {
  // Set up with three nodes.
  AudioNodeList audio_nodes =
      GenerateAudioNodeList({kInternalSpeaker, kHeadphone, kUSBHeadphone1});
  SetUpCrasAudioHandler(audio_nodes);

  // Switch the active output to internal speaker.
  cras_audio_handler_->SwitchToDevice(
      AudioDevice(GenerateAudioNode(kInternalSpeaker)), true,
      DeviceActivateType::kActivateByUser);

  // Verify internal speaker has been made active.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());
  EXPECT_EQ(kInternalSpeaker->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());

  // Remove headphone, which is an non-active device.
  audio_nodes.clear();
  AudioNode speaker = GenerateAudioNode(kInternalSpeaker);
  speaker.active = true;
  audio_nodes.push_back(speaker);
  AudioNode usb_headphone = GenerateAudioNode(kUSBHeadphone1);
  usb_headphone.active = false;
  audio_nodes.push_back(usb_headphone);

  ChangeAudioNodes(audio_nodes);

  // Verify the active output remains as internal speaker.
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());
  EXPECT_EQ(kInternalSpeaker->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());
}

TEST_P(CrasAudioHandlerTest, ChangeActiveNodesHotrodInit) {
  // This simulates a typical hotrod audio device configuration.
  // Verify only the 1st jabra speaker's output and input are selected as active
  // nodes by CrasAudioHandler.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kHDMIOutput, kUSBJabraSpeakerOutput1,
                         kUSBJabraSpeakerOutput2, kUSBJabraSpeakerInput1,
                         kUSBJabraSpeakerInput2, kUSBCameraInput},
      /*expected_active_input_node=*/kUSBJabraSpeakerInput1,
      /*expected_active_output_node=*/kUSBJabraSpeakerOutput1,
      /*expected_has_alternative_input=*/true,
      /*expected_has_alternative_output=*/true);

  AudioDevice active_output;
  EXPECT_TRUE(
      cras_audio_handler_->GetPrimaryActiveOutputDevice(&active_output));
  EXPECT_EQ(2, GetActiveDeviceCount());

  // Set both jabra speakers's input and output nodes to active, this simulate
  // the call sent by hotrod initialization process.
  test_observer_->reset_active_output_node_changed_count();
  test_observer_->reset_active_input_node_changed_count();
  cras_audio_handler_->ChangeActiveNodes(
      {kUSBJabraSpeakerOutput1->id, kUSBJabraSpeakerOutput2->id,
       kUSBJabraSpeakerInput1->id, kUSBJabraSpeakerInput2->id});

  // Verify both jabra speakers' input/output nodes are made active.
  // num_active_nodes = GetActiveDeviceCount();
  EXPECT_EQ(4, GetActiveDeviceCount());
  const AudioDevice* active_output_1 =
      GetDeviceFromId(kUSBJabraSpeakerOutput1->id);
  EXPECT_TRUE(active_output_1->active);
  const AudioDevice* active_output_2 =
      GetDeviceFromId(kUSBJabraSpeakerOutput2->id);
  EXPECT_TRUE(active_output_2->active);
  AudioDevice primary_active_device;
  EXPECT_TRUE(cras_audio_handler_->GetPrimaryActiveOutputDevice(
      &primary_active_device));
  EXPECT_EQ(kUSBJabraSpeakerOutput1->id, primary_active_device.id);
  const AudioDevice* active_input_1 =
      GetDeviceFromId(kUSBJabraSpeakerInput1->id);
  EXPECT_TRUE(active_input_1->active);
  const AudioDevice* active_input_2 =
      GetDeviceFromId(kUSBJabraSpeakerInput2->id);
  EXPECT_TRUE(active_input_2->active);
  EXPECT_EQ(kUSBJabraSpeakerInput1->id,
            cras_audio_handler_->GetPrimaryActiveInputNode());

  // Verify only 1 ActiveOutputNodeChanged notification has been sent out
  // by calling ChangeActiveNodes.
  EXPECT_EQ(1, test_observer_->active_output_node_changed_count());
  EXPECT_EQ(1, test_observer_->active_input_node_changed_count());

  // Verify all active devices are the not muted and their volume values are
  // the same.
  EXPECT_FALSE(cras_audio_handler_->IsOutputMuted());
  EXPECT_FALSE(
      cras_audio_handler_->IsOutputMutedForDevice(kUSBJabraSpeakerOutput1->id));
  EXPECT_FALSE(
      cras_audio_handler_->IsOutputMutedForDevice(kUSBJabraSpeakerOutput2->id));
  EXPECT_EQ(cras_audio_handler_->GetOutputVolumePercent(),
            cras_audio_handler_->GetOutputVolumePercentForDevice(
                kUSBJabraSpeakerOutput1->id));
  EXPECT_EQ(cras_audio_handler_->GetOutputVolumePercent(),
            cras_audio_handler_->GetOutputVolumePercentForDevice(
                kUSBJabraSpeakerOutput2->id));

  // Adjust the volume of output devices, verify all active nodes are set to
  // the same volume.
  cras_audio_handler_->SetOutputVolumePercent(25);
  EXPECT_EQ(25, cras_audio_handler_->GetOutputVolumePercent());
  EXPECT_EQ(25, cras_audio_handler_->GetOutputVolumePercentForDevice(
                    kUSBJabraSpeakerOutput1->id));
  EXPECT_EQ(25, cras_audio_handler_->GetOutputVolumePercentForDevice(
                    kUSBJabraSpeakerOutput2->id));
}

TEST_P(CrasAudioHandlerTest, SetActiveNodesHotrodInit) {
  // This simulates a typical hotrod audio device configuration.
  // Verify only the 1st jabra speaker's output and input are selected as active
  // nodes by CrasAudioHandler.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kHDMIOutput, kUSBJabraSpeakerOutput1,
                         kUSBJabraSpeakerOutput2, kUSBJabraSpeakerInput1,
                         kUSBJabraSpeakerInput2, kUSBCameraInput},
      /*expected_active_input_node=*/kUSBJabraSpeakerInput1,
      /*expected_active_output_node=*/kUSBJabraSpeakerOutput1,
      /*expected_has_alternative_input=*/true,
      /*expected_has_alternative_output=*/true);

  // Set both jabra speakers's input and output nodes to active, this simulate
  // the call sent by hotrod initialization process.
  test_observer_->reset_active_output_node_changed_count();
  test_observer_->reset_active_input_node_changed_count();

  cras_audio_handler_->SetActiveInputNodes(
      {kUSBJabraSpeakerInput1->id, kUSBJabraSpeakerInput2->id});

  cras_audio_handler_->SetActiveOutputNodes(
      {kUSBJabraSpeakerOutput1->id, kUSBJabraSpeakerOutput2->id});

  // Verify both jabra speakers' input/output nodes are made active.
  // num_active_nodes = GetActiveDeviceCount();
  EXPECT_EQ(4, GetActiveDeviceCount());
  const AudioDevice* active_output_1 =
      GetDeviceFromId(kUSBJabraSpeakerOutput1->id);
  EXPECT_TRUE(active_output_1->active);
  const AudioDevice* active_output_2 =
      GetDeviceFromId(kUSBJabraSpeakerOutput2->id);
  EXPECT_TRUE(active_output_2->active);
  AudioDevice primary_active_device;
  EXPECT_TRUE(cras_audio_handler_->GetPrimaryActiveOutputDevice(
      &primary_active_device));
  EXPECT_EQ(kUSBJabraSpeakerOutput1->id, primary_active_device.id);
  const AudioDevice* active_input_1 =
      GetDeviceFromId(kUSBJabraSpeakerInput1->id);
  EXPECT_TRUE(active_input_1->active);
  const AudioDevice* active_input_2 =
      GetDeviceFromId(kUSBJabraSpeakerInput2->id);
  EXPECT_TRUE(active_input_2->active);
  EXPECT_EQ(kUSBJabraSpeakerInput1->id,
            cras_audio_handler_->GetPrimaryActiveInputNode());

  // Verify only 1 ActiveOutputNodeChanged notification has been sent out
  // by calling SetActiveNodes.
  EXPECT_EQ(1, test_observer_->active_output_node_changed_count());
  EXPECT_EQ(1, test_observer_->active_input_node_changed_count());

  // Verify all active devices are the not muted and their volume values are
  // the same.
  EXPECT_FALSE(cras_audio_handler_->IsOutputMuted());
  EXPECT_FALSE(
      cras_audio_handler_->IsOutputMutedForDevice(kUSBJabraSpeakerOutput1->id));
  EXPECT_FALSE(
      cras_audio_handler_->IsOutputMutedForDevice(kUSBJabraSpeakerOutput2->id));
  EXPECT_EQ(cras_audio_handler_->GetOutputVolumePercent(),
            cras_audio_handler_->GetOutputVolumePercentForDevice(
                kUSBJabraSpeakerOutput1->id));
  EXPECT_EQ(cras_audio_handler_->GetOutputVolumePercent(),
            cras_audio_handler_->GetOutputVolumePercentForDevice(
                kUSBJabraSpeakerOutput2->id));

  // Adjust the volume of output devices, verify all active nodes are set to
  // the same volume.
  cras_audio_handler_->SetOutputVolumePercent(25);
  EXPECT_EQ(25, cras_audio_handler_->GetOutputVolumePercent());
  EXPECT_EQ(25, cras_audio_handler_->GetOutputVolumePercentForDevice(
                    kUSBJabraSpeakerOutput1->id));
  EXPECT_EQ(25, cras_audio_handler_->GetOutputVolumePercentForDevice(
                    kUSBJabraSpeakerOutput2->id));
}

TEST_P(CrasAudioHandlerTest, ChangeVolumeHotrodDualSpeakersWithDelayedSignals) {
  AudioNodeList audio_nodes = GenerateAudioNodeList(
      {kHDMIOutput, kUSBJabraSpeakerOutput1, kUSBJabraSpeakerOutput2});
  SetUpCrasAudioHandler(audio_nodes);

  // Verify the audio devices size.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());

  // Set both jabra speakers nodes to active, this simulate
  // the call sent by hotrod initialization process.
  test_observer_->reset_active_output_node_changed_count();
  cras_audio_handler_->ChangeActiveNodes(
      {kUSBJabraSpeakerOutput1->id, kUSBJabraSpeakerOutput2->id});

  // Verify both jabra speakers are made active.
  EXPECT_EQ(2, GetActiveDeviceCount());
  const AudioDevice* active_output_1 =
      GetDeviceFromId(kUSBJabraSpeakerOutput1->id);
  EXPECT_TRUE(active_output_1->active);
  const AudioDevice* active_output_2 =
      GetDeviceFromId(kUSBJabraSpeakerOutput2->id);
  EXPECT_TRUE(active_output_2->active);

  // Verify all active devices are the not muted and their volume values are
  // the same.
  EXPECT_FALSE(cras_audio_handler_->IsOutputMuted());
  EXPECT_FALSE(
      cras_audio_handler_->IsOutputMutedForDevice(kUSBJabraSpeakerOutput1->id));
  EXPECT_FALSE(
      cras_audio_handler_->IsOutputMutedForDevice(kUSBJabraSpeakerOutput2->id));
  EXPECT_EQ(cras_audio_handler_->GetOutputVolumePercent(),
            cras_audio_handler_->GetOutputVolumePercentForDevice(
                kUSBJabraSpeakerOutput1->id));
  EXPECT_EQ(cras_audio_handler_->GetOutputVolumePercent(),
            cras_audio_handler_->GetOutputVolumePercentForDevice(
                kUSBJabraSpeakerOutput2->id));
  const int kDefaultVolume = 75;
  EXPECT_EQ(kDefaultVolume, cras_audio_handler_->GetOutputVolumePercent());

  // Disable the auto OutputNodeVolumeChanged signal.
  fake_cras_audio_client()->disable_volume_change_events();
  test_observer_->reset_output_volume_changed_count();

  // Adjust the volume of output devices continuously.
  cras_audio_handler_->SetOutputVolumePercent(20);
  cras_audio_handler_->SetOutputVolumePercent(30);

  // Sends delayed OutputNodeVolumeChanged signals.
  fake_cras_audio_client()->NotifyOutputNodeVolumeChangedForTesting(
      kUSBJabraSpeakerOutput2->id, 20);
  fake_cras_audio_client()->NotifyOutputNodeVolumeChangedForTesting(
      kUSBJabraSpeakerOutput1->id, 20);
  fake_cras_audio_client()->NotifyOutputNodeVolumeChangedForTesting(
      kUSBJabraSpeakerOutput2->id, 30);
  fake_cras_audio_client()->NotifyOutputNodeVolumeChangedForTesting(
      kUSBJabraSpeakerOutput1->id, 30);

  // Verify that both speakers are set to the designated volume level after
  // receiving all delayed signals.
  EXPECT_EQ(4, test_observer_->output_volume_changed_count());
  EXPECT_EQ(30, cras_audio_handler_->GetOutputVolumePercent());
  EXPECT_EQ(30, cras_audio_handler_->GetOutputVolumePercentForDevice(
                    kUSBJabraSpeakerOutput1->id));
  EXPECT_EQ(30, cras_audio_handler_->GetOutputVolumePercentForDevice(
                    kUSBJabraSpeakerOutput2->id));
}

TEST_P(CrasAudioHandlerTest, ChangeActiveNodesHotrodInitWithCameraInputActive) {
  AudioNodeList audio_nodes = GenerateAudioNodeList(
      {kHDMIOutput, kUSBJabraSpeakerOutput1, kUSBJabraSpeakerOutput2,
       kUSBJabraSpeakerInput1, kUSBJabraSpeakerInput2});
  // Make the camera input to be plugged in later than jabra's input.
  AudioNode usb_camera = GenerateAudioNode(kUSBCameraInput);
  usb_camera.plugged_time = 10000000;
  audio_nodes.push_back(usb_camera);
  SetUpCrasAudioHandler(audio_nodes);

  // Verify the audio devices size.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());

  // Verify the 1st jabra speaker's output is selected as active output
  // node and camera's input is selected active input by CrasAudioHandler.
  EXPECT_EQ(2, GetActiveDeviceCount());
  EXPECT_EQ(kUSBJabraSpeakerOutput1->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());
  EXPECT_EQ(kUSBCameraInput->id,
            cras_audio_handler_->GetPrimaryActiveInputNode());

  // Set both jabra speakers's input and output nodes to active, this simulates
  // the call sent by hotrod initialization process.
  test_observer_->reset_active_output_node_changed_count();
  test_observer_->reset_active_input_node_changed_count();
  cras_audio_handler_->ChangeActiveNodes(
      {kUSBJabraSpeakerOutput1->id, kUSBJabraSpeakerOutput2->id,
       kUSBJabraSpeakerInput1->id, kUSBJabraSpeakerInput2->id});

  // Verify both jabra speakers' input/output nodes are made active.
  // num_active_nodes = GetActiveDeviceCount();
  EXPECT_EQ(4, GetActiveDeviceCount());
  const AudioDevice* active_output_1 =
      GetDeviceFromId(kUSBJabraSpeakerOutput1->id);
  EXPECT_TRUE(active_output_1->active);
  const AudioDevice* active_output_2 =
      GetDeviceFromId(kUSBJabraSpeakerOutput2->id);
  EXPECT_TRUE(active_output_2->active);
  EXPECT_EQ(kUSBJabraSpeakerOutput1->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());
  const AudioDevice* active_input_1 =
      GetDeviceFromId(kUSBJabraSpeakerInput1->id);
  EXPECT_TRUE(active_input_1->active);
  const AudioDevice* active_input_2 =
      GetDeviceFromId(kUSBJabraSpeakerInput2->id);
  EXPECT_TRUE(active_input_2->active);
  EXPECT_EQ(kUSBJabraSpeakerInput1->id,
            cras_audio_handler_->GetPrimaryActiveInputNode());

  // Verify only 1 ActiveOutputNodeChanged notification has been sent out
  // by calling ChangeActiveNodes.
  EXPECT_EQ(1, test_observer_->active_output_node_changed_count());
  EXPECT_EQ(1, test_observer_->active_input_node_changed_count());
}

TEST_P(CrasAudioHandlerTest, SetActiveNodesHotrodInitWithCameraInputActive) {
  AudioNodeList audio_nodes = GenerateAudioNodeList(
      {kHDMIOutput, kUSBJabraSpeakerOutput1, kUSBJabraSpeakerOutput2,
       kUSBJabraSpeakerInput1, kUSBJabraSpeakerInput2});
  // Make the camera input to be plugged in later than jabra's input.
  AudioNode usb_camera = GenerateAudioNode(kUSBCameraInput);
  usb_camera.plugged_time = 10000000;
  audio_nodes.push_back(usb_camera);
  SetUpCrasAudioHandler(audio_nodes);

  // Verify the audio devices size.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());

  // Verify the 1st jabra speaker's output is selected as active output
  // node and camera's input is selected active input by CrasAudioHandler.
  EXPECT_EQ(2, GetActiveDeviceCount());
  EXPECT_EQ(kUSBJabraSpeakerOutput1->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());
  EXPECT_EQ(kUSBCameraInput->id,
            cras_audio_handler_->GetPrimaryActiveInputNode());

  // Set both jabra speakers's input and output nodes to active, this simulates
  // the call sent by hotrod initialization process.
  test_observer_->reset_active_output_node_changed_count();
  test_observer_->reset_active_input_node_changed_count();

  cras_audio_handler_->SetActiveOutputNodes(
      {kUSBJabraSpeakerOutput1->id, kUSBJabraSpeakerOutput2->id});

  cras_audio_handler_->SetActiveInputNodes(
      {kUSBJabraSpeakerInput1->id, kUSBJabraSpeakerInput2->id});

  // Verify both jabra speakers' input/output nodes are made active.
  // num_active_nodes = GetActiveDeviceCount();
  EXPECT_EQ(4, GetActiveDeviceCount());
  const AudioDevice* active_output_1 =
      GetDeviceFromId(kUSBJabraSpeakerOutput1->id);
  EXPECT_TRUE(active_output_1->active);
  const AudioDevice* active_output_2 =
      GetDeviceFromId(kUSBJabraSpeakerOutput2->id);
  EXPECT_TRUE(active_output_2->active);
  EXPECT_EQ(kUSBJabraSpeakerOutput1->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());
  const AudioDevice* active_input_1 =
      GetDeviceFromId(kUSBJabraSpeakerInput1->id);
  EXPECT_TRUE(active_input_1->active);
  const AudioDevice* active_input_2 =
      GetDeviceFromId(kUSBJabraSpeakerInput2->id);
  EXPECT_TRUE(active_input_2->active);
  EXPECT_EQ(kUSBJabraSpeakerInput1->id,
            cras_audio_handler_->GetPrimaryActiveInputNode());

  // Verify only 1 ActiveOutputNodeChanged notification has been sent out
  // by calling ChangeActiveNodes.
  EXPECT_EQ(1, test_observer_->active_output_node_changed_count());
  EXPECT_EQ(1, test_observer_->active_input_node_changed_count());
}

TEST_P(CrasAudioHandlerTest, ChangeActiveNodesWithFewerActives) {
  AudioNodeList audio_nodes = GenerateAudioNodeList(
      {kHDMIOutput, kUSBJabraSpeakerOutput1, kUSBJabraSpeakerOutput2});
  SetUpCrasAudioHandler(audio_nodes);

  // Verify the audio devices size.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());

  // Set all three nodes to be active.
  cras_audio_handler_->ChangeActiveNodes({kHDMIOutput->id,
                                          kUSBJabraSpeakerOutput1->id,
                                          kUSBJabraSpeakerOutput2->id});

  // Verify all three nodes are active.
  EXPECT_EQ(3, GetActiveDeviceCount());
  const AudioDevice* active_output_1 = GetDeviceFromId(kHDMIOutput->id);
  EXPECT_TRUE(active_output_1->active);
  const AudioDevice* active_output_2 =
      GetDeviceFromId(kUSBJabraSpeakerOutput1->id);
  EXPECT_TRUE(active_output_2->active);
  const AudioDevice* active_output_3 =
      GetDeviceFromId(kUSBJabraSpeakerOutput2->id);
  EXPECT_TRUE(active_output_3->active);

  // Now call ChangeActiveDevices with only 2 nodes.
  cras_audio_handler_->ChangeActiveNodes(
      {kUSBJabraSpeakerOutput1->id, kUSBJabraSpeakerOutput2->id});

  // Verify only 2 nodes are active.
  EXPECT_EQ(2, GetActiveDeviceCount());
  const AudioDevice* output_1 = GetDeviceFromId(kHDMIOutput->id);
  EXPECT_FALSE(output_1->active);
  const AudioDevice* output_2 = GetDeviceFromId(kUSBJabraSpeakerOutput1->id);
  EXPECT_TRUE(output_2->active);
  const AudioDevice* output_3 = GetDeviceFromId(kUSBJabraSpeakerOutput2->id);
  EXPECT_TRUE(output_3->active);
}

TEST_P(CrasAudioHandlerTest, SetActiveNodesWithFewerActives) {
  AudioNodeList audio_nodes = GenerateAudioNodeList(
      {kHDMIOutput, kUSBJabraSpeakerOutput1, kUSBJabraSpeakerOutput2});
  SetUpCrasAudioHandler(audio_nodes);

  // Verify the audio devices size.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());

  // Set all three nodes to be active.
  cras_audio_handler_->SetActiveOutputNodes({kHDMIOutput->id,
                                             kUSBJabraSpeakerOutput1->id,
                                             kUSBJabraSpeakerOutput2->id});

  // Verify all three nodes are active.
  EXPECT_EQ(3, GetActiveDeviceCount());
  const AudioDevice* active_output_1 = GetDeviceFromId(kHDMIOutput->id);
  EXPECT_TRUE(active_output_1->active);
  const AudioDevice* active_output_2 =
      GetDeviceFromId(kUSBJabraSpeakerOutput1->id);
  EXPECT_TRUE(active_output_2->active);
  const AudioDevice* active_output_3 =
      GetDeviceFromId(kUSBJabraSpeakerOutput2->id);
  EXPECT_TRUE(active_output_3->active);

  // Now call SetActiveOutputNodes with only 2 nodes.
  cras_audio_handler_->SetActiveOutputNodes(
      {kUSBJabraSpeakerOutput1->id, kUSBJabraSpeakerOutput2->id});

  // Verify only 2 nodes are active.
  EXPECT_EQ(2, GetActiveDeviceCount());
  const AudioDevice* output_1 = GetDeviceFromId(kHDMIOutput->id);
  EXPECT_FALSE(output_1->active);
  const AudioDevice* output_2 = GetDeviceFromId(kUSBJabraSpeakerOutput1->id);
  EXPECT_TRUE(output_2->active);
  const AudioDevice* output_3 = GetDeviceFromId(kUSBJabraSpeakerOutput2->id);
  EXPECT_TRUE(output_3->active);
}

TEST_P(CrasAudioHandlerTest, HotrodInitWithSingleJabra) {
  // Simulates the hotrod initializated with a single jabra device and
  // CrasAudioHandler selected jabra input/output as active devices.
  // Verify the jabra speaker's output and input are selected as active nodes
  // by CrasAudioHandler.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kHDMIOutput, kUSBJabraSpeakerOutput1,
                         kUSBJabraSpeakerInput1, kUSBCameraInput},
      /*expected_active_input_node=*/kUSBJabraSpeakerInput1,
      /*expected_active_output_node=*/kUSBJabraSpeakerOutput1,
      /*expected_has_alternative_input=*/true,
      /*expected_has_alternative_output=*/true);
}

TEST_P(CrasAudioHandlerTest,
       ChangeActiveNodesHotrodInitWithSingleJabraCameraPlugInLater) {
  AudioNodeList audio_nodes = GenerateAudioNodeList(
      {kHDMIOutput, kUSBJabraSpeakerOutput1, kUSBJabraSpeakerInput1});
  AudioNode usb_camera = GenerateAudioNode(kUSBCameraInput);
  usb_camera.plugged_time = 10000000;
  audio_nodes.push_back(usb_camera);
  SetUpCrasAudioHandler(audio_nodes);

  // Verify the audio devices size.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());

  // Verify the jabra speaker's output is selected as active output, and
  // camera's input is selected as active input by CrasAudioHandler
  EXPECT_EQ(2, GetActiveDeviceCount());
  EXPECT_EQ(kUSBJabraSpeakerOutput1->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());
  EXPECT_EQ(kUSBCameraInput->id,
            cras_audio_handler_->GetPrimaryActiveInputNode());

  // Simulate hotrod app call to set jabra input as active device with only
  // jabra input node in the active node list, which does not conform to the
  // new SetActiveDevices protocol, but just show we can still handle it if
  // this happens.
  cras_audio_handler_->ChangeActiveNodes(
      {kUSBJabraSpeakerOutput1->id, kUSBJabraSpeakerInput1->id});

  // Verify the jabra speaker's output is selected as active output, and
  // jabra's input is selected as active input.
  EXPECT_EQ(2, GetActiveDeviceCount());
  EXPECT_EQ(kUSBJabraSpeakerOutput1->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());
  EXPECT_EQ(kUSBJabraSpeakerInput1->id,
            cras_audio_handler_->GetPrimaryActiveInputNode());
}

TEST_P(CrasAudioHandlerTest,
       SetActiveNodesHotrodInitWithSingleJabraCameraPlugInLater) {
  AudioNodeList audio_nodes = GenerateAudioNodeList(
      {kHDMIOutput, kUSBJabraSpeakerOutput1, kUSBJabraSpeakerInput1});
  AudioNode usb_camera = GenerateAudioNode(kUSBCameraInput);
  usb_camera.plugged_time = 10000000;
  audio_nodes.push_back(usb_camera);
  SetUpCrasAudioHandler(audio_nodes);

  // Verify the audio devices size.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());

  // Verify the jabra speaker's output is selected as active output, and
  // camera's input is selected as active input by CrasAudioHandler
  EXPECT_EQ(2, GetActiveDeviceCount());
  EXPECT_EQ(kUSBJabraSpeakerOutput1->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());
  EXPECT_EQ(kUSBCameraInput->id,
            cras_audio_handler_->GetPrimaryActiveInputNode());

  // Simulate hotrod app call to set jabra input as active device with only
  // jabra input node in the active node list, which does not conform to the
  // new SetActiveDevices protocol, but just show we can still handle it if
  // this happens.
  cras_audio_handler_->SetActiveOutputNodes({kUSBJabraSpeakerOutput1->id});

  cras_audio_handler_->SetActiveInputNodes({kUSBJabraSpeakerInput1->id});

  // Verify the jabra speaker's output is selected as active output, and
  // jabra's input is selected as active input.
  EXPECT_EQ(2, GetActiveDeviceCount());
  EXPECT_EQ(kUSBJabraSpeakerOutput1->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());
  EXPECT_EQ(kUSBJabraSpeakerInput1->id,
            cras_audio_handler_->GetPrimaryActiveInputNode());
}

TEST_P(CrasAudioHandlerTest, ChangeActiveNodesDeactivatePrimaryActiveNode) {
  AudioNodeList audio_nodes =
      GenerateAudioNodeList({kUSBJabraSpeakerInput1, kUSBJabraSpeakerInput2});
  AudioNode usb_camera = GenerateAudioNode(kUSBCameraInput);
  usb_camera.plugged_time = 10000000;
  audio_nodes.push_back(usb_camera);
  SetUpCrasAudioHandler(audio_nodes);

  // Verify the audio devices size.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());

  // Verify the camera's input is selected as active input by CrasAudioHandler
  EXPECT_EQ(1, GetActiveDeviceCount());
  EXPECT_EQ(kUSBCameraInput->id,
            cras_audio_handler_->GetPrimaryActiveInputNode());

  // Simulate hotrod app call to set jabra input as active device with only
  // jabra input node in the active node list, which does not conform to the
  // new SetActiveDevices protocol, but just show we can still handle it if
  // this happens.
  cras_audio_handler_->ChangeActiveNodes(
      {kUSBJabraSpeakerInput1->id, kUSBCameraInput->id});

  // Verify active input devices are set as expected, with primary active input
  // staying the same.
  EXPECT_EQ(2, GetActiveDeviceCount());
  EXPECT_EQ(kUSBCameraInput->id,
            cras_audio_handler_->GetPrimaryActiveInputNode());

  const AudioDevice* additional_speaker =
      cras_audio_handler_->GetDeviceFromId(kUSBJabraSpeakerInput1->id);
  ASSERT_TRUE(additional_speaker);
  EXPECT_TRUE(additional_speaker->active);

  // Update active device list so previously primary active device is not
  // active anymore.
  cras_audio_handler_->ChangeActiveNodes(
      {kUSBJabraSpeakerInput1->id, kUSBJabraSpeakerInput2->id});

  // Verify that list of active devices is correctly set, and that a new primary
  // active input is selected.
  EXPECT_EQ(2, GetActiveDeviceCount());
  EXPECT_EQ(kUSBJabraSpeakerInput1->id,
            cras_audio_handler_->GetPrimaryActiveInputNode());

  additional_speaker =
      cras_audio_handler_->GetDeviceFromId(kUSBJabraSpeakerInput2->id);
  ASSERT_TRUE(additional_speaker);
  EXPECT_TRUE(additional_speaker->active);
}

TEST_P(CrasAudioHandlerTest, SetActiveNodesDeactivatePrimaryActiveNode) {
  AudioNodeList audio_nodes =
      GenerateAudioNodeList({kUSBJabraSpeakerInput1, kUSBJabraSpeakerInput2});
  AudioNode usb_camera = GenerateAudioNode(kUSBCameraInput);
  usb_camera.plugged_time = 10000000;
  audio_nodes.push_back(usb_camera);
  SetUpCrasAudioHandler(audio_nodes);

  // Verify the audio devices size.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());

  // Verify the camera's input is selected as active input by CrasAudioHandler.
  EXPECT_EQ(1, GetActiveDeviceCount());
  EXPECT_EQ(kUSBCameraInput->id,
            cras_audio_handler_->GetPrimaryActiveInputNode());

  // Add another device to active input device list.
  cras_audio_handler_->SetActiveInputNodes(
      {kUSBJabraSpeakerInput1->id, kUSBCameraInput->id});

  // Verify active input devices are set as expected, with primary active input
  // staying the same.
  EXPECT_EQ(2, GetActiveDeviceCount());
  EXPECT_EQ(kUSBCameraInput->id,
            cras_audio_handler_->GetPrimaryActiveInputNode());

  const AudioDevice* additional_speaker =
      cras_audio_handler_->GetDeviceFromId(kUSBJabraSpeakerInput1->id);
  ASSERT_TRUE(additional_speaker);
  EXPECT_TRUE(additional_speaker->active);

  // Update active device list so previously primary active device is not
  // active anymore.
  cras_audio_handler_->SetActiveInputNodes(
      {kUSBJabraSpeakerInput1->id, kUSBJabraSpeakerInput2->id});

  // Verify that list of active devices is correctly set, and that a new primary
  // active input is selected.
  EXPECT_EQ(2, GetActiveDeviceCount());
  EXPECT_EQ(kUSBJabraSpeakerInput1->id,
            cras_audio_handler_->GetPrimaryActiveInputNode());

  additional_speaker =
      cras_audio_handler_->GetDeviceFromId(kUSBJabraSpeakerInput2->id);
  ASSERT_TRUE(additional_speaker);
  EXPECT_TRUE(additional_speaker->active);
}

TEST_P(CrasAudioHandlerTest,
       ChangeActiveNodesHotrodInitWithSingleJabraCameraPlugInLaterOldCall) {
  AudioNodeList audio_nodes = GenerateAudioNodeList(
      {kHDMIOutput, kUSBJabraSpeakerOutput1, kUSBJabraSpeakerInput1});
  AudioNode usb_camera = GenerateAudioNode(kUSBCameraInput);
  usb_camera.plugged_time = 10000000;
  audio_nodes.push_back(usb_camera);
  SetUpCrasAudioHandler(audio_nodes);

  // Verify the audio devices size.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());

  // Verify the jabra speaker's output is selected as active output, and
  // camera's input is selected as active input by CrasAudioHandler
  EXPECT_EQ(2, GetActiveDeviceCount());
  EXPECT_EQ(kUSBJabraSpeakerOutput1->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());
  EXPECT_EQ(kUSBCameraInput->id,
            cras_audio_handler_->GetPrimaryActiveInputNode());

  // Simulate hotrod app call to set jabra input as active device with only
  // jabra input node in the active node list, which does not conform to the
  // new SetActiveDevices protocol, but just show we can still handle it if
  // this happens.
  cras_audio_handler_->ChangeActiveNodes({kUSBJabraSpeakerInput1->id});

  // Verify the jabra speaker's output is selected as active output, and
  // jabra's input is selected as active input.
  EXPECT_EQ(2, GetActiveDeviceCount());
  EXPECT_EQ(kUSBJabraSpeakerOutput1->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());
  EXPECT_EQ(kUSBJabraSpeakerInput1->id,
            cras_audio_handler_->GetPrimaryActiveInputNode());
}

TEST_P(CrasAudioHandlerTest,
       SetActiveNodesHotrodInitWithSingleJabraCameraPlugInLaterOldCall) {
  AudioNodeList audio_nodes = GenerateAudioNodeList(
      {kHDMIOutput, kUSBJabraSpeakerOutput1, kUSBJabraSpeakerInput1});
  AudioNode usb_camera = GenerateAudioNode(kUSBCameraInput);
  usb_camera.plugged_time = 10000000;
  audio_nodes.push_back(usb_camera);
  SetUpCrasAudioHandler(audio_nodes);

  // Verify the audio devices size.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());

  // Verify the jabra speaker's output is selected as active output, and
  // camera's input is selected as active input by CrasAudioHandler
  EXPECT_EQ(2, GetActiveDeviceCount());
  EXPECT_EQ(kUSBJabraSpeakerOutput1->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());
  EXPECT_EQ(kUSBCameraInput->id,
            cras_audio_handler_->GetPrimaryActiveInputNode());

  // Simulate hotrod app call to set jabra input as active device with only
  // jabra input node in the active node list, which does not conform to the
  // new SetActiveDevices protocol, but just show we can still handle it if
  // this happens.
  cras_audio_handler_->SetActiveInputNodes({kUSBJabraSpeakerInput1->id});

  // Verify the jabra speaker's output is selected as active output, and
  // jabra's input is selected as active input.
  EXPECT_EQ(2, GetActiveDeviceCount());
  EXPECT_EQ(kUSBJabraSpeakerOutput1->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());
  EXPECT_EQ(kUSBJabraSpeakerInput1->id,
            cras_audio_handler_->GetPrimaryActiveInputNode());
}

TEST_P(CrasAudioHandlerTest,
       ChangeActiveNodesHotrodInitWithSingleJabraChangeOutput) {
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kHDMIOutput, kUSBJabraSpeakerOutput1,
                         kUSBJabraSpeakerInput1, kUSBCameraInput},
      /*expected_active_input_node=*/kUSBJabraSpeakerInput1,
      /*expected_active_output_node=*/kUSBJabraSpeakerOutput1,
      /*expected_has_alternative_input=*/true,
      /*expected_has_alternative_output=*/true);

  // Simulate hotrod app call SetActiveDevices to change active output
  // with only complete list of active nodes passed in, which is the new
  // way of hotrod app.
  cras_audio_handler_->ChangeActiveNodes(
      {kHDMIOutput->id, kUSBJabraSpeakerInput1->id});

  // Verify the jabra speaker's output is selected as active output, and
  // jabra's input is selected as active input.
  EXPECT_EQ(2, GetActiveDeviceCount());
  EXPECT_EQ(kHDMIOutput->id, cras_audio_handler_->GetPrimaryActiveOutputNode());
  EXPECT_EQ(kUSBJabraSpeakerInput1->id,
            cras_audio_handler_->GetPrimaryActiveInputNode());
}

TEST_P(CrasAudioHandlerTest,
       SetActiveNodesHotrodInitWithSingleJabraChangeOutput) {
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kHDMIOutput, kUSBJabraSpeakerOutput1,
                         kUSBJabraSpeakerInput1, kUSBCameraInput},
      /*expected_active_input_node=*/kUSBJabraSpeakerInput1,
      /*expected_active_output_node=*/kUSBJabraSpeakerOutput1,
      /*expected_has_alternative_input=*/true,
      /*expected_has_alternative_output=*/true);

  // Simulate hotrod app calling SetActiveDeviceLists to change active input
  // and output with complete list of active nodes passed in.
  cras_audio_handler_->SetActiveOutputNodes({kHDMIOutput->id});
  cras_audio_handler_->SetActiveInputNodes({kUSBJabraSpeakerInput1->id});

  // Verify the jabra speaker's output is selected as active output, and
  // jabra's input is selected as active input.
  EXPECT_EQ(2, GetActiveDeviceCount());
  EXPECT_EQ(kHDMIOutput->id, cras_audio_handler_->GetPrimaryActiveOutputNode());
  EXPECT_EQ(kUSBJabraSpeakerInput1->id,
            cras_audio_handler_->GetPrimaryActiveInputNode());
}

TEST_P(CrasAudioHandlerTest,
       ChangeActiveNodesHotrodInitWithSingleJabraChangeOutputOldCall) {
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kHDMIOutput, kUSBJabraSpeakerOutput1,
                         kUSBJabraSpeakerInput1, kUSBCameraInput},
      /*expected_active_input_node=*/kUSBJabraSpeakerInput1,
      /*expected_active_output_node=*/kUSBJabraSpeakerOutput1,
      /*expected_has_alternative_input=*/true,
      /*expected_has_alternative_output=*/true);

  // Simulate hotrod app call SetActiveDevices to change active output
  // with only a single active output nodes passed in, which is the old
  // way of hotrod app.
  cras_audio_handler_->ChangeActiveNodes({kHDMIOutput->id});

  // Verify the jabra speaker's output is selected as active output, and
  // jabra's input is selected as active input.
  EXPECT_EQ(2, GetActiveDeviceCount());
  EXPECT_EQ(kHDMIOutput->id, cras_audio_handler_->GetPrimaryActiveOutputNode());
  EXPECT_EQ(kUSBJabraSpeakerInput1->id,
            cras_audio_handler_->GetPrimaryActiveInputNode());
}

TEST_P(CrasAudioHandlerTest, SetEmptyActiveOutputNodes) {
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kHDMIOutput, kUSBJabraSpeakerOutput1,
                         kUSBJabraSpeakerInput1, kUSBCameraInput},
      /*expected_active_input_node=*/kUSBJabraSpeakerInput1,
      /*expected_active_output_node=*/kUSBJabraSpeakerOutput1,
      /*expected_has_alternative_input=*/true,
      /*expected_has_alternative_output=*/true);

  cras_audio_handler_->SetActiveOutputNodes(CrasAudioHandler::NodeIdList());

  // Verify the jabra's input is selected as active input, and that there are
  // no active outputs.
  EXPECT_EQ(1, GetActiveDeviceCount());
  EXPECT_EQ(kUSBJabraSpeakerInput1->id,
            cras_audio_handler_->GetPrimaryActiveInputNode());
  EXPECT_EQ(0u, cras_audio_handler_->GetPrimaryActiveOutputNode());
}

TEST_P(CrasAudioHandlerTest, SetEmptyActiveInputNodes) {
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kHDMIOutput, kUSBJabraSpeakerOutput1,
                         kUSBJabraSpeakerInput1, kUSBCameraInput},
      /*expected_active_input_node=*/kUSBJabraSpeakerInput1,
      /*expected_active_output_node=*/kUSBJabraSpeakerOutput1,
      /*expected_has_alternative_input=*/true,
      /*expected_has_alternative_output=*/true);

  cras_audio_handler_->SetActiveInputNodes(CrasAudioHandler::NodeIdList());

  // Verify the jabra speaker's output is selected as active output, and
  // there are no active inputs..
  EXPECT_EQ(1, GetActiveDeviceCount());
  EXPECT_EQ(kUSBJabraSpeakerOutput1->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());
  EXPECT_EQ(0u, cras_audio_handler_->GetPrimaryActiveInputNode());
}

TEST_P(CrasAudioHandlerTest, NoMoreAudioInputDevices) {
  // Some device like chromebox does not have the internal input device. The
  // active devices should be reset when the user plugs a device and then
  // unplugs it to such device.
  AudioNodeList audio_nodes = GenerateAudioNodeList({kInternalSpeaker});
  SetUpCrasAudioHandler(audio_nodes);

  EXPECT_EQ(0ULL, cras_audio_handler_->GetPrimaryActiveInputNode());

  audio_nodes.push_back(GenerateAudioNode(kMicJack));
  ChangeAudioNodes(audio_nodes);

  EXPECT_EQ(kMicJack->id, cras_audio_handler_->GetPrimaryActiveInputNode());
  EXPECT_EQ(1, test_observer_->active_input_node_changed_count());
  test_observer_->reset_active_input_node_changed_count();

  audio_nodes.pop_back();
  ChangeAudioNodes(audio_nodes);
  EXPECT_EQ(0ULL, cras_audio_handler_->GetPrimaryActiveInputNode());
  EXPECT_EQ(1, test_observer_->active_input_node_changed_count());
}

// Test the case hot plugging 35mm headphone and mic. Both 35mm headphone and
// and mic should always be selected as active output and input devices.
TEST_P(CrasAudioHandlerTest, HotPlug35mmHeadphoneAndMic) {
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker, kInternalMic},
      /*expected_active_input_node=*/kInternalMic,
      /*expected_active_output_node=*/kInternalSpeaker,
      /*expected_has_alternative_input=*/false,
      /*expected_has_alternative_output=*/false);

  // Hotplug the 35mm headset with both headphone and mic.
  AudioNodeList audio_nodes;
  AudioNode internal_speaker = GenerateAudioNode(kInternalSpeaker);
  internal_speaker.active = true;
  audio_nodes.push_back(internal_speaker);
  AudioNode headphone = GenerateAudioNode(kHeadphone);
  headphone.active = false;
  headphone.plugged_time = 50000000;
  audio_nodes.push_back(headphone);
  AudioNode internal_mic = GenerateAudioNode(kInternalMic);
  internal_mic.active = true;
  audio_nodes.push_back(internal_mic);
  AudioNode mic = GenerateAudioNode(kMicJack);
  mic.active = false;
  mic.plugged_time = 50000000;
  audio_nodes.push_back(mic);
  ChangeAudioNodes(audio_nodes);

  // Verify 35mm headphone is selected as active output.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(4u, audio_devices.size());
  EXPECT_EQ(headphone.id, cras_audio_handler_->GetPrimaryActiveOutputNode());
  // Verify 35mm mic is selected as active input.
  EXPECT_EQ(mic.id, cras_audio_handler_->GetPrimaryActiveInputNode());

  // Manually select internal speaker as active output.
  AudioDevice internal_output(internal_speaker);
  cras_audio_handler_->SwitchToDevice(internal_output, true,
                                      DeviceActivateType::kActivateByUser);
  // Manually select internal mic as active input.
  AudioDevice internal_input(internal_mic);
  cras_audio_handler_->SwitchToDevice(internal_input, true,
                                      DeviceActivateType::kActivateByUser);

  // Verify the active output is switched to internal speaker.
  EXPECT_EQ(internal_speaker.id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());
  EXPECT_LT(internal_speaker.plugged_time, headphone.plugged_time);
  // Verify the active input is switched to internal mic.
  EXPECT_EQ(internal_mic.id, cras_audio_handler_->GetPrimaryActiveInputNode());
  EXPECT_LT(internal_mic.plugged_time, mic.plugged_time);

  // Unplug 35mm headphone and mic.
  audio_nodes.clear();
  internal_speaker.active = true;
  audio_nodes.push_back(internal_speaker);
  internal_mic.active = true;
  audio_nodes.push_back(internal_mic);
  ChangeAudioNodes(audio_nodes);

  // Verify internal speaker remains as active output.
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(2u, audio_devices.size());
  EXPECT_EQ(internal_speaker.id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());
  // Verify internal mic remains as active input.
  EXPECT_EQ(internal_mic.id, cras_audio_handler_->GetPrimaryActiveInputNode());

  // Hotplug 35mm headset again.
  headphone.active = false;
  headphone.plugged_time = 90000000;
  audio_nodes.push_back(headphone);
  mic.active = false;
  mic.plugged_time = 90000000;
  audio_nodes.push_back(mic);
  ChangeAudioNodes(audio_nodes);

  // Verify 35mm headphone is active again.
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(4u, audio_devices.size());
  EXPECT_EQ(headphone.id, cras_audio_handler_->GetPrimaryActiveOutputNode());
  EXPECT_LT(internal_speaker.plugged_time, headphone.plugged_time);
  // Verify 35mm mic is active again.
  EXPECT_EQ(mic.id, cras_audio_handler_->GetPrimaryActiveInputNode());
  EXPECT_LT(internal_mic.plugged_time, mic.plugged_time);
}

// Test the case where 3.5mm headphone and mic will be activated automatically
// when being hot plugged, under the condition that the
// kAudioSelectionImprovement flag is on.
TEST_P(CrasAudioHandlerTest,
       HotPlug35mmHeadphoneAndMic_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);

  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker, kInternalMic},
      /*expected_active_input_node=*/kInternalMic,
      /*expected_active_output_node=*/kInternalSpeaker,
      /*expected_has_alternative_input=*/false,
      /*expected_has_alternative_output=*/false);

  // No exception rule metrics are recorded before plugging 3.5mm headset.
  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      AudioDeviceMetricsHandler::AudioSelectionExceptionRules::
          kInputRule1HotPlugPrivilegedDevice,
      /*expected_count=*/0);
  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      AudioDeviceMetricsHandler::AudioSelectionExceptionRules::
          kOutputRule1HotPlugPrivilegedDevice,
      /*expected_count=*/0);

  // Hotplug the 35mm headset with both headphone and mic.
  AudioNodeList audio_nodes;
  AudioNode internal_speaker = GenerateAudioNode(kInternalSpeaker);
  internal_speaker.active = true;
  audio_nodes.push_back(internal_speaker);
  AudioNode headphone = GenerateAudioNode(kHeadphone);
  headphone.active = false;
  headphone.plugged_time = 50000000;
  audio_nodes.push_back(headphone);
  AudioNode internal_mic = GenerateAudioNode(kInternalMic);
  internal_mic.active = true;
  audio_nodes.push_back(internal_mic);
  AudioNode mic = GenerateAudioNode(kMicJack);
  mic.active = false;
  mic.plugged_time = 50000000;
  audio_nodes.push_back(mic);
  ChangeAudioNodes(audio_nodes);

  // Verify 35mm headphone is selected as active output.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(4u, audio_devices.size());
  EXPECT_EQ(headphone.id, cras_audio_handler_->GetPrimaryActiveOutputNode());
  // Verify 35mm mic is selected as active input.
  EXPECT_EQ(mic.id, cras_audio_handler_->GetPrimaryActiveInputNode());

  // Exception rule metrics are recorded after plugging 3.5mm headset.
  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      AudioDeviceMetricsHandler::AudioSelectionExceptionRules::
          kInputRule1HotPlugPrivilegedDevice,
      /*expected_count=*/1);
  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      AudioDeviceMetricsHandler::AudioSelectionExceptionRules::
          kOutputRule1HotPlugPrivilegedDevice,
      /*expected_count=*/1);

  // Manually select internal speaker as active output.
  AudioDevice internal_output(internal_speaker);
  cras_audio_handler_->SwitchToDevice(internal_output, true,
                                      DeviceActivateType::kActivateByUser);
  // Manually select internal mic as active input.
  AudioDevice internal_input(internal_mic);
  cras_audio_handler_->SwitchToDevice(internal_input, true,
                                      DeviceActivateType::kActivateByUser);

  // Verify the active output is switched to internal speaker.
  EXPECT_EQ(internal_speaker.id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());
  EXPECT_LT(internal_speaker.plugged_time, headphone.plugged_time);
  // Verify the active input is switched to internal mic.
  EXPECT_EQ(internal_mic.id, cras_audio_handler_->GetPrimaryActiveInputNode());
  EXPECT_LT(internal_mic.plugged_time, mic.plugged_time);

  // Unplug 35mm headphone and mic.
  audio_nodes.clear();
  internal_speaker.active = true;
  audio_nodes.push_back(internal_speaker);
  internal_mic.active = true;
  audio_nodes.push_back(internal_mic);
  ChangeAudioNodes(audio_nodes);

  // Verify internal speaker remains as active output.
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(2u, audio_devices.size());
  EXPECT_EQ(internal_speaker.id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());
  // Verify internal mic remains as active input.
  EXPECT_EQ(internal_mic.id, cras_audio_handler_->GetPrimaryActiveInputNode());

  // Hotplug 35mm headset again.
  headphone.active = false;
  headphone.plugged_time = 90000000;
  audio_nodes.push_back(headphone);
  mic.active = false;
  mic.plugged_time = 90000000;
  audio_nodes.push_back(mic);
  ChangeAudioNodes(audio_nodes);

  // Verify 35mm headphone is active again.
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(4u, audio_devices.size());
  EXPECT_EQ(headphone.id, cras_audio_handler_->GetPrimaryActiveOutputNode());
  // Make sure headphone is plugged after internal speaker so that headphone is
  // being hot plugged.
  EXPECT_LT(internal_speaker.plugged_time, headphone.plugged_time);
  // Verify 35mm mic is active again.
  EXPECT_EQ(mic.id, cras_audio_handler_->GetPrimaryActiveInputNode());
  EXPECT_LT(internal_mic.plugged_time, mic.plugged_time);
}

// Test the case where bluetooth headphone and mic will be activated
// automatically when being hot plugged, under the condition that the
// kAudioSelectionImprovement flag is on.
TEST_P(CrasAudioHandlerTest,
       HotPlugBluetoothDevices_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);

  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker, kInternalMic},
      /*expected_active_input_node=*/kInternalMic,
      /*expected_active_output_node=*/kInternalSpeaker,
      /*expected_has_alternative_input=*/false,
      /*expected_has_alternative_output=*/false);

  // No exception rule metrics are recorded before plugging bluetooth device.
  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      AudioDeviceMetricsHandler::AudioSelectionExceptionRules::
          kInputRule1HotPlugPrivilegedDevice,
      /*expected_count=*/0);
  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      AudioDeviceMetricsHandler::AudioSelectionExceptionRules::
          kOutputRule1HotPlugPrivilegedDevice,
      /*expected_count=*/0);

  // Hotplug the bluetooth devices.
  AudioNodeList audio_nodes;
  AudioNode internal_speaker = GenerateAudioNode(kInternalSpeaker);
  internal_speaker.active = true;
  audio_nodes.push_back(internal_speaker);
  AudioNode bluetooth_output = GenerateAudioNode(kBluetoothHeadset);
  bluetooth_output.active = false;
  bluetooth_output.plugged_time = 50000000;
  audio_nodes.push_back(bluetooth_output);
  AudioNode internal_mic = GenerateAudioNode(kInternalMic);
  internal_mic.active = true;
  audio_nodes.push_back(internal_mic);
  AudioNode bluetooth_input = GenerateAudioNode(kBluetoothNbMic);
  bluetooth_input.active = false;
  bluetooth_input.plugged_time = 50000000;
  audio_nodes.push_back(bluetooth_input);
  ChangeAudioNodes(audio_nodes);

  // Verify bluetooth_output is selected as active output.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(4u, audio_devices.size());
  EXPECT_EQ(bluetooth_output.id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());
  // Verify bluetooth_input is selected as active input.
  EXPECT_EQ(bluetooth_input.id,
            cras_audio_handler_->GetPrimaryActiveInputNode());

  // Exception rule metrics are recorded after plugging bluetooth device.
  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      AudioDeviceMetricsHandler::AudioSelectionExceptionRules::
          kInputRule1HotPlugPrivilegedDevice,
      /*expected_count=*/1);
  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      AudioDeviceMetricsHandler::AudioSelectionExceptionRules::
          kOutputRule1HotPlugPrivilegedDevice,
      /*expected_count=*/1);

  // Manually select internal speaker as active output.
  AudioDevice internal_output(internal_speaker);
  cras_audio_handler_->SwitchToDevice(internal_output, true,
                                      DeviceActivateType::kActivateByUser);
  // Manually select internal mic as active input.
  AudioDevice internal_input(internal_mic);
  cras_audio_handler_->SwitchToDevice(internal_input, true,
                                      DeviceActivateType::kActivateByUser);

  // Verify the active output is switched to internal speaker.
  EXPECT_EQ(internal_speaker.id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());
  EXPECT_LT(internal_speaker.plugged_time, bluetooth_output.plugged_time);
  // Verify the active input is switched to internal mic.
  EXPECT_EQ(internal_mic.id, cras_audio_handler_->GetPrimaryActiveInputNode());
  EXPECT_LT(internal_mic.plugged_time, bluetooth_input.plugged_time);

  // Unplug bluetooth_output and bluetooth_input.
  audio_nodes.clear();
  internal_speaker.active = true;
  audio_nodes.push_back(internal_speaker);
  internal_mic.active = true;
  audio_nodes.push_back(internal_mic);
  ChangeAudioNodes(audio_nodes);

  // Verify internal speaker remains as active output.
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(2u, audio_devices.size());
  EXPECT_EQ(internal_speaker.id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());
  // Verify internal mic remains as active input.
  EXPECT_EQ(internal_mic.id, cras_audio_handler_->GetPrimaryActiveInputNode());

  // Hotplug bluetooth devices again.
  bluetooth_output.active = false;
  bluetooth_output.plugged_time = 90000000;
  audio_nodes.push_back(bluetooth_output);
  bluetooth_input.active = false;
  bluetooth_input.plugged_time = 90000000;
  audio_nodes.push_back(bluetooth_input);
  ChangeAudioNodes(audio_nodes);

  // Verify bluetooth_output is active again.
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(4u, audio_devices.size());
  EXPECT_EQ(bluetooth_output.id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());
  EXPECT_LT(internal_speaker.plugged_time, bluetooth_output.plugged_time);
  // Verify bluetooth_input is active again.
  EXPECT_EQ(bluetooth_input.id,
            cras_audio_handler_->GetPrimaryActiveInputNode());
  EXPECT_LT(internal_mic.plugged_time, bluetooth_input.plugged_time);
}

// Test the case where 3.5mm headphone will be activated automatically
// when being hot plugged, and exception rule #1 will be fired, under the
// condition that the kAudioSelectionImprovement flag is on.
TEST_P(CrasAudioHandlerTest,
       HotPlug35mmHeadphone_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);

  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kInternalSpeaker,
      /*expected_has_alternative_input=*/false,
      /*expected_has_alternative_output=*/false);

  // No exception rule metrics are recorded before plugging 3.5mm headset.
  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      AudioDeviceMetricsHandler::AudioSelectionExceptionRules::
          kInputRule1HotPlugPrivilegedDevice,
      /*expected_count=*/0);
  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      AudioDeviceMetricsHandler::AudioSelectionExceptionRules::
          kOutputRule1HotPlugPrivilegedDevice,
      /*expected_count=*/0);

  // Hotplug the 35mm headset with only headphone.
  AudioNodeList audio_nodes;
  AudioNode internal_speaker = GenerateAudioNode(kInternalSpeaker);
  internal_speaker.active = true;
  audio_nodes.push_back(internal_speaker);
  AudioNode headphone = GenerateAudioNode(kHeadphone);
  headphone.active = false;
  headphone.plugged_time = 50000000;
  audio_nodes.push_back(headphone);
  ChangeAudioNodes(audio_nodes);

  // Verify 35mm headphone is selected as active output.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(2u, audio_devices.size());
  EXPECT_EQ(headphone.id, cras_audio_handler_->GetPrimaryActiveOutputNode());

  // Exception rule metric for output is recorded after plugging 3.5mm
  // headphone.
  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      AudioDeviceMetricsHandler::AudioSelectionExceptionRules::
          kInputRule1HotPlugPrivilegedDevice,
      /*expected_count=*/0);
  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      AudioDeviceMetricsHandler::AudioSelectionExceptionRules::
          kOutputRule1HotPlugPrivilegedDevice,
      /*expected_count=*/1);
}

// Test the case where 3.5mm mic will be activated automatically
// when being hot plugged, and exception rule #1 will be fired, under the
// condition that the kAudioSelectionImprovement flag is on.
TEST_P(CrasAudioHandlerTest, HotPlug35mmMic_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);

  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalMic},
      /*expected_active_input_node=*/kInternalMic,
      /*expected_active_output_node=*/nullptr,
      /*expected_has_alternative_input=*/false,
      /*expected_has_alternative_output=*/false);

  // No exception rule metrics are recorded before plugging 3.5mm headset.
  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      AudioDeviceMetricsHandler::AudioSelectionExceptionRules::
          kInputRule1HotPlugPrivilegedDevice,
      /*expected_count=*/0);
  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      AudioDeviceMetricsHandler::AudioSelectionExceptionRules::
          kOutputRule1HotPlugPrivilegedDevice,
      /*expected_count=*/0);

  // Hotplug the 35mm headset with both only mic.
  AudioNodeList audio_nodes;
  AudioNode internal_mic = GenerateAudioNode(kInternalMic);
  internal_mic.active = true;
  audio_nodes.push_back(internal_mic);
  AudioNode mic = GenerateAudioNode(kMicJack);
  mic.active = false;
  mic.plugged_time = 50000000;
  audio_nodes.push_back(mic);
  ChangeAudioNodes(audio_nodes);

  // Verify 35mm mic is selected as active input.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(2u, audio_devices.size());
  EXPECT_EQ(mic.id, cras_audio_handler_->GetPrimaryActiveInputNode());

  // Exception rule metric for input is recorded after plugging 3.5mm mic.
  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      AudioDeviceMetricsHandler::AudioSelectionExceptionRules::
          kInputRule1HotPlugPrivilegedDevice,
      /*expected_count=*/1);
  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      AudioDeviceMetricsHandler::AudioSelectionExceptionRules::
          kOutputRule1HotPlugPrivilegedDevice,
      /*expected_count=*/0);
}

// Test the case in which an HDMI output is plugged in with other higher
// priority
// output devices already plugged and user has manually selected an active
// output.
TEST_P(CrasAudioHandlerTest, HotPlugHDMIChangeActiveOutput) {
  AudioNodeList audio_nodes;
  AudioNode internal_speaker = GenerateAudioNode(kInternalSpeaker);
  audio_nodes.push_back(internal_speaker);
  AudioNode usb_headset = GenerateAudioNode(kUSBHeadphone1);
  usb_headset.plugged_time = 80000000;
  audio_nodes.push_back(usb_headset);
  SetUpCrasAudioHandler(audio_nodes);

  // Verify the audio devices size.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());

  // Verify the USB headset is selected as active output by default.
  EXPECT_EQ(usb_headset.id, cras_audio_handler_->GetPrimaryActiveOutputNode());

  // Manually set the active output to internal speaker.
  AudioDevice internal_output(GenerateAudioNode(kInternalSpeaker));
  cras_audio_handler_->SwitchToDevice(internal_output, true,
                                      DeviceActivateType::kActivateByUser);

  // Verify the active output is switched to internal speaker.
  EXPECT_EQ(internal_speaker.id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());
  EXPECT_LT(internal_speaker.plugged_time, usb_headset.plugged_time);
  const AudioDevice* usb_device = GetDeviceFromId(usb_headset.id);
  EXPECT_FALSE(usb_device->active);

  // Plug in HDMI output.
  audio_nodes.clear();
  internal_speaker.active = true;
  audio_nodes.push_back(internal_speaker);
  usb_headset.active = false;
  audio_nodes.push_back(usb_headset);
  AudioNode hdmi = GenerateAudioNode(kHDMIOutput);
  hdmi.plugged_time = 90000000;
  audio_nodes.push_back(hdmi);
  ChangeAudioNodes(audio_nodes);

  // The active output change to hdmi as it has higher built-in priority than
  // the internal speaker.
  EXPECT_EQ(kHDMIOutputId, cras_audio_handler_->GetPrimaryActiveOutputNode());
}

TEST_P(CrasAudioHandlerTest,
       HotPlugHDMIChangeActiveOutput_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);

  AudioNodeList audio_nodes;
  AudioNode internal_speaker = GenerateAudioNode(kInternalSpeaker);
  audio_nodes.push_back(internal_speaker);
  AudioNode usb_headset = GenerateAudioNode(kUSBHeadphone1);
  usb_headset.plugged_time = 80000000;
  audio_nodes.push_back(usb_headset);
  SetUpCrasAudioHandler(audio_nodes);

  // Verify the audio devices size.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());

  // Verify the internal_speaker is selected since the device set was not seen
  // before based on system boots case.
  EXPECT_EQ(internal_speaker.id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());

  // Plug in HDMI output.
  audio_nodes.clear();
  internal_speaker.active = true;
  audio_nodes.push_back(internal_speaker);
  usb_headset.active = false;
  audio_nodes.push_back(usb_headset);
  AudioNode hdmi = GenerateAudioNode(kHDMIOutput);
  hdmi.plugged_time = 90000000;
  audio_nodes.push_back(hdmi);
  ChangeAudioNodes(audio_nodes);

  // The active output does not change to hdmi since it's a new device unseen
  // before.
  EXPECT_NE(kHDMIOutputId, cras_audio_handler_->GetPrimaryActiveOutputNode());
}

// Test the case in which the active device was set to inactive from cras after
// resuming from suspension state. See crbug.com/478968.
TEST_P(CrasAudioHandlerTest, ActiveNodeLostAfterResume) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({kHeadphone, kHDMIOutput});
  for (const auto& node : audio_nodes) {
    ASSERT_FALSE(node.active) << node.id << " expected to be inactive";
  }
  SetUpCrasAudioHandler(audio_nodes);

  // Verify the headphone is selected as the active output.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());
  EXPECT_EQ(kHeadphone->id, cras_audio_handler_->GetPrimaryActiveOutputNode());
  const AudioDevice* active_headphone = GetDeviceFromId(kHeadphone->id);
  EXPECT_EQ(kHeadphone->id, active_headphone->id);
  EXPECT_TRUE(active_headphone->active);

  // Simulate NodesChanged signal with headphone turning into inactive state,
  // and HDMI node removed.
  audio_nodes.clear();
  audio_nodes.push_back(GenerateAudioNode(kHeadphone));
  ChangeAudioNodes(audio_nodes);

  // Verify the headphone is set to active again.
  EXPECT_EQ(kHeadphone->id, cras_audio_handler_->GetPrimaryActiveOutputNode());
  const AudioDevice* headphone_resumed = GetDeviceFromId(kHeadphone->id);
  EXPECT_EQ(kHeadphone->id, headphone_resumed->id);
  EXPECT_TRUE(headphone_resumed->active);
}

// In the mirror mode, when the device resumes after being suspended, the hmdi
// node will be lost first, then re-appear with a different node id, but with
// the same stable id. If it is set as the non-active node  by user before
// suspend/resume, it should remain inactive after the device resumes even
// if it has a higher priority than the current active node.
// crbug.com/443014.
TEST_P(CrasAudioHandlerTest, HDMIRemainInactiveAfterSuspendResume) {
  // Verify the hdmi is selected as the active output since it has a higher
  // priority.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker, kHDMIOutput},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kHDMIOutput,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/true);

  // Manually set the active output to internal speaker.
  AudioNode internal_speaker = GenerateAudioNode(kInternalSpeaker);
  cras_audio_handler_->SwitchToDevice(AudioDevice(internal_speaker), true,
                                      DeviceActivateType::kActivateByUser);
  EXPECT_EQ(internal_speaker.id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());

  // Simulate the suspend and resume of the device during mirror mode. The HDMI
  // node will be lost first.
  AudioNodeList audio_nodes;
  internal_speaker.active = true;
  audio_nodes.push_back(internal_speaker);
  ChangeAudioNodes(audio_nodes);

  // Verify the HDMI node is lost, and internal speaker is still the active
  // node.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(1u, audio_devices.size());
  EXPECT_EQ(internal_speaker.id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());

  // Simulate the re-appearing of the hdmi node, which comes with a new id,
  // but the same stable device id.
  AudioNode hdmi_output = GenerateAudioNode(kHDMIOutput);
  AudioNode hdmi_output_2(hdmi_output);
  hdmi_output_2.id = 20006;
  hdmi_output_2.plugged_time = internal_speaker.plugged_time + 100;
  audio_nodes.push_back(hdmi_output_2);
  ASSERT_NE(hdmi_output.id, hdmi_output_2.id);
  ASSERT_EQ(hdmi_output.StableDeviceId(), hdmi_output_2.StableDeviceId());
  ChangeAudioNodes(audio_nodes);

  // Verify the hdmi node is not set the active, and the current active node
  // , the internal speaker, remains active.
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(2u, audio_devices.size());
  EXPECT_EQ(internal_speaker.id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());
}

// Test the case in which there are two NodesChanged signal for discovering
// output devices, and there is race between NodesChange and SetActiveOutput
// during this process. See crbug.com/478968.
TEST_P(CrasAudioHandlerTest, ActiveNodeLostDuringLoginSession) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({kHeadphone});
  for (const auto& node : audio_nodes) {
    ASSERT_FALSE(node.active) << node.id << " expected to be inactive";
  }
  SetUpCrasAudioHandler(audio_nodes);

  // Verify the headphone is selected as the active output.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(audio_nodes.size(), audio_devices.size());
  EXPECT_EQ(kHeadphone->id, cras_audio_handler_->GetPrimaryActiveOutputNode());
  const AudioDevice* active_headphone = GetDeviceFromId(kHeadphone->id);
  EXPECT_EQ(kHeadphone->id, active_headphone->id);
  EXPECT_TRUE(active_headphone->active);

  // Simulate NodesChanged signal with headphone turning into inactive state,
  // and add a new HDMI output node.
  audio_nodes.clear();
  audio_nodes.push_back(GenerateAudioNode(kHeadphone));
  audio_nodes.push_back(GenerateAudioNode(kHDMIOutput));
  ChangeAudioNodes(audio_nodes);

  // Verify the headphone is set to active again.
  EXPECT_EQ(kHeadphone->id, cras_audio_handler_->GetPrimaryActiveOutputNode());
  const AudioDevice* headphone_resumed = GetDeviceFromId(kHeadphone->id);
  EXPECT_EQ(kHeadphone->id, headphone_resumed->id);
  EXPECT_TRUE(headphone_resumed->active);
}

// This test HDMI output rediscovering case in crbug.com/503667.
TEST_P(CrasAudioHandlerTest, HDMIOutputRediscover) {
  // Verify the HDMI device has been selected as the active output, and audio
  // output is not muted.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker, kHDMIOutput},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kHDMIOutput,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/true);

  EXPECT_FALSE(cras_audio_handler_->IsOutputMuted());

  // Trigger HDMI rediscovering grace period, and remove the HDMI node.
  const int grace_period_in_ms = 200;
  SetHDMIRediscoverGracePeriodDuration(grace_period_in_ms);
  SetActiveHDMIRediscover();
  AudioNodeList audio_nodes_lost_hdmi =
      GenerateAudioNodeList({kInternalSpeaker});
  ChangeAudioNodes(audio_nodes_lost_hdmi);

  // Verify the active output is switched to internal speaker, it is not muted
  // by preference, but the system output is muted during the grace period.
  AudioDevice active_output;
  EXPECT_TRUE(
      cras_audio_handler_->GetPrimaryActiveOutputDevice(&active_output));
  EXPECT_EQ(kInternalSpeaker->id, active_output.id);
  EXPECT_FALSE(
      cras_audio_handler_->IsOutputMutedForDevice(kInternalSpeaker->id));
  EXPECT_TRUE(cras_audio_handler_->IsOutputMuted());

  // Re-attach the HDMI device after a little delay.
  HDMIRediscoverWaiter waiter(this, grace_period_in_ms);
  waiter.WaitUntilTimeOut(grace_period_in_ms / 4);
  AudioNodeList audio_nodes =
      GenerateAudioNodeList({kInternalSpeaker, kHDMIOutput});
  ChangeAudioNodes(audio_nodes);

  // After HDMI re-discover grace period, verify HDMI output is selected as the
  // active device and not muted.
  waiter.WaitUntilHDMIRediscoverGracePeriodEnd();
  EXPECT_TRUE(
      cras_audio_handler_->GetPrimaryActiveOutputDevice(&active_output));
  EXPECT_EQ(kHDMIOutput->id, active_output.id);
  EXPECT_EQ(kHDMIOutput->id, cras_audio_handler_->GetPrimaryActiveOutputNode());
  EXPECT_FALSE(cras_audio_handler_->IsOutputMuted());
}

// This tests the case of output unmuting event is not notified after the hdmi
// output re-discover grace period ends.
TEST_P(CrasAudioHandlerTest, HDMIOutputUnplugDuringSuspension) {
  // Verify the HDMI device has been selected as the active output, and audio
  // output is not muted.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker, kHDMIOutput},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kHDMIOutput,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/true);

  EXPECT_FALSE(cras_audio_handler_->IsOutputMuted());

  // Trigger HDMI rediscovering grace period, and remove the HDMI node.
  const int grace_period_in_ms = 200;
  SetHDMIRediscoverGracePeriodDuration(grace_period_in_ms);
  SetActiveHDMIRediscover();
  AudioNodeList audio_nodes_lost_hdmi =
      GenerateAudioNodeList({kInternalSpeaker});
  ChangeAudioNodes(audio_nodes_lost_hdmi);

  // Verify the active output is switched to internal speaker, it is not muted
  // by preference, but the system output is muted during the grace period.
  AudioDevice active_output;
  EXPECT_TRUE(
      cras_audio_handler_->GetPrimaryActiveOutputDevice(&active_output));
  EXPECT_EQ(kInternalSpeaker->id, active_output.id);
  EXPECT_FALSE(
      cras_audio_handler_->IsOutputMutedForDevice(kInternalSpeaker->id));
  EXPECT_TRUE(cras_audio_handler_->IsOutputMuted());

  // After HDMI re-discover grace period, verify internal speaker is still the
  // active output and not muted.
  test_observer_->reset_output_mute_changed_count();
  HDMIRediscoverWaiter waiter(this, grace_period_in_ms);
  waiter.WaitUntilHDMIRediscoverGracePeriodEnd();
  EXPECT_TRUE(
      cras_audio_handler_->GetPrimaryActiveOutputDevice(&active_output));
  EXPECT_EQ(kInternalSpeaker->id, active_output.id);
  EXPECT_EQ(kInternalSpeaker->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());
  EXPECT_FALSE(cras_audio_handler_->IsOutputMuted());
  EXPECT_EQ(0, test_observer_->output_mute_changed_count());
}

TEST_P(CrasAudioHandlerTest, FrontCameraStartStop) {
  // Verify the front mic has been selected as the active input.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kFrontMic, kRearMic},
      /*expected_active_input_node=*/kFrontMic,
      /*expected_active_output_node=*/nullptr,
      /*expected_has_alternative_input=*/false,
      /*expected_has_alternative_output=*/std::nullopt);

  EXPECT_TRUE(cras_audio_handler_->HasDualInternalMic());

  // Start the front facing camera.
  StartFrontFacingCamera();
  // Verify the front mic has been selected as the active input.
  EXPECT_EQ(kFrontMicId, cras_audio_handler_->GetPrimaryActiveInputNode());

  // Stop the front facing camera.
  StopFrontFacingCamera();
  // Verify the front mic has been selected as the active input.
  EXPECT_EQ(kFrontMicId, cras_audio_handler_->GetPrimaryActiveInputNode());
}

TEST_P(CrasAudioHandlerTest, RearCameraStartStop) {
  // Verify the front mic has been selected as the active input.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kFrontMic, kRearMic},
      /*expected_active_input_node=*/kFrontMic,
      /*expected_active_output_node=*/nullptr,
      /*expected_has_alternative_input=*/false,
      /*expected_has_alternative_output=*/std::nullopt);

  EXPECT_TRUE(cras_audio_handler_->HasDualInternalMic());

  // Start the rear facing camera.
  StartRearFacingCamera();
  // Verify the active input is switched to the rear mic.
  EXPECT_EQ(kRearMicId, cras_audio_handler_->GetPrimaryActiveInputNode());

  // Stop the rear facing camera.
  StopRearFacingCamera();
  // Verify the active input is switched back to front mic.
  EXPECT_EQ(kFrontMicId, cras_audio_handler_->GetPrimaryActiveInputNode());
}

TEST_P(CrasAudioHandlerTest, SwitchFrontRearCamera) {
  // Verify the front mic has been selected as the active input.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kFrontMic, kRearMic},
      /*expected_active_input_node=*/kFrontMic,
      /*expected_active_output_node=*/nullptr,
      /*expected_has_alternative_input=*/false,
      /*expected_has_alternative_output=*/std::nullopt);

  EXPECT_TRUE(cras_audio_handler_->HasDualInternalMic());

  // Start the front facing camera.
  StartFrontFacingCamera();
  // Verify the front mic has been selected as the active input.
  EXPECT_EQ(kFrontMicId, cras_audio_handler_->GetPrimaryActiveInputNode());

  // Simulates the camera app switching from front camera to rear camera.
  StopFrontFacingCamera();
  StartRearFacingCamera();

  // Verify the rear mic has been selected as the active input.
  EXPECT_EQ(kRearMicId, cras_audio_handler_->GetPrimaryActiveInputNode());
}

TEST_P(CrasAudioHandlerTest, StartFrontCameraWithActiveExternalInput) {
  // Verify the mic Jack has been selected as the active input.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kFrontMic, kRearMic, kMicJack},
      /*expected_active_input_node=*/kMicJack,
      /*expected_active_output_node=*/nullptr,
      /*expected_has_alternative_input=*/true,
      /*expected_has_alternative_output=*/std::nullopt);

  EXPECT_TRUE(cras_audio_handler_->HasDualInternalMic());

  // Start the front facing camera.
  StartFrontFacingCamera();
  // Verify the mic Jack has been selected as the active input.
  EXPECT_EQ(kMicJackId, cras_audio_handler_->GetPrimaryActiveInputNode());

  // Stop the front facing camera.
  StopFrontFacingCamera();
  // Verify the mic Jack remains as the active input.
  EXPECT_EQ(kMicJackId, cras_audio_handler_->GetPrimaryActiveInputNode());
}

TEST_P(CrasAudioHandlerTest, StartFrontCameraWithInactiveExternalInput) {
  // Verify the mic Jack has been selected as the active input.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kFrontMic, kRearMic, kMicJack},
      /*expected_active_input_node=*/kMicJack,
      /*expected_active_output_node=*/nullptr,
      /*expected_has_alternative_input=*/true,
      /*expected_has_alternative_output=*/std::nullopt);

  EXPECT_TRUE(cras_audio_handler_->HasDualInternalMic());

  // Change the active input to internal mic.
  cras_audio_handler_->SwitchToFrontOrRearMic();
  // Verify the active input has been switched to front mic.
  EXPECT_EQ(kFrontMicId, cras_audio_handler_->GetPrimaryActiveInputNode());

  // Start the front facing camera.
  StartFrontFacingCamera();
  // Verify the front mic has been selected as the active input.
  EXPECT_EQ(kFrontMicId, cras_audio_handler_->GetPrimaryActiveInputNode());

  // Stop the front facing camera.
  StopFrontFacingCamera();
  // Verify the active input remains as front mic.
  EXPECT_EQ(kFrontMicId, cras_audio_handler_->GetPrimaryActiveInputNode());
}

TEST_P(CrasAudioHandlerTest, StartFrontCameraWithoutDualMic) {
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalMic},
      /*expected_active_input_node=*/kInternalMic,
      /*expected_active_output_node=*/nullptr,
      /*expected_has_alternative_input=*/false,
      /*expected_has_alternative_output=*/std::nullopt);

  EXPECT_FALSE(cras_audio_handler_->HasDualInternalMic());

  // Start the front facing camera.
  StartFrontFacingCamera();
  // Verify the internal mic has been selected as the active input.
  EXPECT_EQ(kInternalMicId, cras_audio_handler_->GetPrimaryActiveInputNode());

  // Stop the front facing camera.
  StopFrontFacingCamera();
  // Verify the active input remains as interanl mic.
  EXPECT_EQ(kInternalMicId, cras_audio_handler_->GetPrimaryActiveInputNode());
}

TEST_P(CrasAudioHandlerTest, FrontRearCameraBothOn) {
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kFrontMic, kRearMic},
      /*expected_active_input_node=*/kFrontMic,
      /*expected_active_output_node=*/nullptr,
      /*expected_has_alternative_input=*/false,
      /*expected_has_alternative_output=*/std::nullopt);

  EXPECT_TRUE(cras_audio_handler_->HasDualInternalMic());

  // Start the rear facing camera.
  StartRearFacingCamera();
  // Verify the rear mic has been selected as the active input.
  EXPECT_EQ(kRearMicId, cras_audio_handler_->GetPrimaryActiveInputNode());

  // Start front camera without stopping the front camera.
  StartFrontFacingCamera();
  // Verify the active microphone does not change.
  EXPECT_EQ(kRearMicId, cras_audio_handler_->GetPrimaryActiveInputNode());

  // Stop the front mic.
  StopFrontFacingCamera();
  // Verity the active mic does not change when there is still camera on.
  EXPECT_EQ(kRearMicId, cras_audio_handler_->GetPrimaryActiveInputNode());

  // Stop the rear mic.
  StopRearFacingCamera();
  // Verify the actice mic changes to front mic after both cameras stop.
  EXPECT_EQ(kFrontMicId, cras_audio_handler_->GetPrimaryActiveInputNode());
}

TEST_P(CrasAudioHandlerTest,
       PlugUSBMicWhichIsInactiveInPrefsWithAnAlreadyActiveUSBMic) {
  // Set up initial audio devices.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kUSBMic1, kInternalMic},
      /*expected_active_input_node=*/kUSBMic1,
      /*expected_active_output_node=*/nullptr,
      /*expected_has_alternative_input=*/true,
      /*expected_has_alternative_output=*/std::nullopt);

  // Plug the second USB mic.
  AudioNodeList audio_nodes;
  AudioNode internal_mic(GenerateAudioNode(kInternalMic));
  AudioNode usb_mic1(GenerateAudioNode(kUSBMic1));
  usb_mic1.active = true;
  usb_mic1.plugged_time = 1000;
  AudioNode usb_mic2 = GenerateAudioNode(kUSBMic2);
  audio_pref_handler_->SetDeviceActive(AudioDevice(usb_mic2), false, false);
  usb_mic2.active = false;
  usb_mic2.plugged_time = 2000;
  audio_nodes.push_back(internal_mic);
  audio_nodes.push_back(usb_mic1);
  audio_nodes.push_back(usb_mic2);
  ChangeAudioNodes(audio_nodes);

  // Verify that system monitor is notified, since we must update the cache of
  // enumerated devices even if the active device isn't changed.
  VerifySystemMonitorWasCalled();

  // Verify the AudioNodesChanged event is fired.
  EXPECT_EQ(1, test_observer_->audio_nodes_changed_count());
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(3u, audio_devices.size());

  // Verify the active input device is changed to usb mic 2.
  EXPECT_EQ(1, test_observer_->active_input_node_changed_count());
  EXPECT_EQ(kUSBMicId2, cras_audio_handler_->GetPrimaryActiveInputNode());
  EXPECT_TRUE(cras_audio_handler_->has_alternative_input());
}

TEST_P(
    CrasAudioHandlerTest,
    PlugUSBMicWhichIsInactiveInPrefsWithAnAlreadyActiveUSBMic_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);

  // Set up initial audio devices.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kUSBMic1, kInternalMic},
      /*expected_active_input_node=*/kInternalMic,
      /*expected_active_output_node=*/nullptr,
      /*expected_has_alternative_input=*/true,
      /*expected_has_alternative_output=*/std::nullopt);

  // Plug the second USB mic.
  AudioNodeList audio_nodes;
  AudioNode internal_mic(GenerateAudioNode(kInternalMic));
  internal_mic.active = true;
  AudioNode usb_mic1(GenerateAudioNode(kUSBMic1));
  usb_mic1.plugged_time = 1000;
  AudioNode usb_mic2 = GenerateAudioNode(kUSBMic2);
  audio_pref_handler_->SetDeviceActive(AudioDevice(usb_mic2), false, false);
  usb_mic2.active = false;
  usb_mic2.plugged_time = 2000;
  audio_nodes.push_back(internal_mic);
  audio_nodes.push_back(usb_mic1);
  audio_nodes.push_back(usb_mic2);
  ChangeAudioNodes(audio_nodes);

  // Verify that system monitor is notified, since we must update the cache of
  // enumerated devices even if the active device isn't changed.
  VerifySystemMonitorWasCalled();

  // Verify the AudioNodesChanged event is fired.
  EXPECT_EQ(1, test_observer_->audio_nodes_changed_count());
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(3u, audio_devices.size());

  // Verify the active input device is not changed to usb mic 2.
  EXPECT_EQ(0, test_observer_->active_input_node_changed_count());
  EXPECT_NE(kUSBMicId2, cras_audio_handler_->GetPrimaryActiveInputNode());
  EXPECT_TRUE(cras_audio_handler_->has_alternative_input());
}

TEST_P(CrasAudioHandlerTest, PlugInUSBHeadphoneAfterLastUnplugNotActive) {
  // Set up initial audio devices.
  AudioNodeList audio_nodes =
      GenerateAudioNodeList({kInternalSpeaker, kHeadphone, kUSBHeadphone1});
  SetUpCrasAudioHandler(audio_nodes);

  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(3u, audio_devices.size());
  // 35mm Headphone is active, but USB headphone is not.
  EXPECT_EQ(kHeadphone->id, cras_audio_handler_->GetPrimaryActiveOutputNode());

  // Unplug both 35mm headphone and USB headphone.
  audio_nodes.clear();
  audio_nodes.push_back(GenerateAudioNode(kInternalSpeaker));
  ChangeAudioNodes(audio_nodes);
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(1u, audio_devices.size());
  // Internal speaker is active.
  EXPECT_EQ(kInternalSpeaker->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());

  // Plug in USB headphone.
  audio_nodes.clear();
  AudioNode internal_speaker = GenerateAudioNode(kInternalSpeaker);
  internal_speaker.active = true;
  audio_nodes.push_back(internal_speaker);
  AudioNode usb_headphone = GenerateAudioNode(kUSBHeadphone1);
  usb_headphone.plugged_time = 80000000;
  audio_nodes.push_back(usb_headphone);
  ChangeAudioNodes(audio_nodes);
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(2u, audio_devices.size());
  // USB headphone is active.
  EXPECT_EQ(kUSBHeadphone1->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());
}

TEST_P(
    CrasAudioHandlerTest,
    PlugInUSBHeadphoneAfterLastUnplugNotActive_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);

  // Set up initial audio devices.
  AudioNodeList audio_nodes =
      GenerateAudioNodeList({kInternalSpeaker, kHeadphone, kUSBHeadphone1});
  SetUpCrasAudioHandler(audio_nodes);

  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(3u, audio_devices.size());
  // 35mm Headphone is active, but USB headphone is not.
  EXPECT_EQ(kHeadphone->id, cras_audio_handler_->GetPrimaryActiveOutputNode());

  // Unplug both 35mm headphone and USB headphone.
  audio_nodes.clear();
  audio_nodes.push_back(GenerateAudioNode(kInternalSpeaker));
  ChangeAudioNodes(audio_nodes);

  // Internal speaker is active.
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(1u, audio_devices.size());
  EXPECT_EQ(kInternalSpeaker->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());

  // Plug in USB headphone.
  audio_nodes.clear();
  AudioNode internal_speaker = GenerateAudioNode(kInternalSpeaker);
  internal_speaker.active = true;
  audio_nodes.push_back(internal_speaker);
  AudioNode usb_headphone = GenerateAudioNode(kUSBHeadphone1);
  usb_headphone.plugged_time = 80000000;
  audio_nodes.push_back(usb_headphone);
  ChangeAudioNodes(audio_nodes);

  // USB headphone is not active since the set of USB headphone and internal
  // speaker is not seen before.
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(2u, audio_devices.size());
  EXPECT_NE(kUSBHeadphone1->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());
}

TEST_P(CrasAudioHandlerTest, SuspendAllSessionsForOutput) {
  // Set up initial output audio devices, with internal speaker, headphone and
  // USBHeadphone.
  // Verify the headphone has been selected as the active output.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker, kHeadphone, kUSBHeadphone1},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kHeadphone,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/true);

  // Unplug USBHeadphone should not suspend media sessions.
  EXPECT_CALL(*fake_manager_.get(), SuspendAllSessions).Times(0);
  AudioNodeList audio_nodes =
      GenerateAudioNodeList({kInternalSpeaker, kHeadphone});
  ChangeAudioNodes(audio_nodes);

  // Unplug active output device should suspend all media sessions.
  EXPECT_CALL(*fake_manager_.get(), SuspendAllSessions).Times(1);
  audio_nodes.clear();
  audio_nodes.push_back(GenerateAudioNode(kInternalSpeaker));
  ChangeAudioNodes(audio_nodes);
}

TEST_P(CrasAudioHandlerTest, SuspendAllSessionsForInput) {
  // Set up initial input audio devices, with internal mic and mic jack.
  // Verify the mic jack has been selected as the active input.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalMic, kMicJack},
      /*expected_active_input_node=*/kMicJack,
      /*expected_active_output_node=*/nullptr,
      /*expected_has_alternative_input=*/true,
      /*expected_has_alternative_output=*/std::nullopt);

  // Unplug active input device should not suspend media sessions.
  EXPECT_CALL(*fake_manager_.get(), SuspendAllSessions).Times(0);
  AudioNodeList audio_nodes;
  audio_nodes.push_back(GenerateAudioNode(kInternalMic));
  ChangeAudioNodes(audio_nodes);
}

TEST_P(CrasAudioHandlerTest, MicrophoneMuteHwSwitchMutesInput) {
  // Set up initial input audio devices, with internal mic and mic jack.
  AudioNodeList audio_nodes = GenerateAudioNodeList({kInternalMic, kMicJack});
  SetUpCrasAudioHandler(audio_nodes);

  // Simulate hw microphone mute switch toggle.
  ui::MicrophoneMuteSwitchMonitor::Get()->SetMicrophoneMuteSwitchValue(
      /*muted=*/true);

  // Verify the input is muted, OnInputMuteChanged event is fired.
  EXPECT_TRUE(cras_audio_handler_->IsInputMuted());
  EXPECT_EQ(1, test_observer_->input_mute_changed_count());

  // Unmuting input while the hw mute switch is on should fail.
  cras_audio_handler_->SetInputMute(
      false, CrasAudioHandler::InputMuteChangeMethod::kOther);

  EXPECT_TRUE(cras_audio_handler_->IsInputMuted());
  EXPECT_EQ(1, test_observer_->input_mute_changed_count());

  // Verify the input is unmuted if the hw mute switch is toggled again.
  ui::MicrophoneMuteSwitchMonitor::Get()->SetMicrophoneMuteSwitchValue(
      /*muted=*/false);
  EXPECT_FALSE(cras_audio_handler_->IsInputMuted());
  EXPECT_EQ(2, test_observer_->input_mute_changed_count());
}

TEST_P(CrasAudioHandlerTest, MicrophoneMuteSwitchToggledBeforeHandlerSetup) {
  // Simulate hw microphone mute switch toggle.
  ui::MicrophoneMuteSwitchMonitor::Get()->SetMicrophoneMuteSwitchValue(
      /*muted=*/true);

  // Set up initial input audio devices, with internal mic and mic jack.
  AudioNodeList audio_nodes = GenerateAudioNodeList({kInternalMic, kMicJack});
  SetUpCrasAudioHandler(audio_nodes);

  // Verify the input is muted, OnInputMuteChanged event is fired.
  EXPECT_TRUE(cras_audio_handler_->IsInputMuted());
  EXPECT_EQ(0, test_observer_->input_mute_changed_count());

  // Unmuting input while the hw mute switch is on should fail.
  cras_audio_handler_->SetInputMute(
      false, CrasAudioHandler::InputMuteChangeMethod::kOther);

  EXPECT_TRUE(cras_audio_handler_->IsInputMuted());
  EXPECT_EQ(0, test_observer_->input_mute_changed_count());

  // Verify the input is unmuted if the hw mute switch is toggled again.
  ui::MicrophoneMuteSwitchMonitor::Get()->SetMicrophoneMuteSwitchValue(
      /*muted=*/false);
  EXPECT_FALSE(cras_audio_handler_->IsInputMuted());
  EXPECT_EQ(1, test_observer_->input_mute_changed_count());
}

TEST_P(CrasAudioHandlerTest, HasActiveInputDeviceForSimpleUsage) {
  using TestCaseWithDescription =
      std::map<std::vector<const AudioNodeInfo*>, const char*>;

  const TestCaseWithDescription positive_cases{
      {{kMicJack}, "A simple input node connected."},
      {{kMicJack, kInternalSpeaker},
       "A simple input node and a simple output node connected."},
      {{kKeyboardMic, kMicJack},
       "A non simple input node and a simple input node connected."},
      {{kInternalSpeaker, kOther, kMicJack, kKeyboardMic},
       "All types of audio nodes connected."}};
  const TestCaseWithDescription negative_cases{
      {{kInternalSpeaker}, "A simple output node connected."},
      {{kKeyboardMic}, "A non simple input node connected."},
      {{kInternalSpeaker, kKeyboardMic},
       "A simple output node and a non simple input node connected."},
      {{kOther}, "A non simple output node connected."}};

  ASSERT_FALSE(AudioDevice(GenerateAudioNode(kOther)).is_for_simple_usage());

  // Set up audio handler with empty audio_nodes.
  AudioNodeList audio_nodes;
  SetUpCrasAudioHandler(audio_nodes);

  EXPECT_FALSE(cras_audio_handler_->HasActiveInputDeviceForSimpleUsage())
      << "No audio nodes connected.";

  for (const auto& [audio_node_infos, description] : positive_cases) {
    audio_nodes = GenerateAudioNodeList(audio_node_infos);
    ChangeAudioNodes(audio_nodes);
    EXPECT_TRUE(cras_audio_handler_->HasActiveInputDeviceForSimpleUsage())
        << description;
  }

  for (const auto& [audio_node_infos, description] : negative_cases) {
    audio_nodes = GenerateAudioNodeList(audio_node_infos);
    ChangeAudioNodes(audio_nodes);
    EXPECT_FALSE(cras_audio_handler_->HasActiveInputDeviceForSimpleUsage())
        << description;
  }
}

TEST_P(CrasAudioHandlerTest, ShouldBeForcefullyMutedByAudioPolicy) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({kInternalSpeaker});
  SetUpCrasAudioHandler(audio_nodes);

  for (bool previous_value : {true, false}) {
    cras_audio_handler_->SetOutputMute(previous_value);

    audio_pref_handler_->SetAudioOutputAllowedValue(false);
    EXPECT_TRUE(cras_audio_handler_->IsOutputMutedByPolicy());
    EXPECT_TRUE(cras_audio_handler_->IsOutputMuted());

    audio_pref_handler_->SetAudioOutputAllowedValue(true);
    EXPECT_FALSE(cras_audio_handler_->IsOutputMutedByPolicy());
    EXPECT_EQ(cras_audio_handler_->IsOutputMuted(), previous_value);
  }
}

TEST_P(CrasAudioHandlerTest, ShouldBeForcefullyMutedBySecurityCurtainMode) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({kInternalSpeaker});
  SetUpCrasAudioHandler(audio_nodes);

  for (bool previous_value : {true, false}) {
    cras_audio_handler_->SetOutputMute(previous_value);

    cras_audio_handler_->SetOutputMuteLockedBySecurityCurtain(true);
    EXPECT_TRUE(cras_audio_handler_->IsOutputMutedBySecurityCurtain());
    EXPECT_TRUE(cras_audio_handler_->IsOutputMuted());

    cras_audio_handler_->SetOutputMuteLockedBySecurityCurtain(false);
    EXPECT_FALSE(cras_audio_handler_->IsOutputMutedBySecurityCurtain());
    EXPECT_EQ(cras_audio_handler_->IsOutputMuted(), previous_value);
  }
}

TEST_P(CrasAudioHandlerTest,
       ShouldNotBreakPolicyMutingByDisablingSecurityCurtain) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({kInternalSpeaker});
  SetUpCrasAudioHandler(audio_nodes);
  cras_audio_handler_->SetOutputMute(false);

  // Forced mute through a policy
  audio_pref_handler_->SetAudioOutputAllowedValue(false);

  // Then enable and disable forced mute through the security curtain.
  cras_audio_handler_->SetOutputMuteLockedBySecurityCurtain(true);
  cras_audio_handler_->SetOutputMuteLockedBySecurityCurtain(false);

  // The force mute through the policy should still be in effect.
  EXPECT_TRUE(cras_audio_handler_->IsOutputMuted());
  EXPECT_TRUE(cras_audio_handler_->IsOutputMutedByPolicy());
}

TEST_P(CrasAudioHandlerTest,
       ShouldNotBreakSecurityCurtainMutingByAudioPolicyChange) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({kInternalSpeaker});
  SetUpCrasAudioHandler(audio_nodes);
  cras_audio_handler_->SetOutputMute(false);

  // Forced mute by security curtain
  cras_audio_handler_->SetOutputMuteLockedBySecurityCurtain(true);

  // Then enable and disable mute through audio policy
  audio_pref_handler_->SetAudioOutputAllowedValue(false);
  audio_pref_handler_->SetAudioOutputAllowedValue(true);

  // The force mute through the policy should still be in effect.
  EXPECT_TRUE(cras_audio_handler_->IsOutputMuted());
  EXPECT_TRUE(cras_audio_handler_->IsOutputMutedBySecurityCurtain());
}

TEST_P(CrasAudioHandlerTest, MicrophoneMuteKeyboardSwitchTest) {
  // Set up initial input audio devices, with internal mic and mic jack.
  AudioNodeList audio_nodes = GenerateAudioNodeList({kInternalMic, kMicJack});
  SetUpCrasAudioHandler(audio_nodes);

  // Setting the hw toggle to off first.
  ui::MicrophoneMuteSwitchMonitor::Get()->SetMicrophoneMuteSwitchValue(
      /*switch_on=*/false);
  EXPECT_FALSE(cras_audio_handler_->IsInputMuted());

  int input_mute_changed_counter = test_observer_->input_mute_changed_count();

  // This is similar to pressing the keyboard button for toggling the microphone
  // mute state.
  cras_audio_handler_->SetInputMute(
      !cras_audio_handler_->IsInputMuted(),
      CrasAudioHandler::InputMuteChangeMethod::kKeyboardButton);

  // Verify the input is muted, OnInputMuteChanged event is fired.
  EXPECT_TRUE(cras_audio_handler_->IsInputMuted());
  EXPECT_EQ(++input_mute_changed_counter,
            test_observer_->input_mute_changed_count());

  // Setting the hw toggle on. Pressing the keyboard switch should not be able
  // to change the system input mute state as long as the hw switch is on.
  ui::MicrophoneMuteSwitchMonitor::Get()->SetMicrophoneMuteSwitchValue(
      /*switch_on=*/true);

  // Input should still be muted as it was before toggling the hw switch.
  // OnInputMuteChanged event should not be fired.
  EXPECT_TRUE(cras_audio_handler_->IsInputMuted());
  EXPECT_EQ(input_mute_changed_counter,
            test_observer_->input_mute_changed_count());

  // Trying to toggle system input mute state using the keyboard switch.
  cras_audio_handler_->SetInputMute(
      !cras_audio_handler_->IsInputMuted(),
      CrasAudioHandler::InputMuteChangeMethod::kKeyboardButton);

  // Input should still be muted. OnInputMuteChanged event should not be fired.
  EXPECT_TRUE(cras_audio_handler_->IsInputMuted());
  EXPECT_EQ(input_mute_changed_counter,
            test_observer_->input_mute_changed_count());
}

TEST_P(CrasAudioHandlerTest,
       InputShouldBeForcefullyMutedBySecurityCurtainMode) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({kInternalMic, kMicJack});
  SetUpCrasAudioHandler(audio_nodes);

  for (bool previous_value : {true, false}) {
    cras_audio_handler_->SetInputMute(
        previous_value,
        CrasAudioHandler::InputMuteChangeMethod::kKeyboardButton);

    // Force enable input muting.
    cras_audio_handler_->SetInputMuteLockedBySecurityCurtain(true);
    EXPECT_TRUE(cras_audio_handler_->IsInputMutedBySecurityCurtain());
    EXPECT_TRUE(cras_audio_handler_->IsInputMuted());

    // Trying to unmute should now be blocked.
    cras_audio_handler_->SetInputMute(
        false, CrasAudioHandler::InputMuteChangeMethod::kKeyboardButton);
    EXPECT_TRUE(cras_audio_handler_->IsInputMutedBySecurityCurtain());
    EXPECT_TRUE(cras_audio_handler_->IsInputMuted());

    // Stop force enabling input muting - the device should go to unmuted state.
    cras_audio_handler_->SetInputMuteLockedBySecurityCurtain(false);
    EXPECT_FALSE(cras_audio_handler_->IsInputMutedBySecurityCurtain());
    EXPECT_FALSE(cras_audio_handler_->IsInputMuted());
  }
}

TEST_P(CrasAudioHandlerTest,
       HwSwitchInputMutingShouldNotBeBrokenBySecurityCurtain) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({kInternalMic, kMicJack});
  SetUpCrasAudioHandler(audio_nodes);

  // Simulate hw microphone mute switch toggle.
  ui::MicrophoneMuteSwitchMonitor::Get()->SetMicrophoneMuteSwitchValue(true);

  // Now enabling and disabling security input muting should not unmute the
  // device

  cras_audio_handler_->SetInputMuteLockedBySecurityCurtain(true);
  EXPECT_TRUE(cras_audio_handler_->IsInputMuted());

  cras_audio_handler_->SetInputMuteLockedBySecurityCurtain(false);
  EXPECT_TRUE(cras_audio_handler_->IsInputMuted());
}

TEST_P(CrasAudioHandlerTest,
       SecurityCurtainInputMutingShouldNotBeBrokenByHwSwitch) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({kInternalMic, kMicJack});
  SetUpCrasAudioHandler(audio_nodes);

  // Force enable input muting through security curtain
  cras_audio_handler_->SetInputMuteLockedBySecurityCurtain(true);

  // Now toggling the hw microphone mute switch should not unmute the system.
  ui::MicrophoneMuteSwitchMonitor::Get()->SetMicrophoneMuteSwitchValue(true);
  EXPECT_TRUE(cras_audio_handler_->IsInputMuted());

  ui::MicrophoneMuteSwitchMonitor::Get()->SetMicrophoneMuteSwitchValue(false);
  EXPECT_TRUE(cras_audio_handler_->IsInputMuted());
}

TEST_P(CrasAudioHandlerTest, IsInputMutedBySecurityCurtainChangeObserver) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({kInternalMic, kMicJack});
  SetUpCrasAudioHandler(audio_nodes);

  EXPECT_EQ(0, test_observer_->input_muted_by_security_curtain_changed_count());

  cras_audio_handler_->SetInputMuteLockedBySecurityCurtain(true);
  EXPECT_EQ(1, test_observer_->input_muted_by_security_curtain_changed_count());

  // Security curtain is already on. Trying to turn it on again should be no-op.
  cras_audio_handler_->SetInputMuteLockedBySecurityCurtain(true);
  EXPECT_EQ(1, test_observer_->input_muted_by_security_curtain_changed_count());

  cras_audio_handler_->SetInputMuteLockedBySecurityCurtain(false);
  EXPECT_EQ(2, test_observer_->input_muted_by_security_curtain_changed_count());
}

TEST_P(CrasAudioHandlerTest, IsNoiseCancellationSupportedForDeviceNoNC) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({});
  // Set up initial audio devices, only with internal mic.
  AudioNode internalMic = GenerateAudioNode(kInternalMic);
  // Clear the audio effect, no Noise Cancellation supported.
  internalMic.audio_effect = 0u;
  audio_nodes.push_back(internalMic);
  // Disable noise cancellation for board.
  SetUpCrasAudioHandlerWithPrimaryActiveNodeAndNoiseCancellationState(
      audio_nodes, /*primary_active_node=*/audio_nodes[0],
      /*noise_cancellation_enabled=*/false);

  EXPECT_FALSE(cras_audio_handler_->IsNoiseCancellationSupportedForDevice(
      kInternalMicId));
}

TEST_P(CrasAudioHandlerTest, IsNoiseCancellationSupportedForDeviceWithNC) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({});
  AudioNode internalMic = GenerateAudioNode(kInternalMic);
  // Set the audio effect, Noise Cancellation supported.
  internalMic.audio_effect = cras::EFFECT_TYPE_NOISE_CANCELLATION;
  audio_nodes.push_back(internalMic);
  AudioNode micJack = GenerateAudioNode(kMicJack);
  // Clear the audio effect, no Noise Cancellation supported.
  micJack.audio_effect = 0u;
  audio_nodes.push_back(micJack);
  AudioNode internalSpeaker = GenerateAudioNode(kInternalSpeaker);
  // Force audio_effect value to verify output nodes always return false.
  internalSpeaker.audio_effect = cras::EFFECT_TYPE_NOISE_CANCELLATION;
  audio_nodes.push_back(internalSpeaker);

  // Enable noise cancellation for board.
  SetUpCrasAudioHandlerWithPrimaryActiveNodeAndNoiseCancellationState(
      audio_nodes, /*primary_active_node=*/audio_nodes[0],
      /*noise_cancellation_enabled=*/true);

  EXPECT_TRUE(cras_audio_handler_->IsNoiseCancellationSupportedForDevice(
      kInternalMicId));
  EXPECT_FALSE(
      cras_audio_handler_->IsNoiseCancellationSupportedForDevice(kMicJackId));
  EXPECT_FALSE(cras_audio_handler_->IsNoiseCancellationSupportedForDevice(
      kInternalSpeakerId));
}

TEST_P(CrasAudioHandlerTest,
       SetNoiseCancellationStateUpdatesAudioPrefAndClient) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({});
  AudioNode internalMic = GenerateAudioNode(kInternalMic);
  // Set the audio effect, Noise Cancellation supported.
  internalMic.audio_effect = cras::EFFECT_TYPE_NOISE_CANCELLATION;
  audio_nodes.push_back(internalMic);

  // Enable noise cancellation for board.
  SetUpCrasAudioHandlerWithPrimaryActiveNodeAndNoiseCancellationState(
      audio_nodes, /*primary_active_node=*/audio_nodes[0],
      /*noise_cancellation_enabled=*/true);

  EXPECT_TRUE(audio_pref_handler_->GetNoiseCancellationState());
  EXPECT_TRUE(fake_cras_audio_client()->noise_cancellation_enabled());

  // Turn off noise cancellation.
  cras_audio_handler_->SetNoiseCancellationState(
      /*noise_cancellation_on=*/false,
      CrasAudioHandler::AudioSettingsChangeSource::kSystemTray);

  EXPECT_FALSE(audio_pref_handler_->GetNoiseCancellationState());
  EXPECT_FALSE(fake_cras_audio_client()->noise_cancellation_enabled());
  histogram_tester_.ExpectBucketCount(
      CrasAudioHandler::kNoiseCancellationEnabledSourceHistogramName,
      CrasAudioHandler::AudioSettingsChangeSource::kSystemTray, 1);

  // Turn on noise cancellation.
  cras_audio_handler_->SetNoiseCancellationState(
      /*noise_cancellation_on=*/true,
      CrasAudioHandler::AudioSettingsChangeSource::kSystemTray);

  EXPECT_TRUE(audio_pref_handler_->GetNoiseCancellationState());
  EXPECT_TRUE(fake_cras_audio_client()->noise_cancellation_enabled());
  histogram_tester_.ExpectBucketCount(
      CrasAudioHandler::kNoiseCancellationEnabledSourceHistogramName,
      CrasAudioHandler::AudioSettingsChangeSource::kSystemTray, 2);
}

TEST_P(CrasAudioHandlerTest, SetNoiseCancellationStateObserver) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({});
  AudioNode internalMic = GenerateAudioNode(kInternalMic);
  // Set the audio effect, Noise Cancellation supported.
  internalMic.audio_effect = cras::EFFECT_TYPE_NOISE_CANCELLATION;
  audio_nodes.push_back(internalMic);

  // Enable noise cancellation for board.
  SetUpCrasAudioHandlerWithPrimaryActiveNodeAndNoiseCancellationState(
      audio_nodes, /*primary_active_node=*/audio_nodes[0],
      /*noise_cancellation_enabled=*/true);

  EXPECT_EQ(0, test_observer_->noise_cancellation_state_change_count());

  // Change noise cancellation state to trigger observer.
  cras_audio_handler_->SetNoiseCancellationState(
      false, CrasAudioHandler::AudioSettingsChangeSource::kSystemTray);

  EXPECT_EQ(1, test_observer_->noise_cancellation_state_change_count());
}

TEST_P(CrasAudioHandlerTest, IsStyleTransferSupportedForDevice) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({});

  AudioNode internalMic = GenerateAudioNode(kInternalMic);
  internalMic.audio_effect = cras::EFFECT_TYPE_STYLE_TRANSFER;
  audio_nodes.push_back(internalMic);

  AudioNode micJack = GenerateAudioNode(kMicJack);
  micJack.audio_effect = 0u;  // no style transfer supported.
  audio_nodes.push_back(micJack);

  SetUpCrasAudioHandlerWithPrimaryActiveNodeAndStyleTransferState(
      audio_nodes, /*primary_active_node=*/audio_nodes[0],
      /*style_transfer_enabled=*/true);

  EXPECT_TRUE(
      cras_audio_handler_->IsStyleTransferSupportedForDevice(kInternalMicId));
  EXPECT_FALSE(
      cras_audio_handler_->IsStyleTransferSupportedForDevice(kMicJackId));
}

TEST_P(CrasAudioHandlerTest, SetStyleTransferStateUpdatesAudioPrefAndClient) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({});

  AudioNode internalMic = GenerateAudioNode(kInternalMic);
  internalMic.audio_effect = cras::EFFECT_TYPE_STYLE_TRANSFER;
  audio_nodes.push_back(internalMic);

  // On.
  SetUpCrasAudioHandlerWithPrimaryActiveNodeAndStyleTransferState(
      audio_nodes, /*primary_active_node=*/audio_nodes[0],
      /*style_transfer_enabled=*/true);

  EXPECT_TRUE(audio_pref_handler_->GetStyleTransferState());
  EXPECT_TRUE(fake_cras_audio_client()->style_transfer_enabled());

  // Off.
  cras_audio_handler_->SetStyleTransferState(/*style_transfer_on=*/false);

  EXPECT_FALSE(audio_pref_handler_->GetStyleTransferState());
  EXPECT_FALSE(fake_cras_audio_client()->style_transfer_enabled());

  // On.
  cras_audio_handler_->SetStyleTransferState(/*style_transfer_on=*/true);

  EXPECT_TRUE(audio_pref_handler_->GetStyleTransferState());
  EXPECT_TRUE(fake_cras_audio_client()->style_transfer_enabled());
}

TEST_P(CrasAudioHandlerTest, SetStyleTransferStateObserver) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({});

  AudioNode internalMic = GenerateAudioNode(kInternalMic);
  internalMic.audio_effect = cras::EFFECT_TYPE_STYLE_TRANSFER;
  audio_nodes.push_back(internalMic);

  SetUpCrasAudioHandlerWithPrimaryActiveNodeAndStyleTransferState(
      audio_nodes, /*primary_active_node=*/audio_nodes[0],
      /*style_transfer_enabled=*/true);

  EXPECT_EQ(0, test_observer_->style_transfer_state_change_count());

  // Change style transfer state to trigger observer.
  cras_audio_handler_->SetStyleTransferState(false);

  EXPECT_EQ(1, test_observer_->style_transfer_state_change_count());
}

TEST_P(CrasAudioHandlerTest, IsHfpMicSrSupportedForDeviceNoHfpMicSr) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({});
  // Set up initial audio devices, only with bluetooth mic.
  AudioNode bt_nb_mic = GenerateAudioNode(kBluetoothNbMic);
  // Clear the audio effect, no hfp_mic_sr supported.
  bt_nb_mic.audio_effect = 0u;
  audio_nodes.push_back(bt_nb_mic);
  // Disable hfp_mic_sr for board.
  SetUpCrasAudioHandlerWithPrimaryActiveNodeAndHfpMicSrState(
      audio_nodes, /*primary_active_node=*/audio_nodes[0],
      /*hfp_mic_sr_enabled=*/false);

  EXPECT_FALSE(
      cras_audio_handler_->IsHfpMicSrSupportedForDevice(kBluetoothNbMicId));
}

TEST_P(CrasAudioHandlerTest, IsHfpMicSrSupportedForDeviceWithHfpMicSr) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({});
  AudioNode bt_nb_mic = GenerateAudioNode(kBluetoothNbMic);
  // Set the audio effect, hfp_mic_sr supported.
  bt_nb_mic.audio_effect = cras::EFFECT_TYPE_HFP_MIC_SR;
  audio_nodes.push_back(bt_nb_mic);
  AudioNode micJack = GenerateAudioNode(kMicJack);
  // Clear the audio effect, no hfp_mic_sr supported.
  micJack.audio_effect = 0u;
  audio_nodes.push_back(micJack);
  AudioNode internalSpeaker = GenerateAudioNode(kInternalSpeaker);
  // Force audio_effect value to verify output nodes always return false.
  internalSpeaker.audio_effect = cras::EFFECT_TYPE_HFP_MIC_SR;
  audio_nodes.push_back(internalSpeaker);

  // Enable hfp_mic_sr for board.
  SetUpCrasAudioHandlerWithPrimaryActiveNodeAndHfpMicSrState(
      audio_nodes, /*primary_active_node=*/audio_nodes[0],
      /*hfp_mic_sr_enabled=*/true);

  EXPECT_TRUE(
      cras_audio_handler_->IsHfpMicSrSupportedForDevice(kBluetoothNbMicId));
  EXPECT_FALSE(cras_audio_handler_->IsHfpMicSrSupportedForDevice(kMicJackId));
  EXPECT_FALSE(
      cras_audio_handler_->IsHfpMicSrSupportedForDevice(kInternalSpeakerId));
}

TEST_P(CrasAudioHandlerTest, SetHfpMicSrStateUpdatesAudioPrefAndClient) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({});
  AudioNode bt_nb_mic = GenerateAudioNode(kBluetoothNbMic);
  // Set the audio effect, hfp_mic_sr supported.
  bt_nb_mic.audio_effect = cras::EFFECT_TYPE_HFP_MIC_SR;
  audio_nodes.push_back(bt_nb_mic);

  // Enable hfp_mic_sr for board.
  SetUpCrasAudioHandlerWithPrimaryActiveNodeAndHfpMicSrState(
      audio_nodes, /*primary_active_node=*/audio_nodes[0],
      /*hfp_mic_sr_enabled=*/true);

  EXPECT_TRUE(audio_pref_handler_->GetHfpMicSrState());
  EXPECT_TRUE(fake_cras_audio_client()->hfp_mic_sr_enabled());

  // Turn off hfp_mic_sr.
  cras_audio_handler_->SetHfpMicSrState(
      /*hfp_mic_sr_on=*/false,
      CrasAudioHandler::AudioSettingsChangeSource::kSystemTray);

  EXPECT_FALSE(audio_pref_handler_->GetHfpMicSrState());
  EXPECT_FALSE(fake_cras_audio_client()->hfp_mic_sr_enabled());

  // Turn on hfp_mic_sr.
  cras_audio_handler_->SetHfpMicSrState(
      /*hfp_mic_sr_on=*/true,
      CrasAudioHandler::AudioSettingsChangeSource::kSystemTray);

  EXPECT_TRUE(audio_pref_handler_->GetHfpMicSrState());
  EXPECT_TRUE(fake_cras_audio_client()->hfp_mic_sr_enabled());
}

TEST_P(CrasAudioHandlerTest, SetHfpMicSrStateObserver) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({});
  AudioNode bt_nb_mic = GenerateAudioNode(kBluetoothNbMic);
  // Set the audio effect, hfp_mic_sr supported.
  bt_nb_mic.audio_effect = cras::EFFECT_TYPE_HFP_MIC_SR;
  audio_nodes.push_back(bt_nb_mic);

  // Enable hfp_mic_sr for board.
  SetUpCrasAudioHandlerWithPrimaryActiveNodeAndHfpMicSrState(
      audio_nodes, /*primary_active_node=*/audio_nodes[0],
      /*hfp_mic_sr_enabled=*/true);

  EXPECT_EQ(0, test_observer_->hfp_mic_sr_state_change_count());

  // Change hfp_mic_sr state to trigger observer.
  cras_audio_handler_->SetHfpMicSrState(
      false, CrasAudioHandler::AudioSettingsChangeSource::kSystemTray);

  EXPECT_EQ(1, test_observer_->hfp_mic_sr_state_change_count());
}

TEST_P(CrasAudioHandlerTest, SpeakOnMuteDetectionSwitchTest) {
  AudioNodeList audio_nodes = GenerateAudioNodeList({});
  // Set up initial audio devices, only with internal mic.
  AudioNode internalMic = GenerateAudioNode(kInternalMic);
  audio_nodes.push_back(internalMic);
  SetUpCrasAudioHandlerWithPrimaryActiveNode(audio_nodes, internalMic);

  // Speak-on-mute detection should still be disabled since it is not set during
  // the initialization.
  EXPECT_FALSE(fake_cras_audio_client()->speak_on_mute_detection_enabled());

  // Simulate enable speak-on-mute detection from handler, which should enable
  // speak-on-mute detection in the client.
  cras_audio_handler_->SetSpeakOnMuteDetection(/*som_on=*/true);
  EXPECT_TRUE(fake_cras_audio_client()->speak_on_mute_detection_enabled());

  // Simulate disable speak-on-mute detection from handler, which should disable
  // speak-on-mute detection in the client.
  cras_audio_handler_->SetSpeakOnMuteDetection(/*som_on=*/false);
  EXPECT_FALSE(fake_cras_audio_client()->speak_on_mute_detection_enabled());
}

TEST_P(CrasAudioHandlerTest, AudioSurveyDefault) {
  AudioNodeList audio_nodes = GenerateAudioNodeList(
      {kInternalSpeaker, kHeadphone, kInternalMic, kUSBMic1});
  base::flat_map<std::string, std::string> survey_specific_data = {
      {"key", "value"}};

  SetUpCrasAudioHandler(audio_nodes);

  // Simulate an audio survey gets triggered.
  fake_cras_audio_client()->NotifySurveyTriggered(survey_specific_data);

  EXPECT_EQ(test_observer_->survey_triggerd_count(), 1);
  EXPECT_EQ(test_observer_->survey_triggerd_recv().type(),
            CrasAudioHandler::SurveyType::kGeneral);
  EXPECT_THAT(test_observer_->survey_triggerd_recv().data(),
              testing::ElementsAre(std::make_pair("key", "value")));
}

TEST_P(CrasAudioHandlerTest, AudioSurveyBluetooth) {
  AudioNodeList audio_nodes = GenerateAudioNodeList(
      {kInternalSpeaker, kHeadphone, kInternalMic, kUSBMic1});
  base::flat_map<std::string, std::string> survey_specific_data = {
      {CrasAudioHandler::kSurveyNameKey,
       CrasAudioHandler::kSurveyNameBluetooth}};

  SetUpCrasAudioHandler(audio_nodes);

  // Simulate an audio survey gets triggered.
  fake_cras_audio_client()->NotifySurveyTriggered(survey_specific_data);

  EXPECT_EQ(test_observer_->survey_triggerd_count(), 1);
  EXPECT_EQ(test_observer_->survey_triggerd_recv().type(),
            CrasAudioHandler::SurveyType::kBluetooth);
  EXPECT_EQ(test_observer_->survey_triggerd_recv().data().size(), 0u);
}

TEST_P(CrasAudioHandlerTest, SimpleUsageAudioDevices) {
  // Set up initial audio devices, only with internal speaker and mic.
  AudioNodeList audio_nodes =
      GenerateAudioNodeList({kInternalSpeaker, kInternalMic});
  SetUpCrasAudioHandler(audio_nodes);

  uint32_t previous_input_count = 1u;
  uint32_t previous_output_count = 1u;
  uint32_t input_count = 1u;
  uint32_t output_count = 1u;

  // Verify the audio devices size.
  AudioDeviceList input_devices =
      cras_audio_handler_->GetSimpleUsageAudioDevices(
          GetAudioDeviceMap(/*is_current_device=*/true),
          /*is_input=*/true);
  AudioDeviceList output_devices =
      cras_audio_handler_->GetSimpleUsageAudioDevices(
          GetAudioDeviceMap(/*is_current_device=*/true),
          /*is_input=*/false);
  EXPECT_EQ(input_count, input_devices.size());
  EXPECT_EQ(output_count, output_devices.size());

  // Plug the headphone.
  audio_nodes = GenerateAudioNodeList(
      {kInternalSpeaker, kInternalMic, kHeadphone, kMicJack});
  ChangeAudioNodes(audio_nodes);

  // Verify the audio devices size.
  input_devices = cras_audio_handler_->GetSimpleUsageAudioDevices(
      GetAudioDeviceMap(/*is_current_device=*/true),
      /*is_input=*/true);
  output_devices = cras_audio_handler_->GetSimpleUsageAudioDevices(
      GetAudioDeviceMap(/*is_current_device=*/true),
      /*is_input=*/false);
  EXPECT_EQ(++input_count, input_devices.size());
  EXPECT_EQ(++output_count, output_devices.size());

  AudioDeviceList previous_input_devices =
      cras_audio_handler_->GetSimpleUsageAudioDevices(
          GetAudioDeviceMap(/*is_current_device=*/false),
          /*is_input=*/true);
  AudioDeviceList previous_output_devices =
      cras_audio_handler_->GetSimpleUsageAudioDevices(
          GetAudioDeviceMap(/*is_current_device=*/false),
          /*is_input=*/false);
  EXPECT_EQ(previous_input_count, previous_input_devices.size());
  EXPECT_EQ(previous_output_count, previous_output_devices.size());

  // Unplug the headphone.
  audio_nodes = GenerateAudioNodeList({kInternalSpeaker, kInternalMic});
  ChangeAudioNodes(audio_nodes);

  // Verify the audio devices size.
  input_devices = cras_audio_handler_->GetSimpleUsageAudioDevices(
      GetAudioDeviceMap(/*is_current_device=*/true),
      /*is_input=*/true);
  output_devices = cras_audio_handler_->GetSimpleUsageAudioDevices(
      GetAudioDeviceMap(/*is_current_device=*/true),
      /*is_input=*/false);
  EXPECT_EQ(--input_count, input_devices.size());
  EXPECT_EQ(--output_count, output_devices.size());

  previous_input_devices = cras_audio_handler_->GetSimpleUsageAudioDevices(
      GetAudioDeviceMap(/*is_current_device=*/false),
      /*is_input=*/true);
  previous_output_devices = cras_audio_handler_->GetSimpleUsageAudioDevices(
      GetAudioDeviceMap(/*is_current_device=*/false),
      /*is_input=*/false);
  EXPECT_EQ(++previous_input_count, previous_input_devices.size());
  EXPECT_EQ(++previous_output_count, previous_output_devices.size());

  // Plug a non simple usage device, which should not be counted.
  audio_nodes =
      GenerateAudioNodeList({kInternalSpeaker, kInternalMic, kKeyboardMic});
  ChangeAudioNodes(audio_nodes);

  // Verify the audio devices size.
  input_devices = cras_audio_handler_->GetSimpleUsageAudioDevices(
      GetAudioDeviceMap(/*is_current_device=*/true),
      /*is_input=*/true);
  output_devices = cras_audio_handler_->GetSimpleUsageAudioDevices(
      GetAudioDeviceMap(/*is_current_device=*/true),
      /*is_input=*/false);
  EXPECT_EQ(input_count, input_devices.size());
  EXPECT_EQ(output_count, output_devices.size());

  previous_input_devices = cras_audio_handler_->GetSimpleUsageAudioDevices(
      GetAudioDeviceMap(/*is_current_device=*/false),
      /*is_input=*/true);
  previous_output_devices = cras_audio_handler_->GetSimpleUsageAudioDevices(
      GetAudioDeviceMap(/*is_current_device=*/false),
      /*is_input=*/false);
  EXPECT_EQ(--previous_input_count, previous_input_devices.size());
  EXPECT_EQ(--previous_output_count, previous_output_devices.size());
}

// Tests audio selection exception rule #3 metrics are fired that hot plugging
// an unpreferred device keeps current active device unchanged.
TEST_P(
    CrasAudioHandlerTest,
    AudioSelectionExceptionRule3MetricsFired_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);

  // Set up initial audio devices.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker, kInternalMic},
      /*expected_active_input_node=*/kInternalMic,
      /*expected_active_output_node=*/kInternalSpeaker,
      /*expected_has_alternative_input=*/false,
      /*expected_has_alternative_output=*/false);

  // Plug a HDMI output device. Expect active device remains unchanged as
  // internal speaker.
  AudioNodeList audio_nodes;
  AudioNode internal_speaker = GenerateAudioNode(kInternalSpeaker);
  internal_speaker.active = true;
  audio_nodes.push_back(internal_speaker);

  AudioNode internal_mic = GenerateAudioNode(kInternalMic);
  internal_mic.active = true;
  audio_nodes.push_back(internal_mic);

  AudioNode hdmi_output = GenerateAudioNode(kHDMIOutput);
  audio_nodes.push_back(hdmi_output);
  system_monitor_observer_.reset_count();
  ChangeAudioNodes(audio_nodes);

  EXPECT_EQ(0, test_observer_->active_output_node_changed_count());
  AudioDevice active_output;
  EXPECT_TRUE(
      cras_audio_handler_->GetPrimaryActiveOutputDevice(&active_output));
  EXPECT_EQ(kInternalSpeaker->id, active_output.id);
  EXPECT_EQ(kInternalSpeaker->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());

  // Plug a USB output device. Expect active device remains unchanged as
  // internal speaker.
  AudioNode usb_output = GenerateAudioNode(kUSBHeadphone1);
  audio_nodes.push_back(usb_output);
  ChangeAudioNodes(audio_nodes);

  EXPECT_EQ(0, test_observer_->active_output_node_changed_count());
  EXPECT_TRUE(
      cras_audio_handler_->GetPrimaryActiveOutputDevice(&active_output));
  EXPECT_EQ(kInternalSpeaker->id, active_output.id);
  EXPECT_EQ(kInternalSpeaker->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());

  // Activate USB output device. Expect active device is the USB output device.
  // Now USB is the preferred device among USB, internal and HDMI device.
  AudioDevice usb_output_device(usb_output);
  cras_audio_handler_->SwitchToDevice(usb_output_device, true,
                                      DeviceActivateType::kActivateByUser);

  EXPECT_EQ(1, test_observer_->active_output_node_changed_count());
  EXPECT_TRUE(
      cras_audio_handler_->GetPrimaryActiveOutputDevice(&active_output));
  EXPECT_EQ(kUSBHeadphone1->id, active_output.id);
  EXPECT_EQ(kUSBHeadphone1->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());

  // Unplug HDMI output device.
  audio_nodes.clear();
  audio_nodes.push_back(internal_speaker);
  audio_nodes.push_back(internal_mic);
  audio_nodes.push_back(usb_output);
  ChangeAudioNodes(audio_nodes);

  EXPECT_EQ(2, test_observer_->active_output_node_changed_count());

  // Activate internal speaker. Expect active device is the internal speaker.
  AudioDevice internal_speaker_device(internal_speaker);
  cras_audio_handler_->SwitchToDevice(internal_speaker_device, true,
                                      DeviceActivateType::kActivateByUser);

  EXPECT_EQ(3, test_observer_->active_output_node_changed_count());
  EXPECT_TRUE(
      cras_audio_handler_->GetPrimaryActiveOutputDevice(&active_output));
  EXPECT_EQ(kInternalSpeaker->id, active_output.id);
  EXPECT_EQ(kInternalSpeaker->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());

  // Plug HDMI output device. Expect active device remains unchanged even though
  // USB is the preferred device among USB,internal and HDMI device. Expect
  // exception rule #3 metric is fired.
  audio_nodes.push_back(hdmi_output);
  ChangeAudioNodes(audio_nodes);

  EXPECT_EQ(3, test_observer_->active_output_node_changed_count());
  EXPECT_TRUE(
      cras_audio_handler_->GetPrimaryActiveOutputDevice(&active_output));
  EXPECT_EQ(kInternalSpeaker->id, active_output.id);
  EXPECT_EQ(kInternalSpeaker->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());

  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      AudioDeviceMetricsHandler::AudioSelectionExceptionRules::
          kOutputRule3HotPlugUnpreferredDevice,
      /*expected_count=*/1);
}

// Tests audio selection performance metrics of system not switching output
// device are fired when hot plugging an unpreferred device.
TEST_P(
    CrasAudioHandlerTest,
    AudioSelectionPerformanceSystemNotSwitchingOutput_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);

  // Set up initial audio devices.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kInternalSpeaker,
      /*expected_has_alternative_input=*/false,
      /*expected_has_alternative_output=*/false);

  // Plug a HDMI output device. Expect active device remains unchanged as
  // internal speaker.
  AudioNodeList audio_nodes;
  AudioNode internal_speaker = GenerateAudioNode(kInternalSpeaker);
  internal_speaker.active = true;
  audio_nodes.push_back(internal_speaker);

  AudioNode hdmi_output = GenerateAudioNode(kHDMIOutput);
  audio_nodes.push_back(hdmi_output);
  ChangeAudioNodes(audio_nodes);

  EXPECT_EQ(0, test_observer_->active_output_node_changed_count());
  AudioDevice active_output;
  EXPECT_TRUE(
      cras_audio_handler_->GetPrimaryActiveOutputDevice(&active_output));
  EXPECT_EQ(kInternalSpeaker->id, active_output.id);
  EXPECT_EQ(kInternalSpeaker->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());

  // At this moment, system has seen the device set of internal speaker and HDMI
  // output, and the preferable device is internal speaker.
  // Unplug HDMI output device.
  audio_nodes.clear();
  audio_nodes.push_back(internal_speaker);
  ChangeAudioNodes(audio_nodes);

  // No audio selection performance metrics are fired.
  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionPerformance,
      AudioDeviceMetricsHandler::AudioSelectionEvents::kSystemNotSwitchOutput,
      /*expected_count=*/0);
  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionPerformance,
      AudioDeviceMetricsHandler::AudioSelectionEvents::
          kSystemNotSwitchOutputNonChromeRestart,
      /*expected_count=*/0);
  histogram_tester_.ExpectTotalCount(
      AudioDeviceMetricsHandler::kAudioSelectionPerformance,
      /*expected_count=*/0);

  // Plug HDMI output device. Expect active device remains unchanged. Expect
  // audio selection performance metric of system not switching device is fired.
  audio_nodes.push_back(hdmi_output);
  ChangeAudioNodes(audio_nodes);

  EXPECT_EQ(0, test_observer_->active_output_node_changed_count());
  EXPECT_TRUE(
      cras_audio_handler_->GetPrimaryActiveOutputDevice(&active_output));
  EXPECT_EQ(kInternalSpeaker->id, active_output.id);
  EXPECT_EQ(kInternalSpeaker->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());

  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionPerformance,
      AudioDeviceMetricsHandler::AudioSelectionEvents::kSystemNotSwitchOutput,
      /*expected_count=*/1);
  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionPerformance,
      AudioDeviceMetricsHandler::AudioSelectionEvents::
          kSystemNotSwitchOutputNonChromeRestart,
      /*expected_count=*/1);
  histogram_tester_.ExpectTotalCount(
      AudioDeviceMetricsHandler::kAudioSelectionPerformance,
      /*expected_count=*/2);
}

// Tests audio selection performance metrics of system not switching input
// device are fired when hot plugging an unpreferred device.
TEST_P(
    CrasAudioHandlerTest,
    AudioSelectionPerformanceSystemNotSwitchingInput_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);

  // Set up initial audio devices.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalMic},
      /*expected_active_input_node=*/kInternalMic,
      /*expected_active_output_node=*/nullptr,
      /*expected_has_alternative_input=*/false,
      /*expected_has_alternative_output=*/false);

  // Plug a USB input device. Expect active device remains unchanged as
  // internal mic.
  AudioNodeList audio_nodes;
  AudioNode internal_mic = GenerateAudioNode(kInternalMic);
  internal_mic.active = true;
  audio_nodes.push_back(internal_mic);

  AudioNode usb_input = GenerateAudioNode(kUSBMic1);
  audio_nodes.push_back(usb_input);
  ChangeAudioNodes(audio_nodes);

  EXPECT_EQ(0, test_observer_->active_input_node_changed_count());
  ExpectActiveDevice(/*is_input=*/true,
                     /*expected_active_device=*/kInternalMic,
                     /*has_alternative_device=*/true);

  // At this moment, system has seen the device set of internal mic and HDMI
  // output, and the preferable device is internal mic.
  // Unplug HDMI output device.
  audio_nodes.clear();
  audio_nodes.push_back(internal_mic);
  ChangeAudioNodes(audio_nodes);

  // No audio selection performance metrics are fired.
  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionPerformance,
      AudioDeviceMetricsHandler::AudioSelectionEvents::kSystemNotSwitchInput,
      /*expected_count=*/0);
  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionPerformance,
      AudioDeviceMetricsHandler::AudioSelectionEvents::
          kSystemNotSwitchInputNonChromeRestart,
      /*expected_count=*/0);
  histogram_tester_.ExpectTotalCount(
      AudioDeviceMetricsHandler::kAudioSelectionPerformance,
      /*expected_count=*/0);

  // Plug the USB input device. Expect active device remains unchanged. Expect
  // audio selection performance metric of system not switching device is fired.
  audio_nodes.push_back(usb_input);
  ChangeAudioNodes(audio_nodes);

  EXPECT_EQ(0, test_observer_->active_input_node_changed_count());
  ExpectActiveDevice(/*is_input=*/true,
                     /*expected_active_device=*/kInternalMic,
                     /*has_alternative_device=*/true);

  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionPerformance,
      AudioDeviceMetricsHandler::AudioSelectionEvents::kSystemNotSwitchInput,
      /*expected_count=*/1);
  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionPerformance,
      AudioDeviceMetricsHandler::AudioSelectionEvents::
          kSystemNotSwitchInputNonChromeRestart,
      /*expected_count=*/1);
  histogram_tester_.ExpectTotalCount(
      AudioDeviceMetricsHandler::kAudioSelectionPerformance,
      /*expected_count=*/2);
}

// Tests audio selection exception rule #2 metric is recorded when unplugging a
// non active device and the currently active device is not the preferred device
// in the new device set.
TEST_P(CrasAudioHandlerTest,
       AudioSelectionExceptionRule2Output_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);

  // Set up initial audio devices.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kInternalSpeaker,
      /*expected_has_alternative_input=*/false,
      /*expected_has_alternative_output=*/false);

  // Plug a HDMI output device. Expect active device remains unchanged as
  // internal speaker.
  AudioNodeList audio_nodes;
  AudioNode internal_speaker = GenerateAudioNode(kInternalSpeaker);
  internal_speaker.active = true;
  audio_nodes.push_back(internal_speaker);

  AudioNode hdmi_output = GenerateAudioNode(kHDMIOutput);
  audio_nodes.push_back(hdmi_output);
  ChangeAudioNodes(audio_nodes);

  EXPECT_EQ(0, test_observer_->active_output_node_changed_count());
  AudioDevice active_output;
  EXPECT_TRUE(
      cras_audio_handler_->GetPrimaryActiveOutputDevice(&active_output));
  EXPECT_EQ(kInternalSpeaker->id, active_output.id);
  EXPECT_EQ(kInternalSpeaker->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());

  // At this moment, system has seen the device set of internal speaker and HDMI
  // output, and the preferable device is internal speaker.

  // Plug a USB output device. Expect active device remains unchanged as
  // internal speaker.
  AudioNode usb_output = GenerateAudioNode(kUSBHeadphone1);
  audio_nodes.push_back(usb_output);
  ChangeAudioNodes(audio_nodes);

  EXPECT_TRUE(
      cras_audio_handler_->GetPrimaryActiveOutputDevice(&active_output));
  EXPECT_EQ(kInternalSpeaker->id, active_output.id);
  EXPECT_EQ(kInternalSpeaker->id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());

  // Activate HDMI output device, expect HDMI output device becomes active.
  AudioDevice hdmi_output_device(hdmi_output);
  cras_audio_handler_->SwitchToDevice(hdmi_output_device, true,
                                      DeviceActivateType::kActivateByUser);

  EXPECT_TRUE(
      cras_audio_handler_->GetPrimaryActiveOutputDevice(&active_output));
  EXPECT_EQ(kHDMIOutput->id, active_output.id);
  EXPECT_EQ(kHDMIOutput->id, cras_audio_handler_->GetPrimaryActiveOutputNode());

  // No metrics are fired before unplugging.
  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      AudioDeviceMetricsHandler::AudioSelectionExceptionRules::
          kOutputRule2UnplugNonActiveDevice,
      /*expected_count=*/0);
  histogram_tester_.ExpectTotalCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      /*expected_count=*/0);

  // Unplug the USB device
  audio_nodes.clear();
  audio_nodes.push_back(internal_speaker);
  hdmi_output.active = true;
  audio_nodes.push_back(hdmi_output);
  ChangeAudioNodes(audio_nodes);

  // Expect active device remains HDMI, disregarding that the internal speaker
  // is the preferable device in the device set of internal speaker and HDMI
  // output device.
  EXPECT_TRUE(
      cras_audio_handler_->GetPrimaryActiveOutputDevice(&active_output));
  EXPECT_EQ(kHDMIOutput->id, active_output.id);
  EXPECT_EQ(kHDMIOutput->id, cras_audio_handler_->GetPrimaryActiveOutputNode());

  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      AudioDeviceMetricsHandler::AudioSelectionExceptionRules::
          kOutputRule2UnplugNonActiveDevice,
      /*expected_count=*/1);
  histogram_tester_.ExpectTotalCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      /*expected_count=*/1);
}

// Tests audio selection exception rule #2 metric is recorded when unplugging a
// non active device and the currently active device is not the preferred device
// in the new device set.
TEST_P(CrasAudioHandlerTest,
       AudioSelectionExceptionRule2Input_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);

  // Set up initial audio devices.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalMic},
      /*expected_active_input_node=*/kInternalMic,
      /*expected_active_output_node=*/nullptr,
      /*expected_has_alternative_input=*/false,
      /*expected_has_alternative_output=*/false);

  // Plug a USB input1 device. Expect active device remains unchanged as
  // internal speaker.
  AudioNodeList audio_nodes;
  AudioNode internal_mic = GenerateAudioNode(kInternalMic);
  internal_mic.active = true;
  audio_nodes.push_back(internal_mic);

  AudioNode usb_input1 = GenerateAudioNode(kUSBMic1);
  audio_nodes.push_back(usb_input1);
  ChangeAudioNodes(audio_nodes);

  EXPECT_EQ(0, test_observer_->active_input_node_changed_count());
  ExpectActiveDevice(/*is_input=*/true,
                     /*expected_active_device=*/kInternalMic,
                     /*has_alternative_device=*/true);

  // At this moment, system has seen the device set of internal mic and USB
  // input1, and the preferable device is internal mic.

  // Plug a USB input2 device. Expect active device remains unchanged as
  // internal mic.
  AudioNode usb_input2 = GenerateAudioNode(kUSBMic2);
  audio_nodes.push_back(usb_input2);
  ChangeAudioNodes(audio_nodes);

  ExpectActiveDevice(/*is_input=*/true,
                     /*expected_active_device=*/kInternalMic,
                     /*has_alternative_device=*/true);

  // Activate USB input1 device, expect USB input1 device becomes active.
  AudioDevice usb_input1_device(usb_input1);
  cras_audio_handler_->SwitchToDevice(usb_input1_device, true,
                                      DeviceActivateType::kActivateByUser);

  ExpectActiveDevice(/*is_input=*/true, /*expected_active_device=*/kUSBMic1,
                     /*has_alternative_device=*/true);

  // No metrics are fired before unplugging.
  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      AudioDeviceMetricsHandler::AudioSelectionExceptionRules::
          kInputRule2UnplugNonActiveDevice,
      /*expected_count=*/0);
  histogram_tester_.ExpectTotalCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      /*expected_count=*/0);

  // Unplug the USB2 device
  audio_nodes.clear();
  audio_nodes.push_back(internal_mic);
  usb_input1.active = true;
  audio_nodes.push_back(usb_input1);
  ChangeAudioNodes(audio_nodes);

  // Expect active device remains USB1 input device, disregarding that the
  // internal mic is the preferable device in the device set of internal mic and
  // USB1 input device.
  ExpectActiveDevice(/*is_input=*/true, /*expected_active_device=*/kUSBMic1,
                     /*has_alternative_device=*/true);

  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      AudioDeviceMetricsHandler::AudioSelectionExceptionRules::
          kInputRule2UnplugNonActiveDevice,
      /*expected_count=*/1);
  histogram_tester_.ExpectTotalCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      /*expected_count=*/1);
}

TEST_P(CrasAudioHandlerTest, AudioSurveyOutputProc) {
  AudioNodeList audio_nodes = GenerateAudioNodeList(
      {kInternalSpeaker, kHeadphone, kInternalMic, kUSBMic1});
  base::flat_map<std::string, std::string> survey_specific_data = {
      {CrasAudioHandler::kSurveyNameKey,
       CrasAudioHandler::kSurveyNameOutputProc}};

  SetUpCrasAudioHandler(audio_nodes);

  // Simulate an audio survey gets triggered.
  fake_cras_audio_client()->NotifySurveyTriggered(survey_specific_data);

  EXPECT_EQ(test_observer_->survey_triggerd_count(), 1);
  EXPECT_EQ(test_observer_->survey_triggerd_recv().type(),
            CrasAudioHandler::SurveyType::kOutputProc);
  EXPECT_EQ(test_observer_->survey_triggerd_recv().data().size(), 0u);
}

// Tests that two internal input devices don't set alternative_device to be
// true.
TEST_P(CrasAudioHandlerTest,
       AlternativeInputDeviceWithTwoInternalInputDevices) {
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalMic, kFrontMic},
      /*expected_active_input_node=*/kInternalMic,
      /*expected_active_output_node=*/nullptr,
      /*expected_has_alternative_input=*/false,
      /*expected_has_alternative_output=*/std::nullopt);
}

// Tests that three internal input devices don't set alternative_device to be
// true.
TEST_P(CrasAudioHandlerTest,
       AlternativeInputDeviceWithThreeInternalInputDevices) {
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalMic, kFrontMic, kRearMic},
      /*expected_active_input_node=*/kInternalMic,
      /*expected_active_output_node=*/nullptr,
      /*expected_has_alternative_input=*/false,
      /*expected_has_alternative_output=*/std::nullopt);
}

// Tests that one external input devices doesn't set alternative_device to be
// true.
TEST_P(CrasAudioHandlerTest, AlternativeInputDeviceWithOneExternalInputDevice) {
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kUSBMic1},
      /*expected_active_input_node=*/kUSBMic1,
      /*expected_active_output_node=*/nullptr,
      /*expected_has_alternative_input=*/false,
      /*expected_has_alternative_output=*/std::nullopt);
}

// Tests that two external input devices set alternative_device to be
// true.
TEST_P(CrasAudioHandlerTest,
       AlternativeInputDeviceWithTwoExternalInputDevices) {
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kUSBMic1, kUSBMic2},
      /*expected_active_input_node=*/kUSBMic1,
      /*expected_active_output_node=*/nullptr,
      /*expected_has_alternative_input=*/true,
      /*expected_has_alternative_output=*/std::nullopt);
}

// Tests that one external output devices doesn't set alternative_device to be
// true.
TEST_P(CrasAudioHandlerTest,
       AlternativeInputDeviceWithOneExternalOutputDevice) {
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kUSBHeadphone1},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kUSBHeadphone1,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/false);
}

// Tests that two external output devices set alternative_device to be true.
TEST_P(CrasAudioHandlerTest,
       AlternativeInputDeviceWithTwoExternalOutputDevices) {
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kUSBHeadphone1, kHDMIOutput},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kUSBHeadphone1,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/true);
}

// Tests that one internal and one external output devices set
// alternative_device to be true.
TEST_P(CrasAudioHandlerTest,
       AlternativeInputDeviceWithOneInternalAndOneExternalOutputDevice) {
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker, kHDMIOutput},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kHDMIOutput,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/true);
}

// Tests that one internal and one external input devices set alternative_device
// to be true.
TEST_P(CrasAudioHandlerTest,
       AlternativeInputDeviceWithOneInternalAndOneExternalInputDevice) {
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kUSBMic1, kInternalMic},
      /*expected_active_input_node=*/kUSBMic1,
      /*expected_active_output_node=*/nullptr,
      /*expected_has_alternative_input=*/true,
      /*expected_has_alternative_output=*/std::nullopt);
}

// Unplug a device with only one device left, activate that remaining device.
TEST_P(CrasAudioHandlerTest,
       UnplugDeviceWithOneDeviceLeft_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);

  // Set up initial audio devices, with internal speaker and headphone.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker, kHeadphone},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kHeadphone,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/true);

  // Unplug the headphone.
  AudioNodeList audio_nodes;
  audio_nodes.push_back(GenerateAudioNode(kInternalSpeaker));
  ChangeAudioNodes(audio_nodes);

  // Verify the AudioNodesChanged event is fired and one audio device is
  // removed.
  EXPECT_EQ(1, test_observer_->audio_nodes_changed_count());
  VerifySystemMonitorWasCalled();
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(1u, audio_devices.size());

  // Verify the active output device is switched to internal speaker and
  // ActiveOutputChanged event is fired.
  EXPECT_EQ(1, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kInternalSpeaker,
                     /*has_alternative_device=*/false);
}

// Unplug an active device and the remaining device set was seen before, expect
// that the preferred device is activated.
TEST_P(
    CrasAudioHandlerTest,
    UnplugActiveDeviceWithRemainingSetSeenBefore_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);

  // Set up initial audio devices, with kInternalSpeaker, kUSBHeadphone1 and
  // kUSBHeadphone2.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker, kUSBHeadphone1, kUSBHeadphone2},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kInternalSpeaker,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/true);

  AudioNode internal_speaker = GenerateAudioNode(kInternalSpeaker);
  AudioNode usb_headphone_1 = GenerateAudioNode(kUSBHeadphone1);
  AudioNode usb_headphone_2 = GenerateAudioNode(kUSBHeadphone2);
  AudioNode hdmi_output = GenerateAudioNode(kHDMIOutput);

  // Activate kUSBHeadphone2, now that kUSBHeadphone2 is the preferred device
  // among kInternalSpeaker, kUSBHeadphone1 and kUSBHeadphone2.
  AudioDevice usb_headphone_device_2(usb_headphone_2);
  cras_audio_handler_->SwitchToDevice(usb_headphone_device_2, true,
                                      DeviceActivateType::kActivateByUser);
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kUSBHeadphone2,
                     /*has_alternative_device=*/true);

  // Plug kHDMIOutput and activate it.
  AudioNodeList audio_nodes;
  audio_nodes.push_back(internal_speaker);
  audio_nodes.push_back(usb_headphone_1);
  audio_nodes.push_back(usb_headphone_2);
  audio_nodes.push_back(hdmi_output);
  ChangeAudioNodes(audio_nodes);

  AudioDevice hdmi_output_device(hdmi_output);
  cras_audio_handler_->SwitchToDevice(hdmi_output_device, true,
                                      DeviceActivateType::kActivateByUser);
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kHDMIOutput,
                     /*has_alternative_device=*/true);

  // Unplug the kHDMIOutput, expect that kUSBHeadphone2 is acivated as the
  // preferred device among remaining device set.
  audio_nodes.clear();
  audio_nodes.push_back(internal_speaker);
  audio_nodes.push_back(usb_headphone_1);
  audio_nodes.push_back(usb_headphone_2);
  ChangeAudioNodes(audio_nodes);

  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kUSBHeadphone2,
                     /*has_alternative_device=*/true);
}

// Unplug an active device and the remaining device set was not seen before,
// expect that the most recently activated device is activated.
TEST_P(
    CrasAudioHandlerTest,
    UnplugActiveDeviceWithRemainingSetUnseenBefore1_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);

  // Set up initial audio devices, with kInternalSpeaker, kUSBHeadphone1 and
  // kUSBHeadphone2.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker, kUSBHeadphone1, kUSBHeadphone2},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kInternalSpeaker,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/true);

  AudioNode internal_speaker = GenerateAudioNode(kInternalSpeaker);
  AudioNode usb_headphone_1 = GenerateAudioNode(kUSBHeadphone1);
  AudioNode usb_headphone_2 = GenerateAudioNode(kUSBHeadphone2);
  AudioNode hdmi_output = GenerateAudioNode(kHDMIOutput);

  // Activate kUSBHeadphone2. Now that kUSBHeadphone2 the most recently active
  // device.
  AudioDevice usb_headphone_device_2(usb_headphone_2);
  cras_audio_handler_->SwitchToDevice(usb_headphone_device_2, true,
                                      DeviceActivateType::kActivateByUser);
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kUSBHeadphone2,
                     /*has_alternative_device=*/true);

  // Plug kHDMIOutput and activate it. Now that kHDMIOutputis the most recently
  // active device.
  AudioNodeList audio_nodes;
  audio_nodes.push_back(internal_speaker);
  audio_nodes.push_back(usb_headphone_1);
  audio_nodes.push_back(usb_headphone_2);
  audio_nodes.push_back(hdmi_output);
  ChangeAudioNodes(audio_nodes);

  AudioDevice hdmi_output_device(hdmi_output);
  cras_audio_handler_->SwitchToDevice(hdmi_output_device, true,
                                      DeviceActivateType::kActivateByUser);
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kHDMIOutput,
                     /*has_alternative_device=*/true);

  // Activate kUSBHeadphone1 and Unplug it, remaining device set
  // kInternalSpeaker, kUSBHeadphone2, kHDMIOutputis was not seen before. Expect
  // that kHDMIOutput is acivated as the most recently active device.
  AudioDevice usb_headphone_device_1(usb_headphone_1);
  cras_audio_handler_->SwitchToDevice(usb_headphone_device_1, true,
                                      DeviceActivateType::kActivateByUser);

  // No metrics are fired before unplugging the active device.
  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      AudioDeviceMetricsHandler::AudioSelectionExceptionRules::
          kOutputRule4UnplugDeviceCausesUnseenSet,
      /*expected_count=*/0);
  histogram_tester_.ExpectTotalCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      /*expected_count=*/0);

  audio_nodes.clear();
  audio_nodes.push_back(internal_speaker);
  audio_nodes.push_back(usb_headphone_2);
  audio_nodes.push_back(hdmi_output);
  ChangeAudioNodes(audio_nodes);

  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kHDMIOutput,
                     /*has_alternative_device=*/true);

  // Exception rule #4 metrics are fired after unplugging the active device and
  // remaining device set was not seen before.
  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      AudioDeviceMetricsHandler::AudioSelectionExceptionRules::
          kOutputRule4UnplugDeviceCausesUnseenSet,
      /*expected_count=*/1);
  histogram_tester_.ExpectTotalCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      /*expected_count=*/1);
}

// Unplug an active device and the remaining device set was not seen before.
// There is no most recently active device available, fall back to previous
// approach.
TEST_P(
    CrasAudioHandlerTest,
    UnplugActiveDeviceWithRemainingSetUnseenBefore2_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);

  // Set up initial audio devices, with kHDMIOutput, kUSBHeadphone1 and
  // kUSBHeadphone2.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kHDMIOutput, kUSBHeadphone1, kUSBHeadphone2},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kUSBHeadphone1,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/true);

  AudioNode hdmi_output = GenerateAudioNode(kHDMIOutput);
  AudioNode usb_headphone_1 = GenerateAudioNode(kUSBHeadphone1);
  AudioNode usb_headphone_2 = GenerateAudioNode(kUSBHeadphone2);

  // Activate kUSBHeadphone1 and Unplug it, remaining device set
  // kHDMIOutput, kUSBHeadphone2 was not seen before. There is no most
  // recently active device available. Fall back to previous approach and expect
  // that kUSBHeadphone2 is acivated since it has higher default priority than
  // kHDMIOutput.
  AudioDevice usb_headphone_device_1(usb_headphone_1);
  cras_audio_handler_->SwitchToDevice(usb_headphone_device_1, true,
                                      DeviceActivateType::kActivateByUser);

  // No metrics are fired before unplugging the active device.
  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      AudioDeviceMetricsHandler::AudioSelectionExceptionRules::
          kOutputRule4UnplugDeviceCausesUnseenSet,
      /*expected_count=*/0);
  histogram_tester_.ExpectTotalCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      /*expected_count=*/0);

  AudioNodeList audio_nodes;
  audio_nodes.push_back(hdmi_output);
  audio_nodes.push_back(usb_headphone_2);
  ChangeAudioNodes(audio_nodes);

  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kUSBHeadphone2,
                     /*has_alternative_device=*/true);

  // Exception rule #4 metrics are fired after unplugging the active device and
  // remaining device set was not seen before.
  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      AudioDeviceMetricsHandler::AudioSelectionExceptionRules::
          kOutputRule4UnplugDeviceCausesUnseenSet,
      /*expected_count=*/1);
  histogram_tester_.ExpectTotalCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      /*expected_count=*/1);
}

// Unplug an input active device and the remaining device set was not seen
// before. Exception rule #4 metrics are fired.
TEST_P(
    CrasAudioHandlerTest,
    UnplugActiveDeviceWithRemainingSetUnseenBefore4_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);

  // Set up initial audio devices, with kBluetoothNbMic, kUSBMic1 and
  // kUSBMic2.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kBluetoothNbMic, kUSBMic1, kUSBMic2},
      /*expected_active_input_node=*/kUSBMic1,
      /*expected_active_output_node=*/nullptr,
      /*expected_has_alternative_input=*/true,
      /*expected_has_alternative_output=*/std::nullopt);

  AudioNode bluetooth_input = GenerateAudioNode(kBluetoothNbMic);
  AudioNode usb_mic_1 = GenerateAudioNode(kUSBMic1);
  AudioNode usb_mic_2 = GenerateAudioNode(kUSBMic2);

  // Activate kUSBMic1 and Unplug it, remaining device set
  // kBluetoothNbMic, kUSBMic2 was not seen before. There is no most
  // recently active device available. Fall back to previous approach and expect
  // that kUSBMic2 is acivated since it has higher default priority than
  // kBluetoothNbMic.
  AudioDevice usb_mic_device_1(usb_mic_1);
  cras_audio_handler_->SwitchToDevice(usb_mic_device_1, true,
                                      DeviceActivateType::kActivateByUser);

  // No metrics are fired before unplugging the active device.
  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      AudioDeviceMetricsHandler::AudioSelectionExceptionRules::
          kInputRule4UnplugDeviceCausesUnseenSet,
      /*expected_count=*/0);
  histogram_tester_.ExpectTotalCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      /*expected_count=*/0);

  AudioNodeList audio_nodes;
  audio_nodes.push_back(bluetooth_input);
  audio_nodes.push_back(usb_mic_2);
  ChangeAudioNodes(audio_nodes);

  ExpectActiveDevice(/*is_input=*/true,
                     /*expected_active_device=*/kUSBMic2,
                     /*has_alternative_device=*/true);

  // Exception rule #4 metrics are fired after unplugging the active device and
  // remaining device set was not seen before.
  histogram_tester_.ExpectBucketCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      AudioDeviceMetricsHandler::AudioSelectionExceptionRules::
          kInputRule4UnplugDeviceCausesUnseenSet,
      /*expected_count=*/1);
  histogram_tester_.ExpectTotalCount(
      AudioDeviceMetricsHandler::kAudioSelectionExceptionRuleMetrics,
      /*expected_count=*/1);
}

// Tests system boots with only one device kHDMIOutput, kHDMIOutput is expected
// to be activated and no notification is shown.
TEST_P(CrasAudioHandlerTest,
       SystemBootsOnlyOneDevice_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kHDMIOutput},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kHDMIOutput,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/false);

  EXPECT_EQ(0u, GetNotificationCount());
}

// Tests system boots with kInternalSpeaker and 3.5mm kHeadphone, kHeadphone is
// expected to be activated and no notification is shown.
TEST_P(CrasAudioHandlerTest,
       SystemBootsWith35mmHeadphone_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kHDMIOutput, kHeadphone},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kHeadphone,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/true);

  EXPECT_EQ(0u, GetNotificationCount());
}

// Tests system boots with kInternalSpeaker and kHDMIOutput, no most current
// activated device list, kInternalSpeaker is expected to be activated and
// notification will be shown.
TEST_P(CrasAudioHandlerTest,
       SystemBootsWithInternalAndExternal_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kHDMIOutput, kInternalSpeaker},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kInternalSpeaker,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/true);

  FastForwardBy(AudioSelectionNotificationHandler::kDebounceTime);
  EXPECT_EQ(1u, GetNotificationCount());
}

// Tests system boots with kUSBHeadphone1 and kHDMIOutput, no most current
// activated device list, kUSBHeadphone1 is expected to be activated since it
// has higher built-in priority, notification will be shown.
TEST_P(CrasAudioHandlerTest,
       SystemBootsWithTwoExternal_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kHDMIOutput, kUSBHeadphone1},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kUSBHeadphone1,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/true);

  FastForwardBy(AudioSelectionNotificationHandler::kDebounceTime);
  EXPECT_EQ(1u, GetNotificationCount());
}

// Tests system boots with device set that was seen before. Activate the
// preferred device among the device set and no notification is shown.
TEST_P(CrasAudioHandlerTest,
       SystemBootsWithSeenDeviceSet_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kHDMIOutput, kInternalSpeaker, kHeadphone},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kHeadphone,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/true);

  EXPECT_EQ(0u, GetNotificationCount());

  AudioNode hdmi_output = GenerateAudioNode(kHDMIOutput);
  AudioNode internal_speaker = GenerateAudioNode(kInternalSpeaker);
  AudioNode headphone = GenerateAudioNode(kHeadphone);

  // Activate kHDMIOutput.
  AudioDevice hdmi_output_device(hdmi_output);
  cras_audio_handler_->SwitchToDevice(hdmi_output_device, true,
                                      DeviceActivateType::kActivateByUser);

  // Reset audio nodes.
  AudioNodeList audio_nodes;
  audio_nodes.push_back(internal_speaker);
  ChangeAudioNodes(audio_nodes);

  // kHDMIOutput is not activated at this moment.
  EXPECT_NE(kHDMIOutput->id, cras_audio_handler_->GetPrimaryActiveOutputNode());

  // Mock system reboots.
  audio_nodes.clear();
  audio_nodes.push_back(hdmi_output);
  audio_nodes.push_back(internal_speaker);
  audio_nodes.push_back(headphone);
  ChangeAudioNodes(audio_nodes);

  // Expect kHDMIOutput is activated as the preferred device.
  ExpectActiveDevice(/*is_input=*/false, /*expected_active_device=*/kHDMIOutput,
                     /*has_alternative_device=*/true);
}

// Tests non simple usage devices are ignored.
TEST_P(CrasAudioHandlerTest,
       IgnoreNonSimpleUsageDevices_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);

  AudioNodeList audio_nodes;
  audio_nodes.push_back(GenerateAudioNode(kMicJack));
  audio_nodes.push_back(GenerateAudioNode(kKeyboardMic));
  SetUpCrasAudioHandler(audio_nodes);

  // kKeyboardMic is a simple usage device, it should be ignored.
  // The devices size is expected to be 1.
  AudioDeviceList audio_devices;
  cras_audio_handler_->GetAudioDevices(&audio_devices);
  EXPECT_EQ(1u, audio_devices.size());
}

// Tests calling SyncDevicePrefSetMap with no active device will early return
// and no crashes.
TEST_P(CrasAudioHandlerTest, SyncDevicePrefSetMap) {
  SetUpCrasAudioHandler({});
  const std::map<std::string, std::string>& device_pref_set_map =
      GetDevicePrefSetMap();
  EXPECT_TRUE(device_pref_set_map.empty());

  SyncDevicePrefSetMap(/*is_input=*/true);
  SyncDevicePrefSetMap(/*is_input=*/false);
  EXPECT_TRUE(device_pref_set_map.empty());
}

// Tests that showing notification is debounced for audio output device.
TEST_P(CrasAudioHandlerTest,
       DebounceNotificationForOutput_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);

  // Initialize with internal speaker.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kInternalSpeaker,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/false);

  // Expect that notification is not displayed since there is only one output
  // device connected.
  EXPECT_EQ(0u, GetNotificationCount());

  // Plug in a usb headphone without fast forward time.
  AudioNodeList audio_nodes;
  AudioNode internal_speaker = GenerateAudioNode(kInternalSpeaker);
  internal_speaker.active = true;
  audio_nodes.push_back(internal_speaker);
  AudioNode usb_headphone_1 = GenerateAudioNode(kUSBHeadphone1);
  audio_nodes.push_back(usb_headphone_1);
  ChangeAudioNodes(audio_nodes);

  // Although notification is supposed to show up for unseen new connected USB
  // headphone device, the debounce function will delay the display of
  // notification.
  EXPECT_EQ(0u, GetNotificationCount());

  // Plug in another usb headphone with fast forward time.
  AudioNode usb_headphone_2 = GenerateAudioNode(kUSBHeadphone2);
  audio_nodes.push_back(usb_headphone_2);
  ChangeAudioNodes(audio_nodes);

  // Verify the active output device is not switched because the new connected
  // device is not seen before.
  EXPECT_EQ(0, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kInternalSpeaker,
                     /*has_alternative_device=*/true);

  // Verify notification is displayed at this moment.
  FastForwardBy(AudioSelectionNotificationHandler::kDebounceTime);
  EXPECT_EQ(1u, GetNotificationCount());

  // Verify that the notification is for multiple audio sources detected.
  // when two new devices are hot plugged quickly, due to the debounce
  // mechanism, rather than showing different notifications for each of them
  // (with the first one being overridden quickly), we show notification to
  // handle both of them together.
  std::optional<std::u16string> title = GetNotificationTitle();
  EXPECT_TRUE(title.has_value());
  EXPECT_EQ(
      l10n_util::GetStringUTF16(IDS_ASH_AUDIO_SELECTION_MULTIPLE_DEVICES_TITLE),
      title.value());
}

// Tests that showing notification is debounced for audio input device.
TEST_P(CrasAudioHandlerTest,
       DebounceNotificationForInput_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);

  // Initialize with internal mic.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalMic},
      /*expected_active_input_node=*/kInternalMic,
      /*expected_active_output_node=*/nullptr,
      /*expected_has_alternative_input=*/false,
      /*expected_has_alternative_output=*/std::nullopt);

  // Expect that notification is not displayed since there is only one input
  // device connected.
  EXPECT_EQ(0u, GetNotificationCount());

  // Plug in a usb mic without fast forward time.
  AudioNodeList audio_nodes;
  AudioNode inernal_mic = GenerateAudioNode(kInternalMic);
  inernal_mic.active = true;
  audio_nodes.push_back(inernal_mic);
  AudioNode usb_mic_1 = GenerateAudioNode(kUSBMic1);
  audio_nodes.push_back(usb_mic_1);
  ChangeAudioNodes(audio_nodes);

  // Although notification is supposed to show up for unseen new connected USB
  // mic device, the debounce function will delay the display of
  // notification.
  EXPECT_EQ(0u, GetNotificationCount());

  // Plug in another usb mic with fast forward time.
  AudioNode usb_mic_2 = GenerateAudioNode(kUSBMic2);
  audio_nodes.push_back(usb_mic_2);
  ChangeAudioNodes(audio_nodes);

  // Verify the active output device is not switched because the new connected
  // device is not seen before.
  EXPECT_EQ(0, test_observer_->active_input_node_changed_count());
  ExpectActiveDevice(/*is_input=*/true,
                     /*expected_active_device=*/kInternalMic,
                     /*has_alternative_device=*/true);

  // Verify notification is displayed at this moment.
  FastForwardBy(AudioSelectionNotificationHandler::kDebounceTime);
  EXPECT_EQ(1u, GetNotificationCount());

  // Verify that the notification is for multiple audio sources detected.
  // when two new devices are hot plugged quickly, due to the debounce
  // mechanism, rather than showing different notifications for each of them
  // (with the first one being overridden quickly), we show notification to
  // handle both of them together.
  std::optional<std::u16string> title = GetNotificationTitle();
  EXPECT_TRUE(title.has_value());
  EXPECT_EQ(
      l10n_util::GetStringUTF16(IDS_ASH_AUDIO_SELECTION_MULTIPLE_DEVICES_TITLE),
      title.value());
}

// Tests that in a rare case where both input and output have the same stable
// id, manually switch output device should keep the input device unchanged.
TEST_P(CrasAudioHandlerTest,
       SwitchOutputShouldKeepInputUnchanged_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);

  // Initialize with internal devices.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalMic, kInternalSpeaker},
      /*expected_active_input_node=*/kInternalMic,
      /*expected_active_output_node=*/kInternalSpeaker,
      /*expected_has_alternative_input=*/false,
      /*expected_has_alternative_output=*/false);

  // Plug USB input and output devices with the same stable id.
  AudioNodeList audio_nodes;
  AudioNode inernal_mic = GenerateAudioNode(kInternalMic);
  inernal_mic.active = true;
  audio_nodes.push_back(inernal_mic);
  AudioNode internal_speaker = GenerateAudioNode(kInternalSpeaker);
  internal_speaker.active = true;
  audio_nodes.push_back(internal_speaker);

  uint64_t id = 100;
  uint64_t stable_device_id_v1 = 12345;
  uint64_t stable_device_id_v2 = 6789;
  AudioNode usb_input = AudioNode(
      /*is_input=*/true, id, /*has_v2_stable_device_id=*/true,
      stable_device_id_v1, stable_device_id_v2,
      /*device_name=*/"usb_input",
      /*type=*/"USB", /*name=*/"usb_input", false /* is_active*/,
      /*plugged_time=*/100, kInputMaxSupportedChannels, kInputAudioEffect,
      /*number_of_volume_steps=*/0);
  audio_nodes.push_back(usb_input);
  AudioNode usb_output = AudioNode(
      /*is_input=*/false, id, /*has_v2_stable_device_id=*/true,
      stable_device_id_v1, stable_device_id_v2,
      /*device_name=*/"usb_output",
      /*type=*/"USB", /*name=*/"usb_output", false /* is_active*/,
      /*plugged_time=*/100, kOutputMaxSupportedChannels, kOutputAudioEffect,
      /*number_of_volume_steps=*/0);
  audio_nodes.push_back(usb_output);
  ChangeAudioNodes(audio_nodes);

  // Verify internal device is still active.
  ExpectActiveDevice(/*is_input=*/true,
                     /*expected_active_device=*/kInternalMic,
                     /*has_alternative_device=*/true);
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kInternalSpeaker,
                     /*has_alternative_device=*/true);

  // Switch to usb_output.
  AudioDevice usb_output_device(usb_output);
  cras_audio_handler_->SwitchToDevice(usb_output_device, true,
                                      DeviceActivateType::kActivateByUser);
  ChangeAudioNodes(audio_nodes);

  // Verify output is switched to usb_output, input statys the same.
  ExpectActiveDevice(/*is_input=*/true,
                     /*expected_active_device=*/kInternalMic,
                     /*has_alternative_device=*/true);
  EXPECT_EQ(usb_output_device.id,
            cras_audio_handler_->GetPrimaryActiveOutputNode());
}

// Tests that GetDeviceFromStableDeviceId can get the correct device when an
// input and an output device have the same stable id.
TEST_P(CrasAudioHandlerTest,
       GetDeviceFromStableDeviceId_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);

  // Plug USB input and output devices with the same stable id.
  AudioNodeList audio_nodes;
  uint64_t stable_device_id = 12345;
  std::string input_device_name = "usb_input";
  std::string output_device_name = "usb_output";
  AudioNode usb_input = AudioNode(
      /*is_input=*/true, /*id=*/800, /*has_v2_stable_device_id=*/true,
      stable_device_id, stable_device_id,
      /*device_name=*/input_device_name,
      /*type=*/"USB", /*name=*/input_device_name, /*active=*/false,
      /*plugged_time=*/100, kInputMaxSupportedChannels, kInputAudioEffect,
      /*number_of_volume_steps=*/0);

  AudioNode usb_output = AudioNode(
      /*is_input=*/false, /*id=*/900, /*has_v2_stable_device_id=*/true,
      stable_device_id, stable_device_id,
      /*device_name=*/output_device_name,
      /*type=*/"USB", /*name=*/output_device_name, /*active=*/false,
      /*plugged_time=*/100, kOutputMaxSupportedChannels, kOutputAudioEffect,
      /*number_of_volume_steps=*/0);
  audio_nodes.push_back(usb_input);
  audio_nodes.push_back(usb_output);
  SetUpCrasAudioHandler(audio_nodes);

  std::optional<AudioDevice> device1 =
      GetDeviceFromStableDeviceId(/*is_input=*/false, stable_device_id);
  EXPECT_TRUE(device1.has_value());
  EXPECT_FALSE(device1->is_input);
  EXPECT_EQ(device1->device_name, output_device_name);

  std::optional<AudioDevice> device2 =
      GetDeviceFromStableDeviceId(/*is_input=*/true, stable_device_id);
  EXPECT_TRUE(device2.has_value());
  EXPECT_TRUE(device2->is_input);
  EXPECT_EQ(device2->device_name, input_device_name);
}

// Tests that notification is removed if the hot plugged device that triggered
// the notification has already been activated via settings or quick settings.
TEST_P(
    CrasAudioHandlerTest,
    RemoveNotificationIfHotPluggedDeviceHasBeenActivated_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);

  // Initialize with internal speaker.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kInternalSpeaker,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/false);

  // Expect that notification is not displayed since there is only one output
  // device connected.
  EXPECT_EQ(0u, GetNotificationCount());

  // Plug in a usb headphone.
  AudioNodeList audio_nodes;
  AudioNode internal_speaker = GenerateAudioNode(kInternalSpeaker);
  internal_speaker.active = true;
  audio_nodes.push_back(internal_speaker);
  AudioNode usb_headphone_1 = GenerateAudioNode(kUSBHeadphone1);
  audio_nodes.push_back(usb_headphone_1);
  ChangeAudioNodes(audio_nodes);

  // Verify the active output device is not switched because the new connected
  // device is not seen before.
  EXPECT_EQ(0, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kInternalSpeaker,
                     /*has_alternative_device=*/true);

  // Verify notification is displayed.
  FastForwardBy(AudioSelectionNotificationHandler::kDebounceTime);
  EXPECT_EQ(1u, GetNotificationCount());

  // Activate the kUSBHeadphone1 by system, expect that notification is not
  // removed.
  AudioDevice usb_headphone_device(usb_headphone_1);
  cras_audio_handler_->SwitchToDevice(usb_headphone_device, true,
                                      DeviceActivateType::kActivateByPriority);
  EXPECT_EQ(1u, GetNotificationCount());

  // Switch back to internal speaker.
  AudioDevice internal_speaker_device(internal_speaker);
  cras_audio_handler_->SwitchToDevice(internal_speaker_device, true,
                                      DeviceActivateType::kActivateByPriority);

  // Manually activate the kUSBHeadphone1, expect that notification is removed.
  cras_audio_handler_->SwitchToDevice(usb_headphone_device, true,
                                      DeviceActivateType::kActivateByUser);
  EXPECT_EQ(0u, GetNotificationCount());
}

// Tests that notification is not removed if the hot plugged device that
// triggered the notification was disconnected and reconnected within grace
// period.
TEST_P(
    CrasAudioHandlerTest,
    DoNotRemoveNotificationIfHotPluggedDeviceWasDiconnectedAndReconnectedQuickly_AudioSelectionImprovementFlagOn) {
  scoped_feature_list_.InitAndEnableFeature(
      ash::features::kAudioSelectionImprovement);

  // Initialize with internal speaker.
  SetupAudioNodesAndExpectActiveNodes(
      /*initial_nodes=*/{kInternalSpeaker},
      /*expected_active_input_node=*/nullptr,
      /*expected_active_output_node=*/kInternalSpeaker,
      /*expected_has_alternative_input=*/std::nullopt,
      /*expected_has_alternative_output=*/false);

  // Expect that notification is not displayed since there is only one output
  // device connected.
  EXPECT_EQ(0u, GetNotificationCount());

  // Plug in a usb headphone.
  AudioNodeList audio_nodes;
  AudioNode internal_speaker = GenerateAudioNode(kInternalSpeaker);
  internal_speaker.active = true;
  audio_nodes.push_back(internal_speaker);
  AudioNode usb_headphone_1 = GenerateAudioNode(kUSBHeadphone1);
  audio_nodes.push_back(usb_headphone_1);
  ChangeAudioNodes(audio_nodes);

  // Verify the active output device is not switched because the new connected
  // device is not seen before.
  EXPECT_EQ(0, test_observer_->active_output_node_changed_count());
  ExpectActiveDevice(/*is_input=*/false,
                     /*expected_active_device=*/kInternalSpeaker,
                     /*has_alternative_device=*/true);

  // Verify notification is displayed.
  FastForwardBy(AudioSelectionNotificationHandler::kDebounceTime);
  EXPECT_EQ(1u, GetNotificationCount());

  // Disconnect the usb headphone.
  audio_nodes.clear();
  audio_nodes.push_back(internal_speaker);
  ChangeAudioNodes(audio_nodes);

  // Notification still shows within grace period.
  FastForwardBy(base::Milliseconds(2000));
  EXPECT_EQ(1u, GetNotificationCount());

  // Reconnect the usb headphone within grace period.
  audio_nodes.clear();
  audio_nodes.push_back(internal_speaker);
  audio_nodes.push_back(usb_headphone_1);
  ChangeAudioNodes(audio_nodes);

  // Verify that notification still displays.
  FastForwardBy(base::Milliseconds(2000));
  EXPECT_EQ(1u, GetNotificationCount());

  // Verify that notification after grace period.
  FastForwardBy(CrasAudioHandler::kRemoveNotificationDelay);
  EXPECT_EQ(1u, GetNotificationCount());

  // Remove the usb headphone again.
  audio_nodes.clear();
  audio_nodes.push_back(internal_speaker);
  ChangeAudioNodes(audio_nodes);

  // Notification is removed after grace period.
  FastForwardBy(CrasAudioHandler::kRemoveNotificationDelay);
  EXPECT_EQ(0u, GetNotificationCount());
}

}  // namespace ash