blob: ab414082941d7afb951fc0eb4ac2bd343525a69f [file] [log] [blame]
// Copyright 2020 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 "chromeos/components/cdm_factory_daemon/content_decryption_module_adapter.h"
#include <algorithm>
#include "base/logging.h"
#include "base/test/mock_callback.h"
#include "base/test/task_environment.h"
#include "media/base/decoder_buffer.h"
#include "media/base/mock_filters.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using DaemonCdm = chromeos::cdm::mojom::ContentDecryptionModule;
using testing::_;
using testing::IsNull;
namespace chromeos {
namespace {
constexpr char kFakeEmeInitData[] = "fake_init_data";
const std::vector<uint8_t> kFakeEncryptedData = {42, 22, 26, 13, 7, 16, 8, 2};
const std::vector<uint8_t> kFakeSideData = {36, 24, 36};
constexpr char kFakeKeyId[] = "fake_key_id";
constexpr char kFakeIv[] = "fake_iv_16_bytes";
constexpr char kFakeServiceCertificate[] = "fake_service_cert";
constexpr char kFakeSessionId1[] = "fakeSid1";
constexpr char kFakeSessionId2[] = "fakeSid2";
constexpr char kFakeSessionUpdate[] = "fake_session_update";
constexpr int64_t kFakeTimestampSec = 42;
constexpr int64_t kFakeDurationSec = 64;
template <size_t size>
std::vector<uint8_t> ToVector(const char (&array)[size]) {
return std::vector<uint8_t>(array, array + size - 1);
}
MATCHER_P(MatchesDecoderBuffer, buffer, "") {
DCHECK(arg);
return arg->MatchesForTesting(*buffer);
}
MATCHER_P(MatchesDecryptConfig, config, "") {
DCHECK(arg);
return arg.Equals(*config);
}
// Mock of the mojo implementation on the Chrome OS side.
class MockDaemonCdm : public cdm::mojom::ContentDecryptionModule {
public:
MockDaemonCdm(mojo::PendingAssociatedReceiver<ContentDecryptionModule>
pending_receiver) {
receiver_.Bind(std::move(pending_receiver));
}
~MockDaemonCdm() = default;
MOCK_METHOD(void,
SetServerCertificate,
(const std::vector<uint8_t>&, SetServerCertificateCallback));
MOCK_METHOD(void,
GetStatusForPolicy,
(media::HdcpVersion, GetStatusForPolicyCallback));
MOCK_METHOD(void,
CreateSessionAndGenerateRequest,
(media::CdmSessionType,
media::EmeInitDataType,
const std::vector<uint8_t>&,
CreateSessionAndGenerateRequestCallback));
MOCK_METHOD(void,
LoadSession,
(media::CdmSessionType, const std::string&, LoadSessionCallback));
MOCK_METHOD(void,
UpdateSession,
(const std::string&,
const std::vector<uint8_t>&,
UpdateSessionCallback));
MOCK_METHOD(void, CloseSession, (const std::string&, CloseSessionCallback));
MOCK_METHOD(void, RemoveSession, (const std::string&, RemoveSessionCallback));
MOCK_METHOD(void,
Decrypt,
(const std::vector<uint8_t>&,
cdm::mojom::DecryptConfigPtr,
DecryptCallback));
private:
mojo::AssociatedReceiver<ContentDecryptionModule> receiver_{this};
};
cdm::mojom::CdmPromiseResultPtr CreatePromise(bool success) {
cdm::mojom::CdmPromiseResultPtr promise = cdm::mojom::CdmPromiseResult::New();
promise->success = success;
if (!success) {
promise->error_message = "error";
}
return promise;
}
scoped_refptr<media::DecoderBuffer> CreateDecoderBuffer(
const std::vector<uint8_t> data) {
scoped_refptr<media::DecoderBuffer> buffer = media::DecoderBuffer::CopyFrom(
data.data(), data.size(), kFakeSideData.data(), kFakeSideData.size());
buffer->set_timestamp(base::TimeDelta::FromSeconds(kFakeTimestampSec));
buffer->set_duration(base::TimeDelta::FromSeconds(kFakeDurationSec));
return buffer;
}
scoped_refptr<media::DecoderBuffer> CloneDecoderBuffer(
scoped_refptr<media::DecoderBuffer> buffer_in) {
scoped_refptr<media::DecoderBuffer> buffer_out =
media::DecoderBuffer::CopyFrom(buffer_in->data(), buffer_in->data_size(),
buffer_in->side_data(),
buffer_in->side_data_size());
buffer_out->set_timestamp(buffer_in->timestamp());
buffer_out->set_duration(buffer_in->duration());
if (buffer_in->decrypt_config())
buffer_out->set_decrypt_config(buffer_in->decrypt_config()->Clone());
buffer_out->set_is_key_frame(buffer_in->is_key_frame());
return buffer_out;
}
} // namespace
class ContentDecryptionModuleAdapterTest : public testing::Test {
protected:
ContentDecryptionModuleAdapterTest() {
mojo::AssociatedRemote<cdm::mojom::ContentDecryptionModule> daemon_cdm_mojo;
mock_daemon_cdm_ = std::make_unique<MockDaemonCdm>(
daemon_cdm_mojo.BindNewEndpointAndPassDedicatedReceiverForTesting());
cdm_adapter_ = base::WrapRefCounted<ContentDecryptionModuleAdapter>(
new ContentDecryptionModuleAdapter(
nullptr /* storage */, std::move(daemon_cdm_mojo),
mock_session_message_cb_.Get(), mock_session_closed_cb_.Get(),
mock_session_keys_change_cb_.Get(),
mock_session_expiration_update_cb_.Get()));
}
~ContentDecryptionModuleAdapterTest() override {
// Destroy the CdmAdapter first so it can invoke any session closed
// callbacks on destruction before we destroy the callback mockers.
cdm_adapter_.reset();
}
void LoadSession() {
EXPECT_CALL(*mock_daemon_cdm_,
LoadSession(media::CdmSessionType::kPersistentLicense,
kFakeSessionId1, _))
.WillOnce([](media::CdmSessionType session_type,
const std::string& session_id,
MockDaemonCdm::LoadSessionCallback callback) {
std::move(callback).Run(CreatePromise(true), kFakeSessionId2);
});
std::string session_id;
std::unique_ptr<media::MockCdmSessionPromise> promise =
std::make_unique<media::MockCdmSessionPromise>(true, &session_id);
cdm_adapter_->LoadSession(media::CdmSessionType::kPersistentLicense,
kFakeSessionId1, std::move(promise));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(session_id, kFakeSessionId2);
}
scoped_refptr<ContentDecryptionModuleAdapter> cdm_adapter_;
std::unique_ptr<MockDaemonCdm> mock_daemon_cdm_;
base::MockCallback<media::SessionMessageCB> mock_session_message_cb_;
base::MockCallback<media::SessionClosedCB> mock_session_closed_cb_;
base::MockCallback<media::SessionKeysChangeCB> mock_session_keys_change_cb_;
base::MockCallback<media::SessionExpirationUpdateCB>
mock_session_expiration_update_cb_;
private:
base::test::TaskEnvironment task_environment_;
};
TEST_F(ContentDecryptionModuleAdapterTest, GetClientInterface) {
auto pending_remote = cdm_adapter_->GetClientInterface();
EXPECT_TRUE(pending_remote.is_valid());
// If we try to get it again, it should fail.
ASSERT_DEATH(cdm_adapter_->GetClientInterface(), "");
}
TEST_F(ContentDecryptionModuleAdapterTest, SetServerCertificate_Failure) {
EXPECT_CALL(*mock_daemon_cdm_,
SetServerCertificate(ToVector(kFakeServiceCertificate), _))
.WillOnce([](const std::vector<uint8_t>& cert,
MockDaemonCdm::SetServerCertificateCallback callback) {
std::move(callback).Run(CreatePromise(false));
});
std::unique_ptr<media::MockCdmPromise> promise =
std::make_unique<media::MockCdmPromise>(false);
cdm_adapter_->SetServerCertificate(ToVector(kFakeServiceCertificate),
std::move(promise));
base::RunLoop().RunUntilIdle();
}
TEST_F(ContentDecryptionModuleAdapterTest, SetServerCertificate_Success) {
EXPECT_CALL(*mock_daemon_cdm_,
SetServerCertificate(ToVector(kFakeServiceCertificate), _))
.WillOnce([](const std::vector<uint8_t>& cert,
MockDaemonCdm::SetServerCertificateCallback callback) {
std::move(callback).Run(CreatePromise(true));
});
std::unique_ptr<media::MockCdmPromise> promise =
std::make_unique<media::MockCdmPromise>(true);
cdm_adapter_->SetServerCertificate(ToVector(kFakeServiceCertificate),
std::move(promise));
base::RunLoop().RunUntilIdle();
}
TEST_F(ContentDecryptionModuleAdapterTest, GetStatusForPolicy_Failure) {
EXPECT_CALL(*mock_daemon_cdm_,
GetStatusForPolicy(media::HdcpVersion::kHdcpVersion1_4, _))
.WillOnce([](media::HdcpVersion hdcp_version,
MockDaemonCdm::GetStatusForPolicyCallback callback) {
std::move(callback).Run(
CreatePromise(false),
media::CdmKeyInformation::KeyStatus::OUTPUT_RESTRICTED);
});
media::CdmKeyInformation::KeyStatus key_status;
std::unique_ptr<media::MockCdmKeyStatusPromise> promise =
std::make_unique<media::MockCdmKeyStatusPromise>(false, &key_status);
cdm_adapter_->GetStatusForPolicy(media::HdcpVersion::kHdcpVersion1_4,
std::move(promise));
base::RunLoop().RunUntilIdle();
}
TEST_F(ContentDecryptionModuleAdapterTest,
GetStatusForPolicy_SuccessRestricted) {
EXPECT_CALL(*mock_daemon_cdm_,
GetStatusForPolicy(media::HdcpVersion::kHdcpVersion2_2, _))
.WillOnce([](media::HdcpVersion hdcp_version,
MockDaemonCdm::GetStatusForPolicyCallback callback) {
std::move(callback).Run(
CreatePromise(true),
media::CdmKeyInformation::KeyStatus::OUTPUT_RESTRICTED);
});
media::CdmKeyInformation::KeyStatus key_status;
std::unique_ptr<media::MockCdmKeyStatusPromise> promise =
std::make_unique<media::MockCdmKeyStatusPromise>(true, &key_status);
cdm_adapter_->GetStatusForPolicy(media::HdcpVersion::kHdcpVersion2_2,
std::move(promise));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(key_status, media::CdmKeyInformation::KeyStatus::OUTPUT_RESTRICTED);
}
TEST_F(ContentDecryptionModuleAdapterTest, GetStatusForPolicy_SuccessUsable) {
EXPECT_CALL(*mock_daemon_cdm_,
GetStatusForPolicy(media::HdcpVersion::kHdcpVersion2_0, _))
.WillOnce([](media::HdcpVersion hdcp_version,
MockDaemonCdm::GetStatusForPolicyCallback callback) {
std::move(callback).Run(CreatePromise(true),
media::CdmKeyInformation::KeyStatus::USABLE);
});
media::CdmKeyInformation::KeyStatus key_status;
std::unique_ptr<media::MockCdmKeyStatusPromise> promise =
std::make_unique<media::MockCdmKeyStatusPromise>(true, &key_status);
cdm_adapter_->GetStatusForPolicy(media::HdcpVersion::kHdcpVersion2_0,
std::move(promise));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(key_status, media::CdmKeyInformation::KeyStatus::USABLE);
}
TEST_F(ContentDecryptionModuleAdapterTest,
CreateSessionAndGenerateRequest_Failure) {
EXPECT_CALL(*mock_daemon_cdm_,
CreateSessionAndGenerateRequest(media::CdmSessionType::kTemporary,
media::EmeInitDataType::CENC,
ToVector(kFakeEmeInitData), _))
.WillOnce(
[](media::CdmSessionType session_type,
media::EmeInitDataType init_type,
const std::vector<uint8_t>& init_data,
MockDaemonCdm::CreateSessionAndGenerateRequestCallback callback) {
std::move(callback).Run(CreatePromise(false), "");
});
std::string session_id;
std::unique_ptr<media::MockCdmSessionPromise> promise =
std::make_unique<media::MockCdmSessionPromise>(false, &session_id);
cdm_adapter_->CreateSessionAndGenerateRequest(
media::CdmSessionType::kTemporary, media::EmeInitDataType::CENC,
ToVector(kFakeEmeInitData), std::move(promise));
base::RunLoop().RunUntilIdle();
}
TEST_F(ContentDecryptionModuleAdapterTest,
CreateSessionAndGenerateRequest_Success) {
EXPECT_CALL(*mock_daemon_cdm_,
CreateSessionAndGenerateRequest(media::CdmSessionType::kTemporary,
media::EmeInitDataType::CENC,
ToVector(kFakeEmeInitData), _))
.WillOnce(
[](media::CdmSessionType session_type,
media::EmeInitDataType init_type,
const std::vector<uint8_t>& init_data,
MockDaemonCdm::CreateSessionAndGenerateRequestCallback callback) {
std::move(callback).Run(CreatePromise(true), kFakeSessionId1);
});
std::string session_id;
std::unique_ptr<media::MockCdmSessionPromise> promise =
std::make_unique<media::MockCdmSessionPromise>(true, &session_id);
cdm_adapter_->CreateSessionAndGenerateRequest(
media::CdmSessionType::kTemporary, media::EmeInitDataType::CENC,
ToVector(kFakeEmeInitData), std::move(promise));
// We should also be getting a session closed callback for any open sessions.
EXPECT_CALL(mock_session_closed_cb_, Run(kFakeSessionId1));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(session_id, kFakeSessionId1);
}
TEST_F(ContentDecryptionModuleAdapterTest, LoadSession_Failure) {
EXPECT_CALL(*mock_daemon_cdm_,
LoadSession(media::CdmSessionType::kPersistentLicense,
kFakeSessionId1, _))
.WillOnce([](media::CdmSessionType session_type,
const std::string& session_id,
MockDaemonCdm::LoadSessionCallback callback) {
std::move(callback).Run(CreatePromise(false), "");
});
std::string session_id;
std::unique_ptr<media::MockCdmSessionPromise> promise =
std::make_unique<media::MockCdmSessionPromise>(false, &session_id);
cdm_adapter_->LoadSession(media::CdmSessionType::kPersistentLicense,
kFakeSessionId1, std::move(promise));
base::RunLoop().RunUntilIdle();
}
TEST_F(ContentDecryptionModuleAdapterTest, LoadSession_Success) {
LoadSession();
// We should also be getting a session closed callback for any open sessions.
EXPECT_CALL(mock_session_closed_cb_, Run(kFakeSessionId2));
}
TEST_F(ContentDecryptionModuleAdapterTest, UpdateSession_Failure) {
EXPECT_CALL(*mock_daemon_cdm_,
UpdateSession(kFakeSessionId1, ToVector(kFakeSessionUpdate), _))
.WillOnce([](const std::string& session_id,
const std::vector<uint8_t>& response,
MockDaemonCdm::UpdateSessionCallback callback) {
std::move(callback).Run(CreatePromise(false));
});
std::unique_ptr<media::MockCdmPromise> promise =
std::make_unique<media::MockCdmPromise>(false);
cdm_adapter_->UpdateSession(kFakeSessionId1, ToVector(kFakeSessionUpdate),
std::move(promise));
base::RunLoop().RunUntilIdle();
}
TEST_F(ContentDecryptionModuleAdapterTest, UpdateSession_Success) {
EXPECT_CALL(*mock_daemon_cdm_,
UpdateSession(kFakeSessionId1, ToVector(kFakeSessionUpdate), _))
.WillOnce([](const std::string& session_id,
const std::vector<uint8_t>& response,
MockDaemonCdm::UpdateSessionCallback callback) {
std::move(callback).Run(CreatePromise(true));
});
std::unique_ptr<media::MockCdmPromise> promise =
std::make_unique<media::MockCdmPromise>(true);
cdm_adapter_->UpdateSession(kFakeSessionId1, ToVector(kFakeSessionUpdate),
std::move(promise));
base::RunLoop().RunUntilIdle();
}
TEST_F(ContentDecryptionModuleAdapterTest, CloseSession_Failure) {
EXPECT_CALL(*mock_daemon_cdm_, CloseSession(kFakeSessionId1, _))
.WillOnce([](const std::string& session_id,
MockDaemonCdm::CloseSessionCallback callback) {
std::move(callback).Run(CreatePromise(false));
});
std::unique_ptr<media::MockCdmPromise> promise =
std::make_unique<media::MockCdmPromise>(false);
cdm_adapter_->CloseSession(kFakeSessionId1, std::move(promise));
base::RunLoop().RunUntilIdle();
}
TEST_F(ContentDecryptionModuleAdapterTest, CloseSession_Success) {
EXPECT_CALL(*mock_daemon_cdm_, CloseSession(kFakeSessionId1, _))
.WillOnce([](const std::string& session_id,
MockDaemonCdm::CloseSessionCallback callback) {
std::move(callback).Run(CreatePromise(true));
});
std::unique_ptr<media::MockCdmPromise> promise =
std::make_unique<media::MockCdmPromise>(true);
cdm_adapter_->CloseSession(kFakeSessionId1, std::move(promise));
base::RunLoop().RunUntilIdle();
}
TEST_F(ContentDecryptionModuleAdapterTest, RemoveSession_Failure) {
EXPECT_CALL(*mock_daemon_cdm_, RemoveSession(kFakeSessionId1, _))
.WillOnce([](const std::string& session_id,
MockDaemonCdm::RemoveSessionCallback callback) {
std::move(callback).Run(CreatePromise(false));
});
std::unique_ptr<media::MockCdmPromise> promise =
std::make_unique<media::MockCdmPromise>(false);
cdm_adapter_->RemoveSession(kFakeSessionId1, std::move(promise));
base::RunLoop().RunUntilIdle();
}
TEST_F(ContentDecryptionModuleAdapterTest, RemoveSession_Success) {
EXPECT_CALL(*mock_daemon_cdm_, RemoveSession(kFakeSessionId1, _))
.WillOnce([](const std::string& session_id,
MockDaemonCdm::RemoveSessionCallback callback) {
std::move(callback).Run(CreatePromise(true));
});
std::unique_ptr<media::MockCdmPromise> promise =
std::make_unique<media::MockCdmPromise>(true);
cdm_adapter_->RemoveSession(kFakeSessionId1, std::move(promise));
base::RunLoop().RunUntilIdle();
}
TEST_F(ContentDecryptionModuleAdapterTest, OnSessionMessage) {
EXPECT_CALL(mock_session_message_cb_,
Run(kFakeSessionId1, media::CdmMessageType::LICENSE_REQUEST,
ToVector(kFakeSessionUpdate)));
cdm_adapter_->OnSessionMessage(kFakeSessionId1,
media::CdmMessageType::LICENSE_REQUEST,
ToVector(kFakeSessionUpdate));
}
TEST_F(ContentDecryptionModuleAdapterTest, OnSessionClosed) {
LoadSession();
EXPECT_CALL(mock_session_closed_cb_, Run(kFakeSessionId2));
cdm_adapter_->OnSessionClosed(kFakeSessionId2);
}
TEST_F(ContentDecryptionModuleAdapterTest, OnSessionKeysChange) {
EXPECT_CALL(mock_session_keys_change_cb_, Run(kFakeSessionId1, true, _));
cdm_adapter_->OnSessionKeysChange(kFakeSessionId1, true, {});
}
TEST_F(ContentDecryptionModuleAdapterTest, OnSessionExpirationUpdate) {
constexpr double kFakeExpiration = 123456;
EXPECT_CALL(mock_session_expiration_update_cb_,
Run(kFakeSessionId2, base::Time::FromDoubleT(kFakeExpiration)));
cdm_adapter_->OnSessionExpirationUpdate(kFakeSessionId2, kFakeExpiration);
}
TEST_F(ContentDecryptionModuleAdapterTest, RegisterNewKeyCB) {
base::MockRepeatingClosure closure;
cdm_adapter_->RegisterNewKeyCB(media::Decryptor::kVideo, closure.Get());
cdm_adapter_->RegisterNewKeyCB(media::Decryptor::kAudio, closure.Get());
// It should get invoked for each audio & video.
EXPECT_CALL(closure, Run()).Times(2);
EXPECT_CALL(mock_session_keys_change_cb_, Run(kFakeSessionId1, true, _));
cdm_adapter_->OnSessionKeysChange(kFakeSessionId1, true, {});
// If no keys change, they should not get called.
EXPECT_CALL(closure, Run()).Times(0);
EXPECT_CALL(mock_session_keys_change_cb_, Run(kFakeSessionId1, false, _));
cdm_adapter_->OnSessionKeysChange(kFakeSessionId1, false, {});
}
TEST_F(ContentDecryptionModuleAdapterTest, Decrypt_Unencrypted) {
EXPECT_CALL(*mock_daemon_cdm_, Decrypt(_, _, _)).Times(0);
scoped_refptr<media::DecoderBuffer> encrypted_buffer =
CreateDecoderBuffer(kFakeEncryptedData);
encrypted_buffer->set_is_key_frame(true);
scoped_refptr<media::DecoderBuffer> decrypted_buffer =
CloneDecoderBuffer(encrypted_buffer);
base::MockCallback<media::Decryptor::DecryptCB> callback;
EXPECT_CALL(callback, Run(media::Decryptor::kSuccess,
MatchesDecoderBuffer(decrypted_buffer)));
cdm_adapter_->Decrypt(media::Decryptor::kVideo, encrypted_buffer,
callback.Get());
}
TEST_F(ContentDecryptionModuleAdapterTest, Decrypt_NoSubsamples) {
cdm::mojom::DecryptConfigPtr expected_decrypt_config =
cdm::mojom::DecryptConfig::New(
media::EncryptionScheme::kCbcs, kFakeKeyId, kFakeIv,
std::vector<cdm::mojom::SubsampleEntryPtr>(),
media::EncryptionPattern(6, 9));
EXPECT_CALL(*mock_daemon_cdm_,
Decrypt(kFakeEncryptedData,
MatchesDecryptConfig(&expected_decrypt_config), _))
.WillOnce([](const std::vector<uint8_t>& data,
cdm::mojom::DecryptConfigPtr decrypt_config,
MockDaemonCdm::DecryptCallback callback) {
// For decryption, just reverse the data.
std::vector<uint8_t> decrypted = data;
std::reverse(std::begin(decrypted), std::end(decrypted));
std::move(callback).Run(media::Decryptor::kSuccess,
std::move(decrypted));
});
std::vector<uint8_t> decrypted_data = kFakeEncryptedData;
std::reverse(std::begin(decrypted_data), std::end(decrypted_data));
scoped_refptr<media::DecoderBuffer> decrypted_buffer =
CreateDecoderBuffer(decrypted_data);
decrypted_buffer->set_is_key_frame(true);
base::MockCallback<media::Decryptor::DecryptCB> callback;
EXPECT_CALL(callback, Run(media::Decryptor::kSuccess,
MatchesDecoderBuffer(decrypted_buffer)));
scoped_refptr<media::DecoderBuffer> encrypted_buffer =
CreateDecoderBuffer(kFakeEncryptedData);
encrypted_buffer->set_is_key_frame(true);
encrypted_buffer->set_decrypt_config(media::DecryptConfig::CreateCbcsConfig(
kFakeKeyId, kFakeIv, {}, media::EncryptionPattern(6, 9)));
cdm_adapter_->Decrypt(media::Decryptor::kVideo, encrypted_buffer,
callback.Get());
base::RunLoop().RunUntilIdle();
}
TEST_F(ContentDecryptionModuleAdapterTest, Decrypt_Failure) {
EXPECT_CALL(*mock_daemon_cdm_, Decrypt(_, _, _))
.WillOnce([](const std::vector<uint8_t>& data,
cdm::mojom::DecryptConfigPtr decrypt_config,
MockDaemonCdm::DecryptCallback callback) {
std::move(callback).Run(media::Decryptor::kError, {});
});
base::MockCallback<media::Decryptor::DecryptCB> callback;
EXPECT_CALL(callback, Run(media::Decryptor::kError, IsNull()));
scoped_refptr<media::DecoderBuffer> encrypted_buffer =
CreateDecoderBuffer(kFakeEncryptedData);
encrypted_buffer->set_decrypt_config(media::DecryptConfig::CreateCbcsConfig(
kFakeKeyId, kFakeIv, {}, media::EncryptionPattern(6, 9)));
cdm_adapter_->Decrypt(media::Decryptor::kVideo, encrypted_buffer,
callback.Get());
base::RunLoop().RunUntilIdle();
}
TEST_F(ContentDecryptionModuleAdapterTest, Decrypt_NoKey) {
EXPECT_CALL(*mock_daemon_cdm_, Decrypt(_, _, _))
.WillOnce([](const std::vector<uint8_t>& data,
cdm::mojom::DecryptConfigPtr decrypt_config,
MockDaemonCdm::DecryptCallback callback) {
std::move(callback).Run(media::Decryptor::kNoKey, {});
});
base::MockCallback<media::Decryptor::DecryptCB> callback;
EXPECT_CALL(callback, Run(media::Decryptor::kNoKey, IsNull()));
scoped_refptr<media::DecoderBuffer> encrypted_buffer =
CreateDecoderBuffer(kFakeEncryptedData);
encrypted_buffer->set_decrypt_config(media::DecryptConfig::CreateCbcsConfig(
kFakeKeyId, kFakeIv, {}, media::EncryptionPattern(6, 9)));
cdm_adapter_->Decrypt(media::Decryptor::kVideo, encrypted_buffer,
callback.Get());
base::RunLoop().RunUntilIdle();
}
TEST_F(ContentDecryptionModuleAdapterTest, Decrypt_MismatchedSubsamples) {
EXPECT_CALL(*mock_daemon_cdm_, Decrypt(_, _, _)).Times(0);
base::MockCallback<media::Decryptor::DecryptCB> callback;
EXPECT_CALL(callback, Run(media::Decryptor::kError, IsNull()));
scoped_refptr<media::DecoderBuffer> encrypted_buffer =
CreateDecoderBuffer(kFakeEncryptedData);
encrypted_buffer->set_decrypt_config(media::DecryptConfig::CreateCencConfig(
kFakeKeyId, kFakeIv, {media::SubsampleEntry(1, 1)}));
cdm_adapter_->Decrypt(media::Decryptor::kVideo, encrypted_buffer,
callback.Get());
base::RunLoop().RunUntilIdle();
}
TEST_F(ContentDecryptionModuleAdapterTest, Decrypt_InvalidSizeReturned) {
EXPECT_CALL(*mock_daemon_cdm_, Decrypt(_, _, _))
.WillOnce([](const std::vector<uint8_t>& data,
cdm::mojom::DecryptConfigPtr decrypt_config,
MockDaemonCdm::DecryptCallback callback) {
std::move(callback).Run(media::Decryptor::kSuccess, {1});
});
base::MockCallback<media::Decryptor::DecryptCB> callback;
EXPECT_CALL(callback, Run(media::Decryptor::kError, IsNull()));
scoped_refptr<media::DecoderBuffer> encrypted_buffer =
CreateDecoderBuffer(kFakeEncryptedData);
encrypted_buffer->set_decrypt_config(media::DecryptConfig::CreateCbcsConfig(
kFakeKeyId, kFakeIv, {}, media::EncryptionPattern(4, 4)));
cdm_adapter_->Decrypt(media::Decryptor::kVideo, encrypted_buffer,
callback.Get());
base::RunLoop().RunUntilIdle();
}
TEST_F(ContentDecryptionModuleAdapterTest, Decrypt_NoEncryptedSubsamples) {
EXPECT_CALL(*mock_daemon_cdm_, Decrypt(_, _, _)).Times(0);
scoped_refptr<media::DecoderBuffer> encrypted_buffer =
CreateDecoderBuffer(kFakeEncryptedData);
encrypted_buffer->set_decrypt_config(media::DecryptConfig::CreateCencConfig(
kFakeKeyId, kFakeIv,
{media::SubsampleEntry(kFakeEncryptedData.size(), 0)}));
scoped_refptr<media::DecoderBuffer> decrypted_buffer =
CloneDecoderBuffer(encrypted_buffer);
decrypted_buffer->set_decrypt_config(nullptr);
base::MockCallback<media::Decryptor::DecryptCB> callback;
EXPECT_CALL(callback, Run(media::Decryptor::kSuccess,
MatchesDecoderBuffer(decrypted_buffer)));
cdm_adapter_->Decrypt(media::Decryptor::kVideo, encrypted_buffer,
callback.Get());
}
TEST_F(ContentDecryptionModuleAdapterTest, Decrypt_SubsampledCenc) {
cdm::mojom::DecryptConfigPtr expected_decrypt_config =
cdm::mojom::DecryptConfig::New(
media::EncryptionScheme::kCenc, kFakeKeyId, kFakeIv,
std::vector<cdm::mojom::SubsampleEntryPtr>(), base::nullopt);
EXPECT_CALL(*mock_daemon_cdm_,
Decrypt(std::vector<uint8_t>(kFakeEncryptedData.begin() + 3,
kFakeEncryptedData.end()),
MatchesDecryptConfig(&expected_decrypt_config), _))
.WillOnce([](const std::vector<uint8_t>& data,
cdm::mojom::DecryptConfigPtr decrypt_config,
MockDaemonCdm::DecryptCallback callback) {
// For decryption, just reverse the data.
std::vector<uint8_t> decrypted = data;
std::reverse(std::begin(decrypted), std::end(decrypted));
std::move(callback).Run(media::Decryptor::kSuccess,
std::move(decrypted));
});
std::vector<uint8_t> decrypted_data = kFakeEncryptedData;
std::reverse(std::begin(decrypted_data) + 3, std::end(decrypted_data));
scoped_refptr<media::DecoderBuffer> decrypted_buffer =
CreateDecoderBuffer(decrypted_data);
decrypted_buffer->set_is_key_frame(true);
base::MockCallback<media::Decryptor::DecryptCB> callback;
EXPECT_CALL(callback, Run(media::Decryptor::kSuccess,
MatchesDecoderBuffer(decrypted_buffer)));
scoped_refptr<media::DecoderBuffer> encrypted_buffer =
CreateDecoderBuffer(kFakeEncryptedData);
encrypted_buffer->set_is_key_frame(true);
encrypted_buffer->set_decrypt_config(media::DecryptConfig::CreateCencConfig(
kFakeKeyId, kFakeIv, {media::SubsampleEntry(3, 5)}));
cdm_adapter_->Decrypt(media::Decryptor::kVideo, encrypted_buffer,
callback.Get());
base::RunLoop().RunUntilIdle();
}
TEST_F(ContentDecryptionModuleAdapterTest, Decrypt_SubsampledCbcs) {
cdm::mojom::DecryptConfigPtr expected_decrypt_config =
cdm::mojom::DecryptConfig::New(
media::EncryptionScheme::kCbcs, kFakeKeyId, kFakeIv,
std::vector<cdm::mojom::SubsampleEntryPtr>(), base::nullopt);
expected_decrypt_config->subsamples.emplace_back(
cdm::mojom::SubsampleEntry::New(1, 2));
expected_decrypt_config->subsamples.emplace_back(
cdm::mojom::SubsampleEntry::New(2, 3));
EXPECT_CALL(*mock_daemon_cdm_,
Decrypt(kFakeEncryptedData,
MatchesDecryptConfig(&expected_decrypt_config), _))
.WillOnce([](const std::vector<uint8_t>& data,
cdm::mojom::DecryptConfigPtr decrypt_config,
MockDaemonCdm::DecryptCallback callback) {
// For decryption, just reverse the data.
std::vector<uint8_t> decrypted = data;
std::reverse(std::begin(decrypted), std::end(decrypted));
std::move(callback).Run(media::Decryptor::kSuccess,
std::move(decrypted));
});
std::vector<uint8_t> decrypted_data = kFakeEncryptedData;
std::reverse(std::begin(decrypted_data), std::end(decrypted_data));
scoped_refptr<media::DecoderBuffer> decrypted_buffer =
CreateDecoderBuffer(decrypted_data);
decrypted_buffer->set_is_key_frame(true);
base::MockCallback<media::Decryptor::DecryptCB> callback;
EXPECT_CALL(callback, Run(media::Decryptor::kSuccess,
MatchesDecoderBuffer(decrypted_buffer)));
scoped_refptr<media::DecoderBuffer> encrypted_buffer =
CreateDecoderBuffer(kFakeEncryptedData);
encrypted_buffer->set_is_key_frame(true);
encrypted_buffer->set_decrypt_config(media::DecryptConfig::CreateCbcsConfig(
kFakeKeyId, kFakeIv,
{media::SubsampleEntry(1, 2), media::SubsampleEntry(2, 3)},
base::nullopt));
cdm_adapter_->Decrypt(media::Decryptor::kVideo, encrypted_buffer,
callback.Get());
base::RunLoop().RunUntilIdle();
}
TEST_F(ContentDecryptionModuleAdapterTest, Decrypt_CancelDecrypt) {
EXPECT_CALL(*mock_daemon_cdm_, Decrypt(_, _, _))
.WillOnce([](const std::vector<uint8_t>& data,
cdm::mojom::DecryptConfigPtr decrypt_config,
MockDaemonCdm::DecryptCallback callback) {
std::move(callback).Run(media::Decryptor::kSuccess, data);
});
scoped_refptr<media::DecoderBuffer> buffer =
CreateDecoderBuffer(kFakeEncryptedData);
buffer->set_decrypt_config(media::DecryptConfig::CreateCbcsConfig(
kFakeKeyId, kFakeIv, {}, media::EncryptionPattern(6, 9)));
base::MockCallback<media::Decryptor::DecryptCB> callback;
EXPECT_CALL(callback, Run(media::Decryptor::kSuccess, IsNull())).Times(1);
cdm_adapter_->Decrypt(media::Decryptor::kAudio, buffer, callback.Get());
cdm_adapter_->CancelDecrypt(media::Decryptor::kAudio);
base::RunLoop().RunUntilIdle();
}
} // namespace chromeos