blob: 743d5a721c72962f2ee8ca5e5412ff63e3de0e8f [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/media/key_system_support_impl.h"
#include <string>
#include <vector>
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.h"
#include "base/token.h"
#include "content/browser/media/cdm_registry_impl.h"
#include "content/public/browser/cdm_registry.h"
#include "content/public/browser/plugin_service.h"
#include "content/public/common/cdm_info.h"
#include "content/public/common/webplugininfo.h"
#include "content/public/test/browser_task_environment.h"
#include "media/base/decrypt_config.h"
#include "media/base/media_switches.h"
#include "media/base/video_codecs.h"
#include "media/cdm/cdm_capability.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
namespace {
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 Content Decryption Module";
const base::Token kTestCdmGuid{1234, 5678};
const char kVersion[] = "1.1.1.1";
const char kTestPath[] = "/aa/bb";
const char kTestFileSystemId[] = "file_system_id";
// Helper function to compare a STL container to an initializer_list.
template <typename Container, typename T>
bool StlEquals(const Container a, std::initializer_list<T> b) {
return a == Container(b);
}
#define EXPECT_STL_EQ(container, ...) \
do { \
EXPECT_THAT(container, ::testing::ElementsAre(__VA_ARGS__)); \
} while (false)
#define EXPECT_VIDEO_CODECS(...) \
EXPECT_STL_EQ(capability_->sw_secure_capability->video_codecs, __VA_ARGS__)
#define EXPECT_ENCRYPTION_SCHEMES(...) \
EXPECT_STL_EQ(capability_->sw_secure_capability->encryption_schemes, \
__VA_ARGS__)
#define EXPECT_SESSION_TYPES(...) \
EXPECT_STL_EQ(capability_->sw_secure_capability->session_types, __VA_ARGS__)
#define EXPECT_HW_SECURE_VIDEO_CODECS(...) \
EXPECT_STL_EQ(capability_->hw_secure_capability->video_codecs, __VA_ARGS__)
#define EXPECT_HW_SECURE_ENCRYPTION_SCHEMES(...) \
EXPECT_STL_EQ(capability_->hw_secure_capability->encryption_schemes, \
__VA_ARGS__)
#define EXPECT_HW_SECURE_SESSION_TYPES(...) \
EXPECT_STL_EQ(capability_->hw_secure_capability->session_types, __VA_ARGS__)
} // namespace
class KeySystemSupportImplTest : public testing::Test {
protected:
void SetUp() final {
DVLOG(1) << __func__;
// As `CdmRegistryImpl::GetInstance()` is a static, explicitly reset
// `CdmRegistryImpl` so each test starts with a clean state.
CdmRegistryImpl::GetInstance()->ResetForTesting();
auto key_system_support_impl = std::make_unique<KeySystemSupportImpl>();
key_system_support_impl->SetHardwareSecureCapabilityCBForTesting(
hw_secure_capability_cb_.Get());
mojo::MakeSelfOwnedReceiver(
std::move(key_system_support_impl),
key_system_support_.BindNewPipeAndPassReceiver());
}
media::CdmCapability TestCdmCapability() {
return media::CdmCapability(
{VideoCodec::kCodecVP8, VideoCodec::kCodecVP9},
{EncryptionScheme::kCenc, EncryptionScheme::kCbcs},
{CdmSessionType::kTemporary, CdmSessionType::kPersistentLicense});
}
// Registers |key_system| with |capability|. All other values for CdmInfo have
// some default value as they're not returned by IsKeySystemSupported().
void Register(const std::string& key_system,
base::Optional<media::CdmCapability> capability,
Robustness robustness = Robustness::kSoftwareSecure) {
DVLOG(1) << __func__;
CdmRegistry::GetInstance()->RegisterCdm(
CdmInfo(key_system, robustness, std::move(capability),
/*supports_sub_key_systems=*/false, kTestCdmName, kTestCdmGuid,
base::Version(kVersion),
base::FilePath::FromUTF8Unsafe(kTestPath), kTestFileSystemId));
}
// Determines if |key_system| is registered. If it is, updates |codecs_|
// and |persistent_|.
bool IsSupported(const std::string& key_system) {
DVLOG(1) << __func__;
bool is_supported = false;
key_system_support_->IsKeySystemSupported(key_system, &is_supported,
&capability_);
return is_supported;
}
mojo::Remote<media::mojom::KeySystemSupport> key_system_support_;
base::MockCallback<KeySystemSupportImpl::HardwareSecureCapabilityCB>
hw_secure_capability_cb_;
base::test::ScopedFeatureList scoped_feature_list_;
BrowserTaskEnvironment task_environment_;
// Updated by IsSupported().
media::mojom::KeySystemCapabilityPtr capability_;
};
TEST_F(KeySystemSupportImplTest, NoKeySystems) {
EXPECT_FALSE(IsSupported("KeySystem"));
EXPECT_FALSE(capability_);
}
TEST_F(KeySystemSupportImplTest, SoftwareSecureCapability) {
Register("KeySystem", TestCdmCapability());
EXPECT_TRUE(IsSupported("KeySystem"));
EXPECT_TRUE(capability_->sw_secure_capability);
EXPECT_FALSE(capability_->hw_secure_capability);
EXPECT_VIDEO_CODECS(VideoCodec::kCodecVP8, VideoCodec::kCodecVP9);
EXPECT_ENCRYPTION_SCHEMES(EncryptionScheme::kCenc, EncryptionScheme::kCbcs);
EXPECT_SESSION_TYPES(CdmSessionType::kTemporary,
CdmSessionType::kPersistentLicense);
}
TEST_F(KeySystemSupportImplTest,
HardwareSecureCapability_HardwareSecureDecryptionDisabled) {
scoped_feature_list_.InitAndDisableFeature(media::kHardwareSecureDecryption);
Register("KeySystem", TestCdmCapability(), Robustness::kHardwareSecure);
EXPECT_FALSE(IsSupported("KeySystem"));
}
TEST_F(KeySystemSupportImplTest, HardwareSecureCapability) {
scoped_feature_list_.InitAndEnableFeature(media::kHardwareSecureDecryption);
Register("KeySystem", TestCdmCapability(), Robustness::kHardwareSecure);
EXPECT_TRUE(IsSupported("KeySystem"));
EXPECT_FALSE(capability_->sw_secure_capability);
EXPECT_TRUE(capability_->hw_secure_capability);
EXPECT_HW_SECURE_VIDEO_CODECS(VideoCodec::kCodecVP8, VideoCodec::kCodecVP9);
EXPECT_HW_SECURE_ENCRYPTION_SCHEMES(EncryptionScheme::kCenc,
EncryptionScheme::kCbcs);
EXPECT_HW_SECURE_SESSION_TYPES(CdmSessionType::kTemporary,
CdmSessionType::kPersistentLicense);
}
TEST_F(KeySystemSupportImplTest, MultipleKeySystems) {
Register("KeySystem1", TestCdmCapability());
Register("KeySystem2", TestCdmCapability());
EXPECT_TRUE(IsSupported("KeySystem1"));
EXPECT_TRUE(IsSupported("KeySystem2"));
}
TEST_F(KeySystemSupportImplTest, MissingKeySystem) {
Register("KeySystem", TestCdmCapability());
EXPECT_FALSE(IsSupported("KeySystem1"));
EXPECT_FALSE(capability_);
}
TEST_F(KeySystemSupportImplTest, LazyInitialize_Supported) {
scoped_feature_list_.InitAndEnableFeature(media::kHardwareSecureDecryption);
Register("KeySystem", base::nullopt, Robustness::kHardwareSecure);
EXPECT_CALL(hw_secure_capability_cb_, Run("KeySystem", _))
.WillOnce(RunOnceCallback<1>(TestCdmCapability()));
EXPECT_TRUE(IsSupported("KeySystem"));
EXPECT_TRUE(capability_);
// Calling IsSupported() again should not trigger `hw_secure_capability_cb_`.
EXPECT_TRUE(IsSupported("KeySystem"));
EXPECT_TRUE(capability_);
}
TEST_F(KeySystemSupportImplTest, LazyInitialize_NotSupported) {
scoped_feature_list_.InitAndEnableFeature(media::kHardwareSecureDecryption);
Register("KeySystem", base::nullopt, Robustness::kHardwareSecure);
EXPECT_CALL(hw_secure_capability_cb_, Run("KeySystem", _))
.WillOnce(RunOnceCallback<1>(base::nullopt));
EXPECT_FALSE(IsSupported("KeySystem"));
EXPECT_FALSE(capability_);
// Calling IsSupported() again should not trigger `hw_secure_capability_cb_`.
EXPECT_FALSE(IsSupported("KeySystem"));
EXPECT_FALSE(capability_);
}
TEST_F(KeySystemSupportImplTest,
LazyInitialize_HardwareSecureDecryptionDisabled) {
scoped_feature_list_.InitAndDisableFeature(media::kHardwareSecureDecryption);
Register("KeySystem", base::nullopt, Robustness::kHardwareSecure);
EXPECT_FALSE(IsSupported("KeySystem"));
EXPECT_FALSE(capability_);
}
} // namespace content