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


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

#include <algorithm>
#include <string>
#include <vector>

#include "base/base_paths.h"
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/containers/flat_set.h"
#include "base/files/file_path.h"
#include "base/functional/callback_helpers.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/gmock_move_support.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.h"
#include "base/types/expected.h"
#include "base/version.h"
#include "content/browser/gpu/gpu_data_manager_impl.h"
#include "content/public/common/cdm_info.h"
#include "content/public/test/browser_task_environment.h"
#include "gpu/config/gpu_feature_info.h"
#include "media/base/cdm_capability.h"
#include "media/base/media_switches.h"
#include "media/base/video_codecs.h"
#include "media/cdm/cdm_type.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace content {

namespace {

using AudioCodec = media::AudioCodec;
using VideoCodec = media::VideoCodec;
using EncryptionScheme = media::EncryptionScheme;
using CdmSessionType = media::CdmSessionType;
using Robustness = CdmInfo::Robustness;
using base::test::RunOnceCallback;
using testing::_;

const char kTestCdmName[] = "Test CDM";
const char kAlternateCdmName[] = "Alternate CDM";
const media::CdmType kTestCdmType{1234, 5678};
const char kTestPath[] = "/aa/bb";
const char kVersion1[] = "1.1.1.1";
const char kVersion2[] = "1.1.1.2";
const char kTestKeySystem[] = "com.example.somesystem";
const char kOtherKeySystem[] = "com.example.othersystem";
const int kObserver1 = 1;
const int kObserver2 = 2;

#if BUILDFLAG(IS_WIN)
// 0x1234 is randomly picked as a non-software emulated vendor ID.
constexpr uint32_t kNonSoftwareEmulatedVendorId = 0x1234;
// 0x0000 is one of the software emulated vendor IDs.
constexpr uint32_t kSoftwareEmulatedVendorId = 0x0000;
#endif  // BUILDFLAG(IS_WIN)

// Helper function to convert a VideoCodecMap to a list of VideoCodec values
// so that they can be compared. VideoCodecProfiles are ignored.
std::vector<media::VideoCodec> VideoCodecMapToList(
    const media::CdmCapability::VideoCodecMap& map) {
  std::vector<media::VideoCodec> list;
  for (const auto& [video_codec, ignore] : map) {
    list.push_back(video_codec);
  }
  return list;
}

#define EXPECT_STL_EQ(container, ...)                            \
  do {                                                           \
    EXPECT_THAT(container, ::testing::ElementsAre(__VA_ARGS__)); \
  } while (false)

#define EXPECT_AUDIO_CODECS(...) \
  EXPECT_STL_EQ(cdm.capability->audio_codecs, __VA_ARGS__)

#define EXPECT_VIDEO_CODECS(...) \
  EXPECT_STL_EQ(VideoCodecMapToList(cdm.capability->video_codecs), __VA_ARGS__)

#define EXPECT_ENCRYPTION_SCHEMES(...) \
  EXPECT_STL_EQ(cdm.capability->encryption_schemes, __VA_ARGS__)

#define EXPECT_SESSION_TYPES(...) \
  EXPECT_STL_EQ(cdm.capability->session_types, __VA_ARGS__)

}  // namespace

// TODO(crbug.com/347991515): Add browser tests to test protected content id
// settings.
// For simplicity and to make failures easier to diagnose, this test uses
// std::string instead of base::FilePath and std::vector<std::string>.
class CdmRegistryImplTest : public testing::Test {
 public:
  CdmRegistryImplTest() = default;
  ~CdmRegistryImplTest() override = default;

  void SetUp() final {
    DVLOG(1) << __func__;

    auto* gpu_data_manager = GpuDataManagerImpl::GetInstance();

    // Simulate GPU process initialization completing with GL unavailable.
    gpu::GpuFeatureInfo gpu_feature_info = GetGpuFeatureInfoWithOneDisabled(
        gpu::GpuFeatureType::GPU_FEATURE_TYPE_ACCELERATED_GL);
    gpu_data_manager->UpdateGpuFeatureInfo(gpu_feature_info, std::nullopt);

#if BUILDFLAG(IS_WIN)
    // Simulate enabling direct composition.
    gpu::GPUInfo gpu_info;
    gpu_info.overlay_info.direct_composition = true;

    // Simulate non-software emulated GPU. Check out `gpu/config/gpu_info.cc` to
    // see which vendor IDs are for the software emulated GPU.
    gpu_info.gpu.vendor_id = kNonSoftwareEmulatedVendorId;
    gpu_data_manager->UpdateGpuInfo(gpu_info, std::nullopt);
#endif  // BUILDFLAG(IS_WIN)

    cdm_registry_.SetCapabilityCBForTesting(capability_cb_.Get());
  }

  void OnKeySystemCapabilitiesUpdated(
      int observer_id,
      base::OnceClosure done_cb,
      KeySystemCapabilities key_system_capabilities) {
    DVLOG(1) << __func__;
    results_[observer_id].push_back(std::move(key_system_capabilities));
    std::move(done_cb).Run();
  }

 protected:
  media::CdmCapability GetTestCdmCapability() {
    return media::CdmCapability(
        {media::AudioCodec::kVorbis},
        {{media::VideoCodec::kVP8, {}}, {media::VideoCodec::kVP9, {}}},
        {EncryptionScheme::kCenc},
        {CdmSessionType::kTemporary, CdmSessionType::kPersistentLicense},
        base::Version(kVersion1));
  }

  media::CdmCapability GetOtherCdmCapability() {
    return media::CdmCapability(
        {media::AudioCodec::kVorbis}, {{media::VideoCodec::kVP9, {}}},
        {EncryptionScheme::kCbcs}, {CdmSessionType::kTemporary},
        base::Version(kVersion1));
  }

  CdmInfo GetTestCdmInfo() {
    return CdmInfo(kTestKeySystem, CdmInfo::Robustness::kSoftwareSecure,
                   GetTestCdmCapability(),
                   /*supports_sub_key_systems=*/true, kTestCdmName,
                   kTestCdmType, base::FilePath::FromUTF8Unsafe(kTestPath));
  }

  void Register(CdmInfo cdm_info) {
    cdm_registry_.RegisterCdm(std::move(cdm_info));
  }

  void Register(const std::string& key_system,
                std::optional<media::CdmCapability> capability,
                Robustness robustness = Robustness::kSoftwareSecure) {
    Register(CdmInfo(key_system, robustness, std::move(capability),
                     /*supports_sub_key_systems=*/true, kTestCdmName,
                     kTestCdmType, base::FilePath::FromUTF8Unsafe(kTestPath)));
  }

  void RegisterForLazySoftwareSecureInitialization() {
    // Register a CdmInfo without CdmCapability to allow lazy initialization.
    Register(CdmInfo(kTestKeySystem, CdmInfo::Robustness::kSoftwareSecure,
                     std::nullopt, kTestCdmType));
    auto cdm_info = cdm_registry_.GetCdmInfo(
        kTestKeySystem, CdmInfo::Robustness::kSoftwareSecure);
    ASSERT_TRUE(cdm_info);
    ASSERT_FALSE(cdm_info->capability);
  }

  void RegisterForLazyHardwareSecureInitialization() {
    // Register a CdmInfo without CdmCapability to allow lazy initialization.
    Register(CdmInfo(kTestKeySystem, CdmInfo::Robustness::kHardwareSecure,
                     std::nullopt, kTestCdmType));
    auto cdm_info = cdm_registry_.GetCdmInfo(
        kTestKeySystem, CdmInfo::Robustness::kHardwareSecure);
    ASSERT_TRUE(cdm_info);
    ASSERT_FALSE(cdm_info->capability);
  }

  bool IsRegistered(const std::string& name, const std::string& version) {
    for (const auto& cdm : cdm_registry_.GetRegisteredCdms()) {
      if (cdm.name == name && cdm.capability->version.GetString() == version) {
        return true;
      }
    }
    return false;
  }

  std::vector<std::string> GetVersions(const media::CdmType& cdm_type) {
    std::vector<std::string> versions;
    for (const auto& cdm : cdm_registry_.GetRegisteredCdms()) {
      if (cdm.type == cdm_type) {
        versions.push_back(cdm.capability->version.GetString());
      }
    }
    return versions;
  }

  void GetKeySystemCapabilities(bool allow_hw_secure_capability_check = true) {
    DVLOG(1) << __func__;
    base::RunLoop run_loop;
    capabilities_cb_sub_ = cdm_registry_.ObserveKeySystemCapabilities(
        allow_hw_secure_capability_check,
        base::BindRepeating(
            &CdmRegistryImplTest::OnKeySystemCapabilitiesUpdated,
            base::Unretained(this), kObserver1, run_loop.QuitClosure()));
    run_loop.Run();
  }

  [[maybe_unused]] gpu::GpuFeatureInfo GetGpuFeatureInfoWithOneDisabled(
      gpu::GpuFeatureType disabled_feature) {
    gpu::GpuFeatureInfo gpu_feature_info;
    for (auto& status : gpu_feature_info.status_values) {
      status = gpu::GpuFeatureStatus::kGpuFeatureStatusEnabled;
    }
    gpu_feature_info.status_values[disabled_feature] =
        gpu::GpuFeatureStatus::kGpuFeatureStatusDisabled;
    return gpu_feature_info;
  }

  void SelectHardwareSecureDecryption(bool enabled) {
    const std::vector<base::test::FeatureRef> kHardwareSecureFeatures = {
        media::kHardwareSecureDecryption,
        media::kHardwareSecureDecryptionExperiment};
    const std::vector<base::test::FeatureRef> kNoFeatures = {};

    auto enabled_features = enabled ? kHardwareSecureFeatures : kNoFeatures;
    auto disabled_features = enabled ? kNoFeatures : kHardwareSecureFeatures;
    scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
  }

#if BUILDFLAG(IS_ANDROID)
  // On Android checking for key system support can be run on a separate
  // thread. Disable this for testing.
  void DisableMediaDrmQueryInSeparateProcess() {
    scoped_feature_list_.InitAndDisableFeature(
        media::kMediaDrmQueryInSeparateProcess);
  }
#endif

  void ClearCapabilityTestOverride() {
    cdm_registry_.SetCapabilityCBForTesting(base::NullCallback());
  }

  base::test::ScopedFeatureList scoped_feature_list_;
  BrowserTaskEnvironment task_environment_;

  CdmRegistryImpl cdm_registry_;
  base::MockCallback<CdmRegistryImpl::CapabilityCB> capability_cb_;
  base::CallbackListSubscription capabilities_cb_sub_;

  // Map of "observer ID" to the list of updated KeySystemCapabilities.
  std::map<int, std::vector<KeySystemCapabilities>> results_;
};

TEST_F(CdmRegistryImplTest, Register) {
  Register(GetTestCdmInfo());

  auto cdms = cdm_registry_.GetRegisteredCdms();
  ASSERT_EQ(1u, cdms.size());
  CdmInfo cdm = cdms[0];
  EXPECT_EQ(kTestCdmName, cdm.name);
  EXPECT_EQ(kVersion1, cdm.capability->version.GetString());
  EXPECT_EQ(kTestPath, cdm.path.MaybeAsASCII());
  EXPECT_EQ(kTestCdmType, cdm.type);
  EXPECT_AUDIO_CODECS(AudioCodec::kVorbis);
  EXPECT_VIDEO_CODECS(VideoCodec::kVP8, VideoCodec::kVP9);
  EXPECT_ENCRYPTION_SCHEMES(EncryptionScheme::kCenc);
  EXPECT_SESSION_TYPES(CdmSessionType::kTemporary,
                       CdmSessionType::kPersistentLicense);
  EXPECT_EQ(kTestKeySystem, cdm.key_system);
  EXPECT_TRUE(cdm.supports_sub_key_systems);
  EXPECT_EQ(cdm.robustness, CdmInfo::Robustness::kSoftwareSecure);
}

TEST_F(CdmRegistryImplTest, ReRegister) {
  auto cdm_info = GetTestCdmInfo();
  Register(cdm_info);
  EXPECT_TRUE(IsRegistered(kTestCdmName, kVersion1));

  // Now register same key system with different values.
  cdm_info.supports_sub_key_systems = false;
  Register(cdm_info);

  EXPECT_TRUE(IsRegistered(kTestCdmName, kVersion1));
}

TEST_F(CdmRegistryImplTest, MultipleVersions) {
  auto cdm_info = GetTestCdmInfo();
  Register(cdm_info);
  cdm_info.capability->version = base::Version(kVersion2);
  Register(cdm_info);

  EXPECT_TRUE(IsRegistered(kTestCdmName, kVersion1));
  EXPECT_TRUE(IsRegistered(kTestCdmName, kVersion2));

  // The first inserted CdmInfo takes effect.
  auto result = cdm_registry_.GetCdmInfo(kTestKeySystem,
                                         CdmInfo::Robustness::kSoftwareSecure);
  ASSERT_EQ(result->capability->version, base::Version(kVersion1));
}

TEST_F(CdmRegistryImplTest, NewVersionInsertedLast) {
  auto cdm_info = GetTestCdmInfo();
  Register(cdm_info);
  cdm_info.capability->version = base::Version(kVersion2);
  Register(cdm_info);

  const std::vector<std::string> versions = GetVersions(kTestCdmType);
  EXPECT_EQ(2u, versions.size());
  EXPECT_EQ(kVersion1, versions[0]);
  EXPECT_EQ(kVersion2, versions[1]);
}

TEST_F(CdmRegistryImplTest, DifferentNames) {
  auto cdm_info = GetTestCdmInfo();
  Register(cdm_info);
  cdm_info.name = kAlternateCdmName;
  Register(cdm_info);

  EXPECT_TRUE(IsRegistered(kTestCdmName, kVersion1));
  EXPECT_TRUE(IsRegistered(kAlternateCdmName, kVersion1));
}

TEST_F(CdmRegistryImplTest, Profiles) {
  Register(kTestKeySystem,
           media::CdmCapability(
               {AudioCodec::kVorbis},
               {{VideoCodec::kVP9,
                 media::VideoCodecInfo({media::VP9PROFILE_PROFILE0,
                                        media::VP9PROFILE_PROFILE2})}},
               {EncryptionScheme::kCenc}, {CdmSessionType::kTemporary},
               base::Version(kVersion1)));
  auto cdm_info = cdm_registry_.GetCdmInfo(
      kTestKeySystem, CdmInfo::Robustness::kSoftwareSecure);
  CdmInfo& cdm = *cdm_info;
  EXPECT_VIDEO_CODECS(VideoCodec::kVP9);
  EXPECT_TRUE(base::Contains(
      cdm.capability->video_codecs[VideoCodec::kVP9].supported_profiles,
      media::VP9PROFILE_PROFILE0));
  EXPECT_TRUE(base::Contains(
      cdm.capability->video_codecs[VideoCodec::kVP9].supported_profiles,
      media::VP9PROFILE_PROFILE2));
  EXPECT_TRUE(
      cdm.capability->video_codecs[VideoCodec::kVP9].supports_clear_lead);
}

TEST_F(CdmRegistryImplTest, SupportedEncryptionSchemes) {
  auto cdm_info = GetTestCdmInfo();
  cdm_info.capability->encryption_schemes = {EncryptionScheme::kCenc,
                                             EncryptionScheme::kCbcs};
  Register(cdm_info);

  std::vector<CdmInfo> cdms = cdm_registry_.GetRegisteredCdms();
  ASSERT_EQ(1u, cdms.size());
  const CdmInfo& cdm = cdms[0];
  EXPECT_ENCRYPTION_SCHEMES(EncryptionScheme::kCenc, EncryptionScheme::kCbcs);
}

TEST_F(CdmRegistryImplTest, GetCdmInfo_Success) {
  Register(GetTestCdmInfo());
  auto cdm_info = cdm_registry_.GetCdmInfo(
      kTestKeySystem, CdmInfo::Robustness::kSoftwareSecure);
  ASSERT_TRUE(cdm_info);

  const CdmInfo& cdm = *cdm_info;

  EXPECT_EQ(kTestCdmName, cdm.name);
  EXPECT_EQ(kVersion1, cdm.capability->version.GetString());
  EXPECT_EQ(kTestPath, cdm.path.MaybeAsASCII());
  EXPECT_EQ(kTestCdmType, cdm.type);
  EXPECT_VIDEO_CODECS(VideoCodec::kVP8, VideoCodec::kVP9);
  EXPECT_ENCRYPTION_SCHEMES(EncryptionScheme::kCenc);
  EXPECT_SESSION_TYPES(CdmSessionType::kTemporary,
                       CdmSessionType::kPersistentLicense);
  EXPECT_EQ(kTestKeySystem, cdm.key_system);
  EXPECT_TRUE(cdm.supports_sub_key_systems);
  EXPECT_EQ(cdm.robustness, CdmInfo::Robustness::kSoftwareSecure);
}

TEST_F(CdmRegistryImplTest, GetCdmInfo_Fail) {
  Register(GetTestCdmInfo());
  auto cdm_info = cdm_registry_.GetCdmInfo(
      kTestKeySystem, CdmInfo::Robustness::kHardwareSecure);
  ASSERT_FALSE(cdm_info);
}

TEST_F(CdmRegistryImplTest, KeySystemCapabilities_SoftwareSecure) {
  Register(GetTestCdmInfo());
  GetKeySystemCapabilities();

  ASSERT_TRUE(results_.count(kObserver1));
  ASSERT_EQ(results_[kObserver1].size(), 1u);
  auto& key_system_capabilities = results_[kObserver1][0];
  ASSERT_EQ(key_system_capabilities.size(), 1u);
  ASSERT_TRUE(key_system_capabilities.count(kTestKeySystem));
  const auto& support = key_system_capabilities[kTestKeySystem];
  ASSERT_EQ(support.sw_cdm_capability_or_status.value(),
            GetTestCdmCapability());
  ASSERT_FALSE(support.hw_cdm_capability_or_status.has_value());
}

TEST_F(CdmRegistryImplTest, KeySystemCapabilities_HardwareSecure) {
  Register(kTestKeySystem, GetTestCdmCapability(), Robustness::kHardwareSecure);
  SelectHardwareSecureDecryption(true);

  GetKeySystemCapabilities();

  ASSERT_TRUE(results_.count(kObserver1));
  ASSERT_EQ(results_[kObserver1].size(), 1u);
  auto& key_system_capabilities = results_[kObserver1][0];
  ASSERT_EQ(key_system_capabilities.size(), 1u);
  ASSERT_TRUE(key_system_capabilities.count(kTestKeySystem));
  const auto& support = key_system_capabilities[kTestKeySystem];
  ASSERT_FALSE(support.sw_cdm_capability_or_status.has_value());
  ASSERT_EQ(support.hw_cdm_capability_or_status.value(),
            GetTestCdmCapability());
}

TEST_F(CdmRegistryImplTest,
       KeySystemCapabilities_LazySoftwareSecureInitialize_Supported) {
  RegisterForLazySoftwareSecureInitialization();
  SelectHardwareSecureDecryption(true);

  EXPECT_CALL(capability_cb_,
              Run(kTestKeySystem, Robustness::kSoftwareSecure, _))
      .WillOnce(RunOnceCallback<2>(GetTestCdmCapability()));
  GetKeySystemCapabilities();

  ASSERT_TRUE(results_.count(kObserver1));
  ASSERT_EQ(results_[kObserver1].size(), 1u);
  auto& key_system_capabilities = results_[kObserver1][0];
  ASSERT_EQ(key_system_capabilities.size(), 1u);
  ASSERT_TRUE(key_system_capabilities.count(kTestKeySystem));
  const auto& support = key_system_capabilities[kTestKeySystem];
  ASSERT_EQ(support.sw_cdm_capability_or_status.value(),
            GetTestCdmCapability());
  ASSERT_FALSE(support.hw_cdm_capability_or_status.has_value());
}

TEST_F(CdmRegistryImplTest,
       KeySystemCapabilities_LazyHardwareSecureInitialize_Supported) {
  RegisterForLazyHardwareSecureInitialization();
  SelectHardwareSecureDecryption(true);

  EXPECT_CALL(capability_cb_,
              Run(kTestKeySystem, Robustness::kHardwareSecure, _))
      .WillOnce(RunOnceCallback<2>(GetTestCdmCapability()));
  GetKeySystemCapabilities();

  ASSERT_TRUE(results_.count(kObserver1));
  ASSERT_EQ(results_[kObserver1].size(), 1u);
  auto& key_system_capabilities = results_[kObserver1][0];
  ASSERT_EQ(key_system_capabilities.size(), 1u);
  ASSERT_TRUE(key_system_capabilities.count(kTestKeySystem));
  const auto& support = key_system_capabilities[kTestKeySystem];
  ASSERT_FALSE(support.sw_cdm_capability_or_status.has_value());
  ASSERT_EQ(support.hw_cdm_capability_or_status.value(),
            GetTestCdmCapability());
}

TEST_F(CdmRegistryImplTest,
       KeySystemCapabilities_LazySoftwareSecureInitialize_NotSupported) {
  RegisterForLazySoftwareSecureInitialization();
  SelectHardwareSecureDecryption(true);

  EXPECT_CALL(capability_cb_,
              Run(kTestKeySystem, Robustness::kSoftwareSecure, _))
      .WillOnce(RunOnceCallback<2>(
          base::unexpected(media::CdmCapabilityQueryStatus::kUnknown)));
  GetKeySystemCapabilities();

  ASSERT_TRUE(results_.count(kObserver1));
  ASSERT_EQ(results_[kObserver1].size(), 1u);
  auto& key_system_capabilities = results_[kObserver1][0];
  ASSERT_TRUE(key_system_capabilities.empty());

  auto cdm_info = cdm_registry_.GetCdmInfo(
      kTestKeySystem, CdmInfo::Robustness::kSoftwareSecure);
  ASSERT_EQ(cdm_info->status, CdmInfo::Status::kEnabled);
  ASSERT_FALSE(cdm_info->capability);
}

TEST_F(CdmRegistryImplTest,
       KeySystemCapabilities_LazyHardwareSecureInitialize_NotSupported) {
  RegisterForLazyHardwareSecureInitialization();
  SelectHardwareSecureDecryption(true);

  EXPECT_CALL(capability_cb_,
              Run(kTestKeySystem, Robustness::kHardwareSecure, _))
      .WillOnce(RunOnceCallback<2>(
          base::unexpected(media::CdmCapabilityQueryStatus::kUnknown)));
  GetKeySystemCapabilities();

  ASSERT_TRUE(results_.count(kObserver1));
  ASSERT_EQ(results_[kObserver1].size(), 1u);
  auto& key_system_capabilities = results_[kObserver1][0];
  ASSERT_TRUE(key_system_capabilities.empty());

  auto cdm_info = cdm_registry_.GetCdmInfo(
      kTestKeySystem, CdmInfo::Robustness::kHardwareSecure);
  ASSERT_EQ(cdm_info->status, CdmInfo::Status::kEnabled);
  ASSERT_FALSE(cdm_info->capability);
}

TEST_F(CdmRegistryImplTest, KeySystemCapabilities_HardwareSecureDisabled) {
  RegisterForLazyHardwareSecureInitialization();
  SelectHardwareSecureDecryption(false);
  GetKeySystemCapabilities();

  ASSERT_TRUE(results_.count(kObserver1));
  ASSERT_EQ(results_[kObserver1].size(), 1u);
  auto& key_system_capabilities = results_[kObserver1][0];
  ASSERT_TRUE(key_system_capabilities.empty());

  auto cdm_info = cdm_registry_.GetCdmInfo(
      kTestKeySystem, CdmInfo::Robustness::kHardwareSecure);
  ASSERT_EQ(cdm_info->status,
            CdmInfo::Status::kHardwareSecureDecryptionDisabled);
  ASSERT_FALSE(cdm_info->capability);
}

TEST_F(CdmRegistryImplTest, KeySystemCapabilities_SoftwareAndHardwareSecure) {
  RegisterForLazySoftwareSecureInitialization();
  RegisterForLazyHardwareSecureInitialization();
  SelectHardwareSecureDecryption(true);

  EXPECT_CALL(capability_cb_,
              Run(kTestKeySystem, Robustness::kSoftwareSecure, _))
      .WillOnce(RunOnceCallback<2>(GetTestCdmCapability()));
  EXPECT_CALL(capability_cb_,
              Run(kTestKeySystem, Robustness::kHardwareSecure, _))
      .WillOnce(RunOnceCallback<2>(GetOtherCdmCapability()));
  GetKeySystemCapabilities();

  ASSERT_TRUE(results_.count(kObserver1));
  ASSERT_EQ(results_[kObserver1].size(), 1u);
  auto& key_system_capabilities = results_[kObserver1][0];
  ASSERT_EQ(key_system_capabilities.size(), 1u);
  ASSERT_TRUE(key_system_capabilities.count(kTestKeySystem));
  const auto& support = key_system_capabilities[kTestKeySystem];
  ASSERT_EQ(support.sw_cdm_capability_or_status.value(),
            GetTestCdmCapability());
  ASSERT_EQ(support.hw_cdm_capability_or_status.value(),
            GetOtherCdmCapability());
}

TEST_F(CdmRegistryImplTest, KeySystemCapabilities_MultipleObservers) {
  Register(GetTestCdmInfo());

  base::RunLoop run_loop;
  auto subscription1 = cdm_registry_.ObserveKeySystemCapabilities(
      /*allow_hw_secure_capability_check=*/true,
      base::BindRepeating(&CdmRegistryImplTest::OnKeySystemCapabilitiesUpdated,
                          base::Unretained(this), kObserver1,
                          base::DoNothing()));
  auto subscription2 = cdm_registry_.ObserveKeySystemCapabilities(
      /*allow_hw_secure_capability_check=*/true,
      base::BindRepeating(&CdmRegistryImplTest::OnKeySystemCapabilitiesUpdated,
                          base::Unretained(this), kObserver2,
                          run_loop.QuitClosure()));
  run_loop.Run();

  ASSERT_TRUE(results_.count(kObserver1));
  ASSERT_EQ(results_[kObserver1].size(), 1u);
  auto& key_system_capabilities = results_[kObserver1][0];
  ASSERT_EQ(key_system_capabilities.size(), 1u);
  ASSERT_TRUE(key_system_capabilities.count(kTestKeySystem));
  const auto& support = key_system_capabilities[kTestKeySystem];
  ASSERT_EQ(support.sw_cdm_capability_or_status.value(),
            GetTestCdmCapability());

  ASSERT_TRUE(results_.count(kObserver2));
  ASSERT_EQ(results_[kObserver2].size(), 1u);
  ASSERT_EQ(key_system_capabilities, results_[kObserver2][0]);
}

TEST_F(
    CdmRegistryImplTest,
    KeySystemCapabilities_MultipleObservers_PendingLazySoftwareSecureInitialize) {
  RegisterForLazySoftwareSecureInitialization();
  SelectHardwareSecureDecryption(false);

  EXPECT_CALL(capability_cb_,
              Run(kTestKeySystem, Robustness::kSoftwareSecure, _))
      .WillOnce(RunOnceCallback<2>(GetTestCdmCapability()));

  base::RunLoop run_loop;
  auto subscription1 = cdm_registry_.ObserveKeySystemCapabilities(
      /*allow_hw_secure_capability_check=*/true,
      base::BindRepeating(&CdmRegistryImplTest::OnKeySystemCapabilitiesUpdated,
                          base::Unretained(this), kObserver1,
                          base::DoNothing()));
  auto subscription2 = cdm_registry_.ObserveKeySystemCapabilities(
      /*allow_hw_secure_capability_check=*/true,
      base::BindRepeating(&CdmRegistryImplTest::OnKeySystemCapabilitiesUpdated,
                          base::Unretained(this), kObserver2,
                          run_loop.QuitClosure()));
  run_loop.Run();

  ASSERT_TRUE(results_.count(kObserver1));
  ASSERT_EQ(results_[kObserver1].size(), 1u);
  auto& key_system_capabilities = results_[kObserver1][0];
  ASSERT_EQ(key_system_capabilities.size(), 1u);
  ASSERT_TRUE(key_system_capabilities.count(kTestKeySystem));
  const auto& support = key_system_capabilities[kTestKeySystem];
  ASSERT_EQ(support.sw_cdm_capability_or_status.value(),
            GetTestCdmCapability());
  ASSERT_FALSE(support.hw_cdm_capability_or_status.has_value());

  ASSERT_TRUE(results_.count(kObserver2));
  ASSERT_EQ(results_[kObserver2].size(), 1u);
  ASSERT_EQ(key_system_capabilities, results_[kObserver2][0]);
}

TEST_F(
    CdmRegistryImplTest,
    KeySystemCapabilities_MultipleObservers_PendingLazyHardwareSecureInitialize) {
  Register(GetTestCdmInfo());
  RegisterForLazyHardwareSecureInitialization();
  SelectHardwareSecureDecryption(true);

  EXPECT_CALL(capability_cb_,
              Run(kTestKeySystem, Robustness::kHardwareSecure, _))
      .WillOnce(RunOnceCallback<2>(GetTestCdmCapability()));

  base::RunLoop run_loop;
  auto subscription1 = cdm_registry_.ObserveKeySystemCapabilities(
      /*allow_hw_secure_capability_check=*/true,
      base::BindRepeating(&CdmRegistryImplTest::OnKeySystemCapabilitiesUpdated,
                          base::Unretained(this), kObserver1,
                          base::DoNothing()));
  auto subscription2 = cdm_registry_.ObserveKeySystemCapabilities(
      /*allow_hw_secure_capability_check=*/true,
      base::BindRepeating(&CdmRegistryImplTest::OnKeySystemCapabilitiesUpdated,
                          base::Unretained(this), kObserver2,
                          run_loop.QuitClosure()));
  run_loop.Run();

  ASSERT_TRUE(results_.count(kObserver1));
  ASSERT_EQ(results_[kObserver1].size(), 1u);
  auto& key_system_capabilities = results_[kObserver1][0];
  ASSERT_EQ(key_system_capabilities.size(), 1u);
  ASSERT_TRUE(key_system_capabilities.count(kTestKeySystem));
  const auto& support = key_system_capabilities[kTestKeySystem];
  ASSERT_EQ(support.sw_cdm_capability_or_status.value(),
            GetTestCdmCapability());
  ASSERT_EQ(support.hw_cdm_capability_or_status.value(),
            GetTestCdmCapability());

  ASSERT_TRUE(results_.count(kObserver2));
  ASSERT_EQ(results_[kObserver2].size(), 1u);
  ASSERT_EQ(key_system_capabilities, results_[kObserver2][0]);
}

TEST_F(
    CdmRegistryImplTest,
    KeySystemCapabilities_MultipleObservers_AfterLazyHardwareSecureInitialize) {
  Register(GetTestCdmInfo());
  RegisterForLazyHardwareSecureInitialization();
  SelectHardwareSecureDecryption(true);

  EXPECT_CALL(capability_cb_,
              Run(kTestKeySystem, Robustness::kHardwareSecure, _))
      .WillOnce(RunOnceCallback<2>(GetTestCdmCapability()));

  base::CallbackListSubscription subscription1;
  {
    base::RunLoop run_loop;
    subscription1 = cdm_registry_.ObserveKeySystemCapabilities(
        /*allow_hw_secure_capability_check=*/true,
        base::BindRepeating(
            &CdmRegistryImplTest::OnKeySystemCapabilitiesUpdated,
            base::Unretained(this), kObserver1, run_loop.QuitClosure()));
    run_loop.Run();
  }
  base::CallbackListSubscription subscription2;
  {
    base::RunLoop run_loop;
    subscription2 = cdm_registry_.ObserveKeySystemCapabilities(
        /*allow_hw_secure_capability_check=*/true,
        base::BindRepeating(
            &CdmRegistryImplTest::OnKeySystemCapabilitiesUpdated,
            base::Unretained(this), kObserver2, run_loop.QuitClosure()));
    run_loop.Run();
  }

  ASSERT_TRUE(results_.count(kObserver1));
  ASSERT_EQ(results_[kObserver1].size(), 1u);
  auto& key_system_capabilities = results_[kObserver1][0];
  ASSERT_EQ(key_system_capabilities.size(), 1u);
  ASSERT_TRUE(key_system_capabilities.count(kTestKeySystem));
  const auto& support = key_system_capabilities[kTestKeySystem];
  ASSERT_EQ(support.sw_cdm_capability_or_status.value(),
            GetTestCdmCapability());
  ASSERT_EQ(support.hw_cdm_capability_or_status.value(),
            GetTestCdmCapability());

  ASSERT_TRUE(results_.count(kObserver2));
  ASSERT_EQ(results_[kObserver2].size(), 1u);
  ASSERT_EQ(key_system_capabilities, results_[kObserver2][0]);
}

TEST_F(CdmRegistryImplTest, KeySystemCapabilities_RegisterCdmAfterObserving) {
  Register(GetTestCdmInfo());
  SelectHardwareSecureDecryption(true);

  base::CallbackListSubscription subscription;
  {
    base::RunLoop run_loop;
    subscription = cdm_registry_.ObserveKeySystemCapabilities(
        /*allow_hw_secure_capability_check=*/true,
        base::BindRepeating(
            &CdmRegistryImplTest::OnKeySystemCapabilitiesUpdated,
            base::Unretained(this), kObserver1, run_loop.QuitClosure()));
    run_loop.Run();
  }

  // Hardware security not supported for `kTestKeySystem`.
  ASSERT_TRUE(results_.count(kObserver1));
  ASSERT_EQ(results_[kObserver1].size(), 1u);
  auto& key_system_capabilities_1 = results_[kObserver1][0];
  ASSERT_EQ(key_system_capabilities_1.size(), 1u);
  ASSERT_TRUE(key_system_capabilities_1.count(kTestKeySystem));
  const auto& support_1 = key_system_capabilities_1[kTestKeySystem];
  ASSERT_EQ(support_1.sw_cdm_capability_or_status.value(),
            GetTestCdmCapability());
  ASSERT_FALSE(support_1.hw_cdm_capability_or_status.has_value());

  {
    base::RunLoop run_loop;
    EXPECT_CALL(capability_cb_,
                Run(kTestKeySystem, Robustness::kHardwareSecure, _))
        .WillOnce(RunOnceCallback<2>(GetOtherCdmCapability()));
    RegisterForLazyHardwareSecureInitialization();
    run_loop.RunUntilIdle();
  }

  // Now hardware security is supported for `kTestKeySystem`.
  ASSERT_TRUE(results_.count(kObserver1));
  ASSERT_EQ(results_[kObserver1].size(), 2u);
  auto& key_system_capabilities_2 = results_[kObserver1][1];
  ASSERT_EQ(key_system_capabilities_2.size(), 1u);
  ASSERT_TRUE(key_system_capabilities_2.count(kTestKeySystem));
  const auto& support_2 = key_system_capabilities_2[kTestKeySystem];
  ASSERT_EQ(support_2.sw_cdm_capability_or_status.value(),
            GetTestCdmCapability());
  ASSERT_EQ(support_2.hw_cdm_capability_or_status.value(),
            GetOtherCdmCapability());
}

TEST_F(CdmRegistryImplTest,
       KeySystemCapabilities_RegisterCdmPendingLazyInitialize) {
  SelectHardwareSecureDecryption(true);

  // Save the callbacks so we can control when and how they are fired.
  base::OnceCallback<void(media::CdmCapabilityOrStatus)> callback_1, callback_2,
      callback_3;
  EXPECT_CALL(capability_cb_,
              Run(kTestKeySystem, Robustness::kHardwareSecure, _))
      .WillOnce(MoveArg<2>(&callback_1))
      .WillOnce(MoveArg<2>(&callback_2));
  EXPECT_CALL(capability_cb_,
              Run(kOtherKeySystem, Robustness::kHardwareSecure, _))
      .WillOnce(MoveArg<2>(&callback_3));

  // Register CdmInfo for lazy initialization.
  base::CallbackListSubscription subscription;
  {
    base::RunLoop run_loop;
    Register(CdmInfo(kTestKeySystem, CdmInfo::Robustness::kHardwareSecure,
                     std::nullopt, kTestCdmType));
    subscription = cdm_registry_.ObserveKeySystemCapabilities(
        /*allow_hw_secure_capability_check=*/true,
        base::BindRepeating(
            &CdmRegistryImplTest::OnKeySystemCapabilitiesUpdated,
            base::Unretained(this), kObserver1, base::DoNothing()));
    run_loop.RunUntilIdle();
  }

  // No results since we are still pending lazy initialization.
  ASSERT_TRUE(results_.empty());

  // Register CdmInfo for the same key system with a CdmCapability.
  {
    base::RunLoop run_loop;
    // Register a CdmInfo without CdmCapability to allow lazy initialization.
    Register(CdmInfo(kOtherKeySystem, CdmInfo::Robustness::kHardwareSecure,
                     std::nullopt, kTestCdmType));
    std::move(callback_1).Run(GetTestCdmCapability());
    std::move(callback_2).Run(GetTestCdmCapability());
    std::move(callback_3).Run(GetOtherCdmCapability());
    run_loop.RunUntilIdle();
  }

  // The observer should only be updated once with both key systems.
  ASSERT_TRUE(results_.count(kObserver1));
  ASSERT_EQ(results_[kObserver1].size(), 1u);
  auto& key_system_capabilities = results_[kObserver1][0];
  ASSERT_EQ(key_system_capabilities.size(), 2u);

  ASSERT_TRUE(key_system_capabilities.count(kTestKeySystem));
  const auto& support = key_system_capabilities[kTestKeySystem];
  ASSERT_FALSE(support.sw_cdm_capability_or_status.has_value());
  ASSERT_EQ(support.hw_cdm_capability_or_status.value(),
            GetTestCdmCapability());

  ASSERT_TRUE(key_system_capabilities.count(kOtherKeySystem));
  const auto& other_support = key_system_capabilities[kOtherKeySystem];
  ASSERT_FALSE(other_support.sw_cdm_capability_or_status.has_value());
  ASSERT_EQ(other_support.hw_cdm_capability_or_status.value(),
            GetOtherCdmCapability());
}

TEST_F(CdmRegistryImplTest, KeySystemCapabilities_DisableHardwareSecureCdms) {
  Register(GetTestCdmInfo());
  Register(kTestKeySystem, GetTestCdmCapability(), Robustness::kHardwareSecure);
  SelectHardwareSecureDecryption(true);

  GetKeySystemCapabilities();

  // Both software and hardware security are supported for `kTestKeySystem`.
  ASSERT_TRUE(results_.count(kObserver1));
  ASSERT_EQ(results_[kObserver1].size(), 1u);
  auto& key_system_capabilities_1 = results_[kObserver1][0];
  ASSERT_EQ(key_system_capabilities_1.size(), 1u);
  ASSERT_TRUE(key_system_capabilities_1.count(kTestKeySystem));
  const auto& support_1 = key_system_capabilities_1[kTestKeySystem];
  ASSERT_EQ(support_1.sw_cdm_capability_or_status.value(),
            GetTestCdmCapability());
  ASSERT_EQ(support_1.hw_cdm_capability_or_status.value(),
            GetTestCdmCapability());

  {
    base::RunLoop run_loop;
    cdm_registry_.SetHardwareSecureCdmStatus(CdmInfo::Status::kDisabledOnError);
    run_loop.RunUntilIdle();
  }

  // Now hardware security is NOT supported for `kTestKeySystem`. Software
  // security is not affected.
  ASSERT_TRUE(results_.count(kObserver1));
  ASSERT_EQ(results_[kObserver1].size(), 2u);
  auto& key_system_capabilities_2 = results_[kObserver1][1];
  ASSERT_EQ(key_system_capabilities_2.size(), 1u);
  ASSERT_TRUE(key_system_capabilities_2.count(kTestKeySystem));
  const auto& support_2 = key_system_capabilities_2[kTestKeySystem];
  ASSERT_EQ(support_2.sw_cdm_capability_or_status.value(),
            GetTestCdmCapability());
  ASSERT_FALSE(support_2.hw_cdm_capability_or_status.has_value());
}

#if BUILDFLAG(IS_WIN)
TEST_F(CdmRegistryImplTest, KeySystemCapabilities_DirectCompositionDisabled) {
  // Simulate disabling direct composition.
  gpu::GPUInfo gpu_info;
  gpu_info.overlay_info.direct_composition = false;
  GpuDataManagerImpl::GetInstance()->UpdateGpuInfo(gpu_info, std::nullopt);

  RegisterForLazyHardwareSecureInitialization();
  SelectHardwareSecureDecryption(true);
  GetKeySystemCapabilities();

  ASSERT_TRUE(results_.count(kObserver1));
  ASSERT_EQ(results_[kObserver1].size(), 1u);
  auto& key_system_capabilities = results_[kObserver1][0];
  ASSERT_TRUE(key_system_capabilities.empty());

  auto cdm_info = cdm_registry_.GetCdmInfo(
      kTestKeySystem, CdmInfo::Robustness::kHardwareSecure);
  ASSERT_EQ(cdm_info->status, CdmInfo::Status::kGpuCompositionDisabled);
  ASSERT_FALSE(cdm_info->capability);
}

TEST_F(CdmRegistryImplTest,
       KeySystemCapabilities_DisabledBySoftwareEmulatedGpu) {
  // Simulate enabling direct composition and software emulated GPU.
  gpu::GPUInfo gpu_info;
  gpu_info.overlay_info.direct_composition = true;
  gpu_info.gpu.vendor_id = kSoftwareEmulatedVendorId;
  GpuDataManagerImpl::GetInstance()->UpdateGpuInfo(gpu_info, std::nullopt);

  RegisterForLazyHardwareSecureInitialization();
  SelectHardwareSecureDecryption(true);
  GetKeySystemCapabilities();

  ASSERT_TRUE(results_.count(kObserver1));
  ASSERT_EQ(results_[kObserver1].size(), 1u);
  auto& key_system_capabilities = results_[kObserver1][0];
  ASSERT_TRUE(key_system_capabilities.empty());

  auto cdm_info = cdm_registry_.GetCdmInfo(
      kTestKeySystem, CdmInfo::Robustness::kHardwareSecure);
  ASSERT_EQ(cdm_info->status, CdmInfo::Status::kDisabledBySoftwareEmulatedGpu);
  ASSERT_FALSE(cdm_info->capability);
}
#endif  // BUILDFLAG(IS_WIN)

TEST_F(CdmRegistryImplTest,
       KeySystemCapabilities_HwCapabilityNotAllowedToAllowed) {
  RegisterForLazyHardwareSecureInitialization();
  SelectHardwareSecureDecryption(true);

  // Start with hw capability check not allowed and observe that we don't get
  // capability.
  GetKeySystemCapabilities(/*allow_hw_secure_capability_check=*/false);

  ASSERT_TRUE(results_.count(kObserver1));
  ASSERT_EQ(results_[kObserver1].size(), 1u);
  auto& key_system_capabilities = results_[kObserver1][0];
  ASSERT_TRUE(key_system_capabilities.empty());
  auto cdm_info = cdm_registry_.GetCdmInfo(
      kTestKeySystem, CdmInfo::Robustness::kHardwareSecure);
  EXPECT_EQ(cdm_info->status, CdmInfo::Status::kUninitialized);
  EXPECT_FALSE(cdm_info->capability);

  // Now we allow hw capability check and expect that we get capability.
  EXPECT_CALL(capability_cb_,
              Run(kTestKeySystem, Robustness::kHardwareSecure, _))
      .WillOnce(RunOnceCallback<2>(GetTestCdmCapability()));
  GetKeySystemCapabilities(/*allow_hw_secure_capability_check=*/true);

  ASSERT_TRUE(results_.count(kObserver1));
  ASSERT_EQ(results_[kObserver1].size(), 2u);
  auto& key_system_capabilities2 = results_[kObserver1][1];
  ASSERT_FALSE(key_system_capabilities2.empty());
  auto cdm_info2 = cdm_registry_.GetCdmInfo(
      kTestKeySystem, CdmInfo::Robustness::kHardwareSecure);

  EXPECT_EQ(cdm_info2->status, CdmInfo::Status::kEnabled);
  EXPECT_TRUE(cdm_info2->capability);
}

TEST_F(CdmRegistryImplTest,
       KeySystemCapabilities_HwCapabilityAllowedToNotAllowed) {
  RegisterForLazyHardwareSecureInitialization();
  SelectHardwareSecureDecryption(true);

  // Start with hw capability check allowed and observe that we get capability.
  EXPECT_CALL(capability_cb_,
              Run(kTestKeySystem, Robustness::kHardwareSecure, _))
      .WillOnce(RunOnceCallback<2>(GetTestCdmCapability()));
  GetKeySystemCapabilities(/*allow_hw_secure_capability_check=*/true);

  ASSERT_TRUE(results_.count(kObserver1));
  ASSERT_EQ(results_[kObserver1].size(), 1u);
  auto& key_system_capabilities = results_[kObserver1][0];
  ASSERT_FALSE(key_system_capabilities.empty());
  auto cdm_info = cdm_registry_.GetCdmInfo(
      kTestKeySystem, CdmInfo::Robustness::kHardwareSecure);

  EXPECT_EQ(cdm_info->status, CdmInfo::Status::kEnabled);
  EXPECT_EQ(cdm_info->capability, GetTestCdmCapability());

  // Now we don't allow hw capability check, but still expect that we get
  // capability. Note that we don't EXPECT_CALL to capability since the
  // CdmRegistry just returns the cached capability.
  GetKeySystemCapabilities(/*allow_hw_secure_capability_check=*/false);

  ASSERT_TRUE(results_.count(kObserver1));
  ASSERT_EQ(results_[kObserver1].size(), 2u);
  auto& key_system_capabilities2 = results_[kObserver1][1];
  ASSERT_FALSE(key_system_capabilities2.empty());
  auto cdm_info2 = cdm_registry_.GetCdmInfo(
      kTestKeySystem, CdmInfo::Robustness::kHardwareSecure);

  EXPECT_EQ(cdm_info2->status, CdmInfo::Status::kEnabled);
  EXPECT_TRUE(cdm_info2->capability);
}

TEST_F(CdmRegistryImplTest,
       KeySystemCapabilities_HwCapabilityTwoObserverNotAllowedAndAllowed) {
  RegisterForLazyHardwareSecureInitialization();
  SelectHardwareSecureDecryption(true);

  EXPECT_CALL(capability_cb_,
              Run(kTestKeySystem, Robustness::kHardwareSecure, _))
      .WillOnce(RunOnceCallback<2>(GetTestCdmCapability()));

  base::RunLoop run_loop;
  auto subscription1 = cdm_registry_.ObserveKeySystemCapabilities(
      /*allow_hw_secure_capability_check=*/false,
      base::BindRepeating(&CdmRegistryImplTest::OnKeySystemCapabilitiesUpdated,
                          base::Unretained(this), kObserver1,
                          run_loop.QuitClosure()));
  run_loop.Run();

  ASSERT_TRUE(results_.count(kObserver1));
  ASSERT_EQ(results_[kObserver1].size(), 1u);
  auto& key_system_capabilities = results_[kObserver1][0];
  ASSERT_TRUE(key_system_capabilities.empty());

  base::RunLoop run_loop2;
  auto subscription2 = cdm_registry_.ObserveKeySystemCapabilities(
      /*allow_hw_secure_capability_check=*/true,
      base::BindRepeating(&CdmRegistryImplTest::OnKeySystemCapabilitiesUpdated,
                          base::Unretained(this), kObserver2,
                          run_loop2.QuitClosure()));
  run_loop2.Run();

  ASSERT_TRUE(results_.count(kObserver2));
  ASSERT_EQ(results_[kObserver2].size(), 1u);
  auto& key_system_capabilities2 = results_[kObserver2][0];
  const auto& support = key_system_capabilities2[kTestKeySystem];
  ASSERT_EQ(support.hw_cdm_capability_or_status.value(),
            GetTestCdmCapability());
}

TEST_F(
    CdmRegistryImplTest,
    KeySystemCapabilities_MultipleObservers_NotAllowedAndAllowedHwCapabilityCheck) {
  RegisterForLazySoftwareSecureInitialization();
  RegisterForLazyHardwareSecureInitialization();
  SelectHardwareSecureDecryption(true);

  // Expect the lazy software capability to be triggered twice because the
  // second observer will invalidate pending initializations and retrigger them.
  EXPECT_CALL(capability_cb_,
              Run(kTestKeySystem, Robustness::kSoftwareSecure, _))
      .Times(2)
      .WillRepeatedly(
          base::test::RunOnceCallbackRepeatedly<2>(GetTestCdmCapability()));

  EXPECT_CALL(capability_cb_,
              Run(kTestKeySystem, Robustness::kHardwareSecure, _))
      .WillOnce(RunOnceCallback<2>(GetTestCdmCapability()));

  base::RunLoop run_loop;
  auto subscription1 = cdm_registry_.ObserveKeySystemCapabilities(
      /*allow_hw_secure_capability_check=*/false,
      base::BindRepeating(&CdmRegistryImplTest::OnKeySystemCapabilitiesUpdated,
                          base::Unretained(this), kObserver1,
                          base::DoNothing()));
  auto subscription2 = cdm_registry_.ObserveKeySystemCapabilities(
      /*allow_hw_secure_capability_check=*/true,
      base::BindRepeating(&CdmRegistryImplTest::OnKeySystemCapabilitiesUpdated,
                          base::Unretained(this), kObserver2,
                          run_loop.QuitClosure()));
  run_loop.Run();

  ASSERT_TRUE(results_.count(kObserver1));
  ASSERT_EQ(results_[kObserver1].size(), 1u);
  auto& key_system_capabilities = results_[kObserver1][0];
  ASSERT_EQ(key_system_capabilities.size(), 1u);
  ASSERT_TRUE(key_system_capabilities.count(kTestKeySystem));
  const auto& support = key_system_capabilities[kTestKeySystem];
  ASSERT_EQ(support.sw_cdm_capability_or_status.value(),
            GetTestCdmCapability());
  ASSERT_EQ(support.hw_cdm_capability_or_status.value(),
            GetTestCdmCapability());

  ASSERT_TRUE(results_.count(kObserver2));
  ASSERT_EQ(results_[kObserver2].size(), 1u);
  ASSERT_EQ(key_system_capabilities, results_[kObserver2][0]);
}

TEST_F(CdmRegistryImplTest, KeySystemCapabilities_NoOverride) {
#if BUILDFLAG(IS_ANDROID)
  DisableMediaDrmQueryInSeparateProcess();
#endif

  // kTestKeySystem doesn't exist on any platform, but this should at least
  // exercise a bit more of the code (and leave the capabilities as nullptr).
  RegisterForLazySoftwareSecureInitialization();

  // Don't use the testing callback.
  ClearCapabilityTestOverride();
  EXPECT_CALL(capability_cb_, Run(_, _, _)).Times(0);

  GetKeySystemCapabilities();

  ASSERT_TRUE(results_.count(kObserver1));
  ASSERT_EQ(results_[kObserver1].size(), 1u);
  auto& key_system_capabilities = results_[kObserver1][0];
  ASSERT_EQ(key_system_capabilities.size(), 0u);
}

}  // namespace content