Files
android/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp
Alex Dale f2bf164914 Updated usage test to expect v16 duration values.
[ Merge of http://go/wvgerrit/174431 ]

It is possible that the initial license duration was limited due to
the finit rental duration; however, if the license has a soft rental
duration, it will be considered unlimited after playback has begun.

The *.UsageTest* have been updated to ignore cases where initial
license durations are finit, but later report as unlimited.

Bug: 275651559
Test: cdm_extended_duration_test
Change-Id: I689163b1066b2bc9f9345e2279e9373010f844cc
2023-05-05 17:30:13 -07:00

1782 lines
71 KiB
C++

// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#include <errno.h>
#include <getopt.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <limits.h>
#include <utils/Thread.h>
#include <sstream>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <utils/Thread.h>
#include <sstream>
#include "OEMCryptoCENC.h"
#include "cdm_identifier.h"
#include "clock.h"
#include "config_test_env.h"
#include "device_files.h"
#include "file_store.h"
#include "license_protocol.pb.h"
#include "license_request.h"
#include "log.h"
#include "oemcrypto_adapter.h"
#include "properties.h"
#include "pst_report.h"
#include "string_conversions.h"
#include "test_base.h"
#include "test_printers.h"
#include "url_request.h"
#include "wv_cdm_constants.h"
#include "wv_content_decryption_module.h"
using wvutil::FileSystem;
using wvutil::Unpacked_PST_Report;
namespace {
// HTTP response codes.
const int kHttpOk = 200;
// The following two responses are unused, but left here for human debuggers.
// const int kHttpBadRequest = 400;
// const int kHttpInternalServerError = 500;
const uint32_t kMinute = 60;
const uint32_t kClockTolerance = 10;
const uint32_t kMaxUsageTableSize = 300;
const std::string kEmptyServiceCertificate;
constexpr int64_t kUnlimitedDurationValue = LLONG_MAX;
// TODO(rfrias): refactor to print out the decryption test names
struct SubSampleInfo {
bool retrieve_key;
size_t num_of_subsamples;
bool validate_key_id;
bool is_encrypted;
bool is_secure;
wvcdm::KeyId key_id;
std::vector<uint8_t> encrypt_data;
std::vector<uint8_t> decrypt_data;
std::vector<uint8_t> iv;
size_t block_offset;
uint8_t subsample_flags;
};
// clang-format off
const SubSampleInfo kEncryptedStreamingNoPstSubSample = {
// key SD, encrypted, 256b
true, 1, true, true, false,
wvutil::a2bs_hex("371EA35E1A985D75D198A7F41020DC23"),
wvutil::a2b_hex(
"64ab17b3e3dfab47245c7cce4543d4fc7a26dcf248f19f9b59f3c92601440b36"
"17c8ed0c96c656549e461f38708cd47a434066f8df28ccc28b79252eee3f9c2d"
"7f6c68ebe40141fe818fe082ca523c03d69ddaf183a93c022327fedc5582c5ab"
"ca9d342b71263a67f9cb2336f12108aaaef464f17177e44e9b0c4e56e61da53c"
"2150b4405cc82d994dfd9bf4087c761956d6688a9705db4cf350381085f383c4"
"9666d4aed135c519c1f0b5cba06e287feea96ea367bf54e7368dcf998276c6e4"
"6497e0c50e20fef74e42cb518fe7f22ef27202428688f86404e8278587017012"
"c1d65537c6cbd7dde04aae338d68115a9f430afc100ab83cdadf45dca39db685"),
wvutil::a2b_hex(
"217ce9bde99bd91e9733a1a00b9b557ac3a433dc92633546156817fae26b6e1c"
"942ac20a89ff79f4c2f25fba99d6a44618a8c0420b27d54e3da17b77c9d43cca"
"595d259a1e4a8b6d7744cd98c5d3f921adc252eb7d8af6b916044b676a574747"
"8df21fdc42f166880d97a2225cd5c9ea5e7b752f4cf81bbdbe98e542ee10e1c6"
"ad868a6ac55c10d564fc23b8acff407daaf4ed2743520e02cda9680d9ea88e91"
"029359c4cf5906b6ab5bf60fbb3f1a1c7c59acfc7e4fb4ad8e623c04d503a3dd"
"4884604c8da8a53ce33db9ff8f1c5bb6bb97f37b39906bf41596555c1bcce9ed"
"08a899cd760ff0899a1170c2f224b9c52997a0785b7fe170805fd3e8b1127659"),
wvutil::a2b_hex("f6f4b1e600a5b67813ed2bded913ba9f"), 0, 3};
const SubSampleInfo kEncryptedStreamingClip8SubSample = {
true, 1, true, true, false,
wvutil::a2bs_hex("E82DDD1D07CBB31CDD31EBAAE0894609"),
wvutil::a2b_hex(
"fe8670a01c86906c056b4bf85ad278464c4eb79c60de1da8480e66e78561350e"
"a25ae19a001f834c43aaeadf900b3c5a6745e885a4d1d1ae5bafac08dc1d60e5"
"f3465da303909ec4b09023490471f670b615d77db844192854fdab52b7806203"
"89b374594bbb6a2f2fcc31036d7cb8a3f80c0e27637b58a7650028fbf2470d68"
"1bbd77934af165d215ef325c74438c9d99a20fc628792db28c05ed5deff7d9d4"
"dba02ddb6cf11dc6e78cb5200940af9a2321c3a7c4c79be67b54a744dae1209c"
"fa02fc250ce18d30c7da9c3a4a6c9619bf8561a42ff1e55a7b14fa3c8de69196"
"c2b8e3ff672fc37003b479da5d567b7199917dbe5aa402890ebb066bce140b33"),
wvutil::a2b_hex(
"d08733bd0ef671f467906b50ff8322091400f86fd6f016fea2b86e33923775b3"
"ebb4c8c6f3ba8b78dd200a74d3872a40264ab99e1d422e4f819abb7f249114aa"
"b334420b37c86ce81938615ab9d3a6b2de8db545cd88e35091031e73016fb386"
"1b754298329b52dbe483de3a532277815e659f3e05e89257333225b933d92e15"
"ef2deff287a192d2c8fc942a29a5f3a1d54440ac6385de7b34bb650b889e4ae9"
"58c957b5f5ff268f445c0a6b825fcad55290cb7b5c9814bc4c72984dcf4c8fd7"
"5f511c173b2e0a3163b18a1eac58539e5c188aeb0751b946ad4dcd08ea777a7f"
"37326df26fa509343faa98dff667629f557873f1284903202e451227ef465a62"),
wvutil::a2b_hex("7362b5140c4ce0cd5f863858668d3f1a"), 0, 3};
const SubSampleInfo kEncryptedStreamingClip5SubSample = {
true, 1, true, true, false,
wvutil::a2bs_hex("3AE243D83B93B3311A1D777FF5FBE01A"),
wvutil::a2b_hex(
"934997779aa1aeb45d6ba8845f13786575d0adf85a5e93674d9597f8d4286ed7"
"dcce02f306e502bbd9f1cadf502f354038ca921276d158d911bdf3171d335b18"
"0ae0f9abece16ff31ee263228354f724da2f3723b19caa38ea02bd6563b01208"
"fb5bf57854ac0fe38d5883197ef90324b2721ff20fdcf9a53819515e6daa096e"
"70f6f5c1d29a4a13dafd127e2e1f761ea0e28fd451607552ecbaef5da3c780bc"
"aaf2667b4cc4f858f01d480cac9e32c3fbb5705e5d2adcceebefc2535c117208"
"e65f604799fc3d7223e16908550f287a4bea687008cb0064cf14d3aeedb8c705"
"09ebc5c2b8b5315f43c04d78d2f55f4b32c7d33e157114362106395cc0bb6d93"),
wvutil::a2b_hex(
"2dd54eee1307753508e1f250d637044d6e8f5abf057dab73e9e95f83910e4efc"
"191c9bac63950f13fd51833dd94a4d03f2b64fb5c721970c418fe53fa6f74ad5"
"a6e16477a35c7aa6e28909b069cd25770ef80da20918fc30fe95fd5c87fd3522"
"1649de17ca2c7b3dc31f936f0cbdf97c7b1c15de3a86b279dc4b4de64943914a"
"99734556c4b7a1a0b022c1933cb0786068fc18d49fed2f2b49f3ac6d01c32d07"
"92175ce2844eaf9064e6a3fcffade038d690cbed81659351163a22432f0d0545"
"037e1c805d8e92a1272b4196ad0ce22f26bb80063137a8e454d3b97e2414283d"
"ed0716cd8bceb80cf59166a217006bd147c51b04dfb183088ce3f51e9b9f759e"),
wvutil::a2b_hex("b358ab21ac90455bbf60490daad457e3"), 0, 3};
const SubSampleInfo kEncryptedOfflineClip2SubSample = {
true, 1, true, true, false,
wvutil::a2bs_hex("3260F39E12CCF653529990168A3583FF"),
wvutil::a2b_hex(
"3b2cbde084973539329bd5656da22d20396249bf4a18a51c38c4743360cc9fea"
"a1c78d53de1bd7e14dc5d256fd20a57178a98b83804258c239acd7aa38f2d7d2"
"eca614965b3d22049e19e236fc1800e60965d8b36415677bf2f843d50a6943c4"
"683c07c114a32f5e5fbc9939c483c3a1b2ecd3d82b554d649798866191724283"
"f0ab082eba2da79aaca5c4eaf186f9ee9a0c568f621f705a578f30e4e2ef7b96"
"5e14cc046ce6dbf272ee5558b098f332333e95fc879dea6c29bf34acdb649650"
"f08201b9e649960f2493fd7677cc3abf5ae70e5445845c947ba544456b431646"
"d95a133bff5f57614dda5e4446cd8837901d074149dadf4b775b5b07bb88ca20"),
wvutil::a2b_hex(
"D3EE543581F16AB2EABFA13468133314D19CB6A14A42229BE83543828D801475"
"FAE1C2C5D193DA8445B9C4B1598E8FCBDF42EFF1FBB54EBC6A4815E2836C2848"
"15094DEDE76FE4658A2D6EA3E775A872CA71835CF274676C18556C665EC7F32A"
"4DBB04C10BA988B42758E37DCEFD99D9CE3AFFB1E816C412B4013890E1A31E25"
"64EBF2125BC54D66FECDF8A31956830121546DC183B3DAC103E223778875B590"
"3961448C287B367C585E510DA43BF9242B8E9A27B9F6F3EC7E4B5A0A74A1742B"
"F5CD65EA66D7D9E79C02C7E7D5CD02DB182DDD8EAC3525B0834F1F2822AD0006"
"944B5080B998BB0FE6E566AAFAE2328B37FD189F1920A964434ECF18E11AC81E"),
wvutil::a2b_hex("7362b5140c4ce0cd5f863858668d3f1a"), 0, 3};
const std::string kStreamingClip8PstInitData = wvutil::a2bs_hex(
"000000427073736800000000" // blob size and pssh
"EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id
"08011a0d7769646576696e655f74657374220f73" // pssh data
"747265616d696e675f636c697038");
const std::string kOfflineClip2PstInitData = wvutil::a2bs_hex(
"000000407073736800000000" // blob size and pssh
"EDEF8BA979D64ACEA3C827DCD51D21ED00000020" // Widevine system id
"08011a0d7769646576696e655f74657374220d6f" // pssh data
"66666c696e655f636c697032");
std::string kOfflineClip4 = wvutil::a2bs_hex(
"000000407073736800000000" // blob size and pssh
"EDEF8BA979D64ACEA3C827DCD51D21ED00000020" // Widevine system id
"08011a0d7769646576696e655f74657374220d6f" // pssh data
"66666c696e655f636c697034");
// clang-format off
std::string kUatLicenseServer = "https://proxy.uat.widevine.com/proxy";
bool StringToInt64(const std::string& input, int64_t* output) {
std::istringstream ss(input);
ss >> *output;
return !ss.fail();
}
} // namespace
using ::testing::Contains;
using ::testing::Pair;
using ::testing::StrNe;
namespace wvcdm {
// Protobuf generated classes
using video_widevine::ClientIdentification;
using video_widevine::LicenseIdentification;
using video_widevine::LicenseRequest_ContentIdentification;
using video_widevine::LicenseRequest_ContentIdentification_WidevinePsshData;
using video_widevine::SignedMessage;
class TestWvCdmClientPropertySet : public CdmClientPropertySet {
public:
TestWvCdmClientPropertySet()
: use_privacy_mode_(false),
is_session_sharing_enabled_(false),
session_sharing_id_(0),
use_atsc_mode_(false) {}
virtual ~TestWvCdmClientPropertySet() {}
virtual const std::string& app_id() const { return app_id_; }
virtual const std::string& security_level() const { return security_level_; }
virtual const std::string& service_certificate() const {
return service_certificate_;
}
virtual void set_service_certificate(const std::string& cert) {
service_certificate_ = cert;
}
virtual bool use_privacy_mode() const { return use_privacy_mode_; }
virtual bool is_session_sharing_enabled() const {
return is_session_sharing_enabled_;
}
virtual uint32_t session_sharing_id() const { return session_sharing_id_; }
virtual bool use_atsc_mode() const { return use_atsc_mode_; }
void set_app_id(const std::string& app_id) { app_id_ = app_id; }
void set_security_level(const std::string& security_level) {
if (security_level == QUERY_VALUE_SECURITY_LEVEL_L1 ||
security_level == QUERY_VALUE_SECURITY_LEVEL_L3) {
security_level_ = security_level;
}
}
void set_use_privacy_mode(bool use_privacy_mode) {
use_privacy_mode_ = use_privacy_mode;
}
void set_session_sharing_mode(bool enable) {
is_session_sharing_enabled_ = enable;
}
void set_session_sharing_id(uint32_t id) { session_sharing_id_ = id; }
void set_use_atsc_mode(bool use_atsc_mode) {
use_atsc_mode_ = use_atsc_mode;
}
private:
std::string app_id_;
std::string security_level_;
std::string service_certificate_;
bool use_privacy_mode_;
bool is_session_sharing_enabled_;
uint32_t session_sharing_id_;
bool use_atsc_mode_;
};
class WvCdmExtendedDurationTest : public WvCdmTestBase {
public:
WvCdmExtendedDurationTest()
: decryptor_(new wvcdm::WvContentDecryptionModule()) {}
~WvCdmExtendedDurationTest() {}
protected:
void GetOfflineConfiguration(std::string* key_id, std::string* client_auth) {
ConfigTestEnv config(config_.server_id(), false);
if (binary_key_id().compare(wvutil::a2bs_hex(config_.key_id())) == 0)
key_id->assign(wvutil::a2bs_hex(config.key_id()));
else
key_id->assign(binary_key_id());
client_auth->assign(config.client_auth());
}
void GenerateKeyRequest(const std::string& init_data,
CdmLicenseType license_type) {
CdmResponseType response;
GenerateKeyRequest(init_data, license_type, &response);
EXPECT_EQ(KEY_MESSAGE, response);
}
void GenerateKeyRequest(const std::string& init_data,
CdmLicenseType license_type,
CdmResponseType *response) {
CdmAppParameterMap app_parameters;
CdmKeyRequest key_request;
const std::string init_data_type = "video/mp4";
if (wvutil::g_cutoff >= wvutil::CDM_LOG_DEBUG) {
InitializationData parsed_init_data(init_data_type, init_data);
parsed_init_data.DumpToLogs();
}
*response = decryptor_->GenerateKeyRequest(
session_id_, key_set_id_, init_data_type, init_data,
license_type, app_parameters, nullptr,
kDefaultCdmIdentifier, &key_request);
if (*response == KEY_MESSAGE) {
EXPECT_EQ(kKeyRequestTypeInitial, key_request.type);
key_msg_ = key_request.message;
EXPECT_EQ(0u, key_request.url.size());
}
}
void GenerateRenewalRequest(CdmLicenseType license_type,
std::string* server_url) {
// TODO application makes a license request, CDM will renew the license
// when appropriate.
std::string init_data;
CdmAppParameterMap app_parameters;
CdmKeyRequest key_request;
EXPECT_EQ(KEY_MESSAGE, decryptor_->GenerateKeyRequest(
session_id_, key_set_id_, "video/mp4", init_data,
license_type, app_parameters, nullptr,
kDefaultCdmIdentifier, &key_request));
*server_url = key_request.url;
key_msg_ = key_request.message;
EXPECT_EQ(kKeyRequestTypeRenewal, key_request.type);
// TODO(rfrias): Add tests cases for when license server url
// is empty on renewal. Need appropriate key id at the server.
EXPECT_NE(0u, key_request.url.size());
}
void GenerateKeyRelease(CdmKeySetId key_set_id,
CdmResponseType expected_response) {
CdmSessionId session_id;
CdmInitData init_data;
CdmAppParameterMap app_parameters;
CdmKeyRequest key_request;
EXPECT_EQ(expected_response, decryptor_->GenerateKeyRequest(
session_id, key_set_id, "video/mp4", init_data,
kLicenseTypeRelease, app_parameters, nullptr,
kDefaultCdmIdentifier, &key_request));
if (expected_response == KEY_MESSAGE) {
key_msg_ = key_request.message;
EXPECT_EQ(kKeyRequestTypeRelease, key_request.type);
}
}
void GenerateKeyRelease(CdmKeySetId key_set_id) {
GenerateKeyRelease(key_set_id, CdmResponseType(KEY_MESSAGE));
}
void LogResponseError(const std::string& message, int http_status_code) {
LOGD("HTTP Status code = %d", http_status_code);
LOGD("HTTP response(%zu): %s", message.size(), wvutil::b2a_hex(message).c_str());
}
// Post a request and extract the drm message from the response
std::string GetKeyRequestResponse(const std::string& server_url,
const std::string& client_auth) {
// Use secure connection and chunk transfer coding.
UrlRequest url_request(server_url + client_auth);
EXPECT_TRUE(url_request.is_connected()) << "Fail to connect to "
<< server_url << client_auth;
url_request.PostRequest(key_msg_);
std::string message;
EXPECT_TRUE(url_request.GetResponse(&message));
int http_status_code = url_request.GetStatusCode(message);
if (kHttpOk != http_status_code) {
LogResponseError(message, http_status_code);
}
EXPECT_EQ(kHttpOk, http_status_code);
std::string drm_msg;
if (kHttpOk == http_status_code) {
LicenseRequest lic_request;
lic_request.GetDrmMessage(message, drm_msg);
LOGV("HTTP response body: (%zu bytes)", drm_msg.size());
}
key_response_ = drm_msg;
return drm_msg;
}
// Post a request and extract the signed provisioning message from
// the HTTP response.
std::string GetCertRequestResponse(const std::string& server_url) {
// Use secure connection and chunk transfer coding.
UrlRequest url_request(server_url);
EXPECT_TRUE(url_request.is_connected()) << "Fail to connect to "
<< server_url;
url_request.PostCertRequestInQueryString(key_msg_);
std::string message;
EXPECT_TRUE(url_request.GetResponse(&message));
int http_status_code = url_request.GetStatusCode(message);
if (kHttpOk != http_status_code) {
LogResponseError(message, http_status_code);
}
EXPECT_EQ(kHttpOk, http_status_code);
return message;
}
// Post a request and extract the signed provisioning message from
// the HTTP response.
std::string GetSecureStopResponse(const std::string& server_url,
const std::string& client_auth,
const std::string& secure_stop_request) {
// Use secure connection and chunk transfer coding.
UrlRequest url_request(server_url + client_auth);
EXPECT_TRUE(url_request.is_connected())
<< "Fail to connect to " << server_url << client_auth;
url_request.PostRequest(secure_stop_request);
std::string message;
EXPECT_TRUE(url_request.GetResponse(&message));
const int http_status_code = url_request.GetStatusCode(message);
if (kHttpOk != http_status_code) {
LogResponseError(message, http_status_code);
}
EXPECT_EQ(kHttpOk, http_status_code);
std::string secure_stop_response;
if (kHttpOk == http_status_code) {
LicenseRequest license;
license.GetDrmMessage(message, secure_stop_response);
LOGV("HTTP response body: (%zu bytes)", secure_stop_response.size());
}
return secure_stop_response;
}
void VerifyKeyRequestResponse(const std::string& server_url,
const std::string& client_auth,
bool is_renewal) {
VerifyKeyRequestResponse(server_url, client_auth, is_renewal, nullptr);
}
void VerifyKeyRequestResponse(const std::string& server_url,
const std::string& client_auth,
bool /* is_renewal */,
CdmResponseType* status) {
std::string resp = GetKeyRequestResponse(server_url, client_auth);
CdmResponseType sts =
decryptor_->AddKey(session_id_, resp, &key_set_id_);
if (status == nullptr) {
EXPECT_EQ(KEY_ADDED, sts);
} else {
*status = sts;
}
}
void Unprovision() {
EXPECT_EQ(NO_ERROR,
decryptor_->Unprovision(kSecurityLevelL1, kDefaultCdmIdentifier));
EXPECT_EQ(NO_ERROR,
decryptor_->Unprovision(kSecurityLevelL3, kDefaultCdmIdentifier));
}
void Provision() {
CdmResponseType status = decryptor_->OpenSession(
config_.key_system(), nullptr, kDefaultCdmIdentifier, nullptr, &session_id_);
switch (status.code()) {
case NO_ERROR:
decryptor_->CloseSession(session_id_);
return;
case NEED_PROVISIONING:
break;
default:
EXPECT_EQ(NO_ERROR, status);
return;
}
std::string provisioning_server_url;
CdmCertificateType cert_type = kCertificateWidevine;
std::string cert_authority, cert, wrapped_key;
status = decryptor_->GetProvisioningRequest(
cert_type, cert_authority, kDefaultCdmIdentifier,
kEmptyServiceCertificate, kLevelDefault, &key_msg_,
&provisioning_server_url);
EXPECT_EQ(NO_ERROR, status);
if (NO_ERROR != status) return;
EXPECT_EQ(provisioning_server_url, kDefaultProvisioningServerUrl);
std::string response =
GetCertRequestResponse(config_.provisioning_server());
EXPECT_NE(0, static_cast<int>(response.size()));
EXPECT_EQ(NO_ERROR,
decryptor_->HandleProvisioningResponse(kDefaultCdmIdentifier, response,
kLevelDefault, &cert,
&wrapped_key));
EXPECT_EQ(0, static_cast<int>(cert.size()));
EXPECT_EQ(0, static_cast<int>(wrapped_key.size()));
decryptor_->CloseSession(session_id_);
return;
}
void ValidateResponse(video_widevine::LicenseType license_type,
bool has_provider_session_token) {
// Validate signed response
SignedMessage signed_message;
EXPECT_TRUE(signed_message.ParseFromString(key_response_));
EXPECT_EQ(SignedMessage::LICENSE, signed_message.type());
EXPECT_TRUE(signed_message.has_signature());
EXPECT_TRUE(!signed_message.msg().empty());
// Verify license
video_widevine::License license;
EXPECT_TRUE(license.ParseFromString(signed_message.msg()));
// Verify license identification
license_id_ = license.id();
EXPECT_EQ(license_type, license_id_.type());
EXPECT_EQ(has_provider_session_token,
license_id_.has_provider_session_token());
}
void ValidateRenewalRequest(int64_t expected_seconds_since_started,
int64_t expected_seconds_since_last_played,
bool has_provider_session_token) {
// Validate signed renewal request
SignedMessage signed_message;
EXPECT_TRUE(signed_message.ParseFromString(key_msg_));
EXPECT_EQ(SignedMessage::LICENSE_REQUEST, signed_message.type());
EXPECT_TRUE(signed_message.has_signature());
EXPECT_TRUE(!signed_message.msg().empty());
// Verify license request
video_widevine::LicenseRequest license_renewal;
EXPECT_TRUE(license_renewal.ParseFromString(signed_message.msg()));
// Verify Content Identification
const LicenseRequest_ContentIdentification& content_id =
license_renewal.content_id();
EXPECT_FALSE(content_id.has_widevine_pssh_data());
EXPECT_FALSE(content_id.has_webm_key_id());
EXPECT_TRUE(content_id.has_existing_license());
const LicenseRequest_ContentIdentification::ExistingLicense&
existing_license = content_id.existing_license();
const LicenseIdentification& id = existing_license.license_id();
EXPECT_EQ(license_id_.request_id(), id.request_id());
EXPECT_EQ(license_id_.session_id(), id.session_id());
EXPECT_EQ(license_id_.purchase_id(), id.purchase_id());
EXPECT_EQ(license_id_.type(), id.type());
EXPECT_EQ(license_id_.version(), id.version());
EXPECT_EQ(has_provider_session_token, !id.provider_session_token().empty());
EXPECT_NEAR(existing_license.seconds_since_started(),
expected_seconds_since_started, kClockTolerance);
EXPECT_NEAR(existing_license.seconds_since_last_played(),
expected_seconds_since_last_played, kClockTolerance);
EXPECT_TRUE(existing_license.session_usage_table_entry().empty());
EXPECT_EQ(::video_widevine::LicenseRequest_RequestType_RENEWAL,
license_renewal.type());
EXPECT_LT(0, license_renewal.request_time());
EXPECT_EQ(video_widevine::VERSION_2_1, license_renewal.protocol_version());
}
void ValidateReleaseRequest(std::string& usage_msg, bool license_used,
int64_t expected_seconds_since_license_received,
int64_t expected_seconds_since_first_playback,
int64_t expected_seconds_since_last_playback) {
// Validate signed renewal request
SignedMessage signed_message;
EXPECT_TRUE(signed_message.ParseFromString(usage_msg));
EXPECT_EQ(SignedMessage::LICENSE_REQUEST, signed_message.type());
EXPECT_TRUE(signed_message.has_signature());
EXPECT_TRUE(!signed_message.msg().empty());
// Verify license request
video_widevine::LicenseRequest license_renewal;
EXPECT_TRUE(license_renewal.ParseFromString(signed_message.msg()));
// Verify Content Identification
const LicenseRequest_ContentIdentification& content_id =
license_renewal.content_id();
EXPECT_FALSE(content_id.has_widevine_pssh_data());
EXPECT_FALSE(content_id.has_webm_key_id());
EXPECT_TRUE(content_id.has_existing_license());
const LicenseRequest_ContentIdentification::ExistingLicense&
existing_license = content_id.existing_license();
const LicenseIdentification& id = existing_license.license_id();
EXPECT_EQ(license_id_.request_id(), id.request_id());
EXPECT_EQ(license_id_.session_id(), id.session_id());
EXPECT_EQ(license_id_.purchase_id(), id.purchase_id());
EXPECT_EQ(license_id_.type(), id.type());
EXPECT_EQ(license_id_.version(), id.version());
EXPECT_TRUE(!id.provider_session_token().empty());
EXPECT_NEAR(existing_license.seconds_since_started(),
expected_seconds_since_first_playback, kClockTolerance);
EXPECT_NEAR(existing_license.seconds_since_last_played(),
expected_seconds_since_last_playback, kClockTolerance);
EXPECT_TRUE(!existing_license.session_usage_table_entry().empty());
// Verify usage report
uint8_t* buffer = reinterpret_cast<uint8_t*>(
const_cast<char*>(existing_license.session_usage_table_entry().data()));
Unpacked_PST_Report usage_report(buffer);
EXPECT_EQ(usage_report.report_size(),
existing_license.session_usage_table_entry().size());
EXPECT_EQ(license_used ? kInactiveUsed : kInactiveUnused,
usage_report.status());
EXPECT_EQ(id.provider_session_token().size(), usage_report.pst_length());
std::string pst(reinterpret_cast<char*>(usage_report.pst()),
usage_report.pst_length());
EXPECT_EQ(id.provider_session_token(), pst);
EXPECT_LE(kInsecureClock, usage_report.clock_security_level());
int64_t seconds_since_license_received =
usage_report.seconds_since_license_received();
int64_t seconds_since_first_decrypt =
usage_report.seconds_since_first_decrypt();
int64_t seconds_since_last_decrypt =
usage_report.seconds_since_last_decrypt();
// Detect licenses that were never used
if (seconds_since_first_decrypt < 0 ||
seconds_since_first_decrypt > seconds_since_license_received) {
seconds_since_first_decrypt = 0;
seconds_since_last_decrypt = 0;
}
EXPECT_NEAR(seconds_since_license_received,
expected_seconds_since_license_received, kClockTolerance);
if (license_used) {
EXPECT_NEAR(seconds_since_first_decrypt,
expected_seconds_since_first_playback, kClockTolerance);
EXPECT_NEAR(seconds_since_last_decrypt,
expected_seconds_since_last_playback, kClockTolerance);
}
EXPECT_EQ(::video_widevine::LicenseRequest_RequestType_RELEASE,
license_renewal.type());
EXPECT_LT(0, license_renewal.request_time());
EXPECT_EQ(video_widevine::VERSION_2_1, license_renewal.protocol_version());
}
void ValidateHasUpdateUsageEntry(
const drm_metrics::WvCdmMetrics& metrics) const {
bool has_update_usage_entry_metrics = false;
for (const auto& session : metrics.session_metrics()) {
has_update_usage_entry_metrics |=
session.crypto_metrics()
.usage_table_header_update_entry_time_us().size() > 0;
has_update_usage_entry_metrics |=
session.crypto_metrics().oemcrypto_update_usage_entry().size() > 0;
}
std::string serialized_metrics;
ASSERT_TRUE(metrics.SerializeToString(&serialized_metrics));
EXPECT_TRUE(has_update_usage_entry_metrics)
<< "metrics: " << wvutil::b2a_hex(serialized_metrics);
}
void QueryKeyStatus(bool streaming, bool expect_renewal,
int64_t* license_duration_remaining,
int64_t* playback_duration_remaining) {
CdmQueryMap query_info;
EXPECT_EQ(NO_ERROR, decryptor_->QueryKeyStatus(session_id_, &query_info));
EXPECT_THAT(query_info, Contains(Pair(QUERY_KEY_LICENSE_TYPE,
streaming ? QUERY_VALUE_STREAMING
: QUERY_VALUE_OFFLINE)));
EXPECT_THAT(query_info,
Contains(Pair(QUERY_KEY_PLAY_ALLOWED, QUERY_VALUE_TRUE)));
EXPECT_THAT(query_info, Contains(Pair(QUERY_KEY_PERSIST_ALLOWED,
streaming ? QUERY_VALUE_FALSE
: QUERY_VALUE_TRUE)));
if (expect_renewal) {
EXPECT_THAT(query_info,
Contains(Pair(QUERY_KEY_RENEW_ALLOWED, QUERY_VALUE_TRUE)));
EXPECT_THAT(query_info,
Contains(Pair(QUERY_KEY_RENEWAL_SERVER_URL, StrNe(""))));
}
std::string key = QUERY_KEY_LICENSE_DURATION_REMAINING;
ASSERT_THAT(query_info, Contains(Pair(key, StrNe(""))));
EXPECT_TRUE(StringToInt64(query_info[key], license_duration_remaining));
key = QUERY_KEY_PLAYBACK_DURATION_REMAINING;
ASSERT_THAT(query_info, Contains(Pair(key, StrNe(""))));
EXPECT_TRUE(StringToInt64(query_info[key], playback_duration_remaining));
}
uint32_t QueryStatus(RequestedSecurityLevel security_level, const std::string& key) {
std::string str;
EXPECT_EQ(wvcdm::NO_ERROR,
decryptor_->QueryStatus(security_level, key, &str));
std::istringstream ss(str);
uint32_t value;
ss >> value;
EXPECT_FALSE(ss.fail());
EXPECT_TRUE(ss.eof());
return value;
}
std::string GetSecurityLevel(TestWvCdmClientPropertySet* property_set) {
decryptor_->OpenSession(config_.key_system(), property_set,
kDefaultCdmIdentifier, nullptr, &session_id_);
CdmQueryMap query_info;
EXPECT_EQ(NO_ERROR,
decryptor_->QuerySessionStatus(session_id_, &query_info));
CdmQueryMap::iterator itr = query_info.find(QUERY_KEY_SECURITY_LEVEL);
EXPECT_TRUE(itr != query_info.end());
decryptor_->CloseSession(session_id_);
return itr->second;
}
CdmSecurityLevel GetDefaultSecurityLevel() {
std::string level = GetSecurityLevel(nullptr);
CdmSecurityLevel security_level = kSecurityLevelUninitialized;
if (level == QUERY_VALUE_SECURITY_LEVEL_L1) {
security_level = kSecurityLevelL1;
} else if (level == QUERY_VALUE_SECURITY_LEVEL_L3) {
security_level = kSecurityLevelL3;
} else {
// common_typos_disable
EXPECT_TRUE(false);
// common_typos_enable
}
return security_level;
}
class CloseSessionThread : public android::Thread {
public:
CloseSessionThread() :
Thread(false) {}
~CloseSessionThread() {}
bool Start(const android::sp<wvcdm::WvContentDecryptionModule>& decryptor,
const CdmSessionId& session_id,
uint32_t time_in_msecs) {
wv_content_decryption_module_ = decryptor;
sess_id_ = session_id;
delay_.tv_sec = time_in_msecs / 1000;
delay_.tv_nsec = (time_in_msecs % 1000) * 10000000ll;
return run("CloseSessionThread") == android::NO_ERROR;
}
void Stop() { requestExitAndWait(); }
private:
virtual bool threadLoop() {
struct timespec delay_remaining;
int result = nanosleep(&delay_, &delay_remaining);
while (result < 0 &&
(delay_remaining.tv_sec > 0 || delay_remaining.tv_nsec > 0)) {
result = nanosleep(&delay_remaining, &delay_remaining);
}
wv_content_decryption_module_->CloseSession(sess_id_);
return false;
}
android::sp<wvcdm::WvContentDecryptionModule> wv_content_decryption_module_;
CdmSessionId sess_id_;
struct timespec delay_;
CORE_DISALLOW_COPY_AND_ASSIGN(CloseSessionThread);
};
android::sp<wvcdm::WvContentDecryptionModule> decryptor_;
CdmKeyMessage key_msg_;
CdmKeyResponse key_response_;
CdmSessionId session_id_;
CdmKeySetId key_set_id_;
video_widevine::LicenseIdentification license_id_;
};
TEST_F(WvCdmExtendedDurationTest, VerifyLicenseRequestTest) {
Provision();
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
nullptr, &session_id_);
GenerateKeyRequest(binary_key_id(), kLicenseTypeStreaming);
EXPECT_TRUE(!key_msg_.empty());
// Validate signed request
SignedMessage signed_message;
EXPECT_TRUE(signed_message.ParseFromString(key_msg_));
EXPECT_EQ(SignedMessage::LICENSE_REQUEST, signed_message.type());
EXPECT_TRUE(signed_message.has_signature());
EXPECT_TRUE(!signed_message.msg().empty());
// Verify license request
video_widevine::LicenseRequest license_request;
EXPECT_TRUE(license_request.ParseFromString(signed_message.msg()));
// Verify Client Identification
const ClientIdentification& client_id = license_request.client_id();
EXPECT_EQ(
video_widevine::ClientIdentification_TokenType_DRM_DEVICE_CERTIFICATE,
client_id.type());
EXPECT_LT(0, client_id.client_info_size());
for (int i = 0; i < client_id.client_info_size(); ++i) {
const ::video_widevine::ClientIdentification_NameValue& name_value =
client_id.client_info(i);
EXPECT_TRUE(!name_value.name().empty());
EXPECT_TRUE(!name_value.value().empty());
}
EXPECT_FALSE(client_id.has_provider_client_token());
EXPECT_FALSE(client_id.has_license_counter());
const ClientIdentification::ClientCapabilities& client_capabilities =
client_id.client_capabilities();
EXPECT_TRUE(client_capabilities.has_client_token());
EXPECT_TRUE(client_capabilities.client_token());
EXPECT_TRUE(client_capabilities.has_session_token());
EXPECT_FALSE(client_capabilities.video_resolution_constraints());
EXPECT_TRUE(client_capabilities.has_max_hdcp_version());
EXPECT_TRUE(client_capabilities.has_oem_crypto_api_version());
// Verify Content Identification
const LicenseRequest_ContentIdentification& content_id =
license_request.content_id();
EXPECT_TRUE(content_id.has_widevine_pssh_data());
EXPECT_FALSE(content_id.has_webm_key_id());
EXPECT_FALSE(content_id.has_existing_license());
const LicenseRequest_ContentIdentification_WidevinePsshData& pssh_data =
content_id.widevine_pssh_data();
EXPECT_TRUE(std::equal(pssh_data.pssh_data(0).begin(),
pssh_data.pssh_data(0).end(),
binary_key_id().begin() + 32));
EXPECT_EQ(video_widevine::STREAMING, pssh_data.license_type());
EXPECT_TRUE(pssh_data.has_request_id());
// Verify other license request fields
EXPECT_EQ(::video_widevine::LicenseRequest_RequestType_NEW,
license_request.type());
EXPECT_LT(0, license_request.request_time());
EXPECT_EQ(video_widevine::VERSION_2_1, license_request.protocol_version());
EXPECT_TRUE(license_request.has_key_control_nonce());
decryptor_->CloseSession(session_id_);
}
TEST_F(WvCdmExtendedDurationTest, VerifyLicenseRenewalTest) {
Provision();
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
nullptr, &session_id_);
GenerateKeyRequest(binary_key_id(), kLicenseTypeStreaming);
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth(),
false);
// Validate signed response
SignedMessage signed_message;
EXPECT_TRUE(signed_message.ParseFromString(key_response_));
EXPECT_EQ(SignedMessage::LICENSE, signed_message.type());
EXPECT_TRUE(signed_message.has_signature());
EXPECT_TRUE(!signed_message.msg().empty());
// Verify license
video_widevine::License license;
EXPECT_TRUE(license.ParseFromString(signed_message.msg()));
// Verify license identification
video_widevine::LicenseIdentification license_id = license.id();
EXPECT_LT(0u, license_id.request_id().size());
EXPECT_LT(0u, license_id.session_id().size());
EXPECT_EQ(video_widevine::STREAMING, license_id.type());
EXPECT_FALSE(license_id.has_provider_session_token());
// Create renewal request
std::string license_server;
GenerateRenewalRequest(kLicenseTypeStreaming, &license_server);
EXPECT_TRUE(!license_server.empty());
EXPECT_TRUE(!key_msg_.empty());
// Validate signed renewal request
signed_message.Clear();
EXPECT_TRUE(signed_message.ParseFromString(key_msg_));
EXPECT_EQ(SignedMessage::LICENSE_REQUEST, signed_message.type());
EXPECT_TRUE(signed_message.has_signature());
EXPECT_TRUE(!signed_message.msg().empty());
// Verify license request
video_widevine::LicenseRequest license_renewal;
EXPECT_TRUE(license_renewal.ParseFromString(signed_message.msg()));
// Client Identification not filled in in renewal
// Verify Content Identification
const LicenseRequest_ContentIdentification& content_id =
license_renewal.content_id();
EXPECT_FALSE(content_id.has_widevine_pssh_data());
EXPECT_FALSE(content_id.has_webm_key_id());
EXPECT_TRUE(content_id.has_existing_license());
const LicenseRequest_ContentIdentification::ExistingLicense&
existing_license = content_id.existing_license();
const LicenseIdentification& id = existing_license.license_id();
EXPECT_EQ(license_id.request_id(), id.request_id());
EXPECT_EQ(license_id.session_id(), id.session_id());
EXPECT_EQ(license_id.purchase_id(), id.purchase_id());
EXPECT_EQ(license_id.type(), id.type());
EXPECT_EQ(license_id.version(), id.version());
EXPECT_TRUE(id.provider_session_token().empty());
EXPECT_EQ(0, existing_license.seconds_since_started());
EXPECT_EQ(0, existing_license.seconds_since_last_played());
EXPECT_TRUE(existing_license.session_usage_table_entry().empty());
EXPECT_EQ(::video_widevine::LicenseRequest_RequestType_RENEWAL,
license_renewal.type());
EXPECT_LT(0, license_renewal.request_time());
EXPECT_EQ(video_widevine::VERSION_2_1, license_renewal.protocol_version());
decryptor_->CloseSession(session_id_);
}
TEST_F(WvCdmExtendedDurationTest, DecryptionCloseSessionConcurrencyTest) {
Unprovision();
Provision();
// Leave session open to avoid CDM termination
CdmSessionId session_id;
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
nullptr, &session_id);
// Retrieve offline license
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
nullptr, &session_id_);
GenerateKeyRequest(kOfflineClip2PstInitData, kLicenseTypeOffline);
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth(),
false);
EXPECT_FALSE(key_set_id_.empty());
decryptor_->CloseSession(session_id_);
for (uint32_t j = 0; j < 500; ++j) {
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
nullptr, &session_id_);
EXPECT_EQ(KEY_ADDED, decryptor_->RestoreKey(session_id_, key_set_id_));
CdmResponseType status(NO_ERROR);
struct timespec decrypt_delay;
decrypt_delay.tv_sec = 0;
decrypt_delay.tv_nsec = 10000000ll; // 10 ms
CloseSessionThread* thread = new CloseSessionThread();
thread->Start(decryptor_, session_id_, 500 /* 500 ms */);
thread = nullptr;
while (status == NO_ERROR) {
struct timespec delay_remaining;
int result = nanosleep(&decrypt_delay, &delay_remaining);
while (result < 0 &&
(delay_remaining.tv_sec > 0 || delay_remaining.tv_nsec > 0)) {
result = nanosleep(&delay_remaining, &delay_remaining);
}
const SubSampleInfo* data = &kEncryptedOfflineClip2SubSample;
for (size_t i = 0; i < data->num_of_subsamples; i++) {
std::vector<uint8_t> decrypt_buffer((data + i)->encrypt_data.size());
CdmDecryptionParameters decryption_parameters(
&(data + i)->key_id, &(data + i)->encrypt_data.front(),
(data + i)->encrypt_data.size(), &(data + i)->iv,
(data + i)->block_offset, &decrypt_buffer[0]);
decryption_parameters.is_encrypted = (data + i)->is_encrypted;
decryption_parameters.is_secure = (data + i)->is_secure;
decryption_parameters.subsample_flags = (data + i)->subsample_flags;
status = decryptor_->Decrypt(session_id_, (data + i)->validate_key_id,
decryption_parameters);
switch (status.code()) {
case SESSION_NOT_FOUND_FOR_DECRYPT:
case SESSION_NOT_FOUND_18:
// Session was closed before decrypt was called. This is expected
// occasionally as we are testing resilience to concurrency.
break;
case NO_ERROR:
EXPECT_EQ((data + i)->decrypt_data, decrypt_buffer);
break;
default:
ADD_FAILURE() << "Unexpected decrypt result: " << status.ToString();
}
}
}
}
decryptor_->CloseSession(session_id);
}
TEST_F(WvCdmExtendedDurationTest, DISABLED_UsageOverflowTest) {
Provision();
TestWvCdmClientPropertySet client_property_set;
TestWvCdmClientPropertySet* property_set = nullptr;
CdmSecurityLevel security_level = GetDefaultSecurityLevel();
FileSystem file_system;
DeviceFiles handle(&file_system);
EXPECT_TRUE(handle.Init(security_level));
std::vector<std::string> provider_session_tokens;
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(
DeviceFiles::GetUsageInfoFileName(""), &provider_session_tokens));
const std::string key_id = wvutil::a2bs_hex(
"000000427073736800000000" // blob size and pssh
"EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id
"08011a0d7769646576696e655f74657374220f73" // pssh data
"747265616d696e675f636c697035");
for (size_t i = 0; i < kMaxUsageTableSize + 100; ++i) {
decryptor_->OpenSession(config_.key_system(), property_set,
kDefaultCdmIdentifier, nullptr, &session_id_);
GenerateKeyRequest(key_id, kLicenseTypeStreaming);
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth(),
false);
decryptor_->CloseSession(session_id_);
}
CdmUsageReportList usage_reports;
CdmResponseType status =
decryptor_->GetUsageInfo("", kDefaultCdmIdentifier, &usage_reports);
EXPECT_EQ(usage_reports.empty() ? NO_ERROR : KEY_MESSAGE, status);
int error_count = 0;
while (!usage_reports.empty()) {
// Release each of the listed secure stops.
for (size_t i = 0; i < usage_reports.size(); ++i) {
const CdmUsageReport& usage_report = usage_reports[i];
const CdmKeyMessage release_msg = GetSecureStopResponse(
config_.license_server(), config_.client_auth(), usage_report);
EXPECT_EQ(NO_ERROR,
decryptor_->ReleaseUsageInfo(release_msg, kDefaultCdmIdentifier))
<< i << "/" << usage_reports.size() << " (err " << (error_count++) << ")"
<< release_msg;
}
ASSERT_LE(error_count, 100); // Give up after 100 failures.
// Get an updated list. The CDM might not list all entries at once.
status = decryptor_->GetUsageInfo("", kDefaultCdmIdentifier, &usage_reports);
switch (status.code()) {
case KEY_MESSAGE:
EXPECT_FALSE(usage_reports.empty());
break;
case NO_ERROR:
EXPECT_TRUE(usage_reports.empty());
break;
default:
FAIL() << "GetUsageInfo failed with error " << static_cast<int>(status);
break;
}
}
}
// This test verifies that sessions allocated internally during key release
// message generation are deallocated after their time to live period expires
// by timer events (if other sessions are open).
TEST_F(WvCdmExtendedDurationTest, AutomatedOfflineSessionReleaseOnTimerEvent) {
Unprovision();
Provision();
// Leave session open to run the CDM timer
CdmSessionId streaming_session_id;
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
nullptr, &streaming_session_id);
// override default settings unless configured through the command line
std::string key_id;
std::string client_auth;
GetOfflineConfiguration(&key_id, &client_auth);
uint32_t initial_open_sessions =
QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS);
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
nullptr, &session_id_);
GenerateKeyRequest(kOfflineClip4, kLicenseTypeOffline);
VerifyKeyRequestResponse(kUatLicenseServer, client_auth, false);
EXPECT_FALSE(key_set_id_.empty());
decryptor_->CloseSession(session_id_);
CdmKeySetId key_set_id = key_set_id_;
session_id_.clear();
key_set_id_.clear();
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
nullptr, &session_id_);
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_->RestoreKey(session_id_, key_set_id));
decryptor_->CloseSession(session_id_);
session_id_.clear();
GenerateKeyRelease(key_set_id);
uint32_t open_sessions =
QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS);
EXPECT_GT(open_sessions, initial_open_sessions);
sleep(kMinute + kClockTolerance);
open_sessions =
QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS);
EXPECT_EQ(open_sessions, initial_open_sessions);
session_id_.clear();
GenerateKeyRelease(key_set_id);
key_set_id_ = key_set_id;
VerifyKeyRequestResponse(kUatLicenseServer, client_auth, false);
decryptor_->CloseSession(streaming_session_id);
}
// This test verifies that sessions allocated internally during key release
// message generation are deallocated after their time to live period expires
// when a new session is opened.
TEST_F(WvCdmExtendedDurationTest, AutomatedOfflineSessionReleaseOnOpenSession) {
Unprovision();
Provision();
// override default settings unless configured through the command line
std::string key_id;
std::string client_auth;
GetOfflineConfiguration(&key_id, &client_auth);
uint32_t initial_open_sessions =
QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS);
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
nullptr, &session_id_);
GenerateKeyRequest(kOfflineClip4, kLicenseTypeOffline);
VerifyKeyRequestResponse(kUatLicenseServer, client_auth, false);
EXPECT_FALSE(key_set_id_.empty());
decryptor_->CloseSession(session_id_);
CdmKeySetId key_set_id = key_set_id_;
session_id_.clear();
key_set_id_.clear();
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
nullptr, &session_id_);
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_->RestoreKey(session_id_, key_set_id));
decryptor_->CloseSession(session_id_);
session_id_.clear();
GenerateKeyRelease(key_set_id);
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
nullptr, &session_id_);
EXPECT_GT(
QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS),
initial_open_sessions);
decryptor_->CloseSession(session_id_);
EXPECT_GT(
QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS),
initial_open_sessions);
sleep(kMinute + kClockTolerance);
EXPECT_EQ(
QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS),
initial_open_sessions);
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
nullptr, &session_id_);
EXPECT_GT(
QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS),
initial_open_sessions);
decryptor_->CloseSession(session_id_);
EXPECT_EQ(
QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS),
initial_open_sessions);
session_id_.clear();
GenerateKeyRelease(key_set_id);
key_set_id_ = key_set_id;
VerifyKeyRequestResponse(kUatLicenseServer, client_auth, false);
}
// This test verifies that sessions allocated internally during
// key release message generation are deallocated after their
// time to live period expires.
// TODO: Disabled till b/32617908 is addressed
TEST_F(WvCdmExtendedDurationTest, DISABLED_AutomatedOfflineSessionReleaseTest) {
Unprovision();
Provision();
// override default settings unless configured through the command line
std::string key_id;
std::string client_auth;
GetOfflineConfiguration(&key_id, &client_auth);
uint32_t initial_open_sessions =
QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS);
uint32_t max_sessions =
QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_MAX_NUMBER_OF_SESSIONS);
uint32_t num_key_set_ids = max_sessions - initial_open_sessions;
if (num_key_set_ids > kMaxUsageTableSize)
num_key_set_ids = kMaxUsageTableSize;
std::set<std::string> key_set_id_map;
for (uint32_t i = 0; i < num_key_set_ids; ++i) {
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
nullptr, &session_id_);
GenerateKeyRequest(kOfflineClip4, kLicenseTypeOffline);
VerifyKeyRequestResponse(kUatLicenseServer, client_auth, false);
EXPECT_FALSE(key_set_id_.empty());
decryptor_->CloseSession(session_id_);
key_set_id_map.insert(key_set_id_);
}
std::set<std::string>::iterator iter;
for (iter = key_set_id_map.begin(); iter != key_set_id_map.end(); ++iter) {
session_id_.clear();
key_set_id_.clear();
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
nullptr, &session_id_);
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_->RestoreKey(session_id_, *iter));
decryptor_->CloseSession(session_id_);
}
for (iter = key_set_id_map.begin(); iter != key_set_id_map.end(); ++iter) {
session_id_.clear();
GenerateKeyRelease(*iter);
}
uint32_t open_sessions =
QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS);
EXPECT_EQ(open_sessions, key_set_id_map.size() + initial_open_sessions);
sleep(kMinute + kClockTolerance);
iter = key_set_id_map.begin();
session_id_.clear();
GenerateKeyRelease(*iter);
open_sessions =
QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS);
EXPECT_GE(open_sessions, initial_open_sessions);
EXPECT_LE(open_sessions - initial_open_sessions, key_set_id_map.size());
for (iter = key_set_id_map.begin(); iter != key_set_id_map.end(); ++iter) {
session_id_.clear();
GenerateKeyRelease(*iter);
key_set_id_ = *iter;
VerifyKeyRequestResponse(kUatLicenseServer, client_auth, false);
}
}
class WvCdmStreamingNoPstTest : public WvCdmExtendedDurationTest,
public ::testing::WithParamInterface<size_t> {};
TEST_P(WvCdmStreamingNoPstTest, UsageTest) {
Unprovision();
Provision();
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
nullptr, &session_id_);
GenerateKeyRequest(binary_key_id(), kLicenseTypeStreaming);
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth(),
false);
ValidateResponse(video_widevine::STREAMING, false);
int64_t initial_license_duration_remaining = 0;
int64_t initial_playback_duration_remaining = 0;
QueryKeyStatus(true, true, &initial_license_duration_remaining,
&initial_playback_duration_remaining);
sleep(kMinute);
int64_t expected_seconds_since_license_received = kMinute;
int64_t expected_seconds_since_initial_playback = 0;
int64_t expected_seconds_since_last_playback = 0;
for (size_t i = 0; i < GetParam(); ++i) {
// Decrypt data
const SubSampleInfo* data = &kEncryptedStreamingNoPstSubSample;
for (size_t i = 0; i < data->num_of_subsamples; i++) {
std::vector<uint8_t> decrypt_buffer((data + i)->encrypt_data.size());
CdmDecryptionParameters decryption_parameters(
&(data + i)->key_id, &(data + i)->encrypt_data.front(),
(data + i)->encrypt_data.size(), &(data + i)->iv,
(data + i)->block_offset, &decrypt_buffer[0]);
decryption_parameters.is_encrypted = (data + i)->is_encrypted;
decryption_parameters.is_secure = (data + i)->is_secure;
decryption_parameters.subsample_flags = (data + i)->subsample_flags;
EXPECT_EQ(NO_ERROR,
decryptor_->Decrypt(session_id_, (data + i)->validate_key_id,
decryption_parameters));
EXPECT_EQ((data + i)->decrypt_data, decrypt_buffer);
}
sleep(kMinute);
expected_seconds_since_license_received += kMinute;
expected_seconds_since_initial_playback += kMinute;
expected_seconds_since_last_playback = kMinute;
}
// Create renewal request and validate
std::string license_server;
GenerateRenewalRequest(kLicenseTypeStreaming, &license_server);
EXPECT_TRUE(!license_server.empty());
EXPECT_TRUE(!key_msg_.empty());
ValidateRenewalRequest(expected_seconds_since_initial_playback,
expected_seconds_since_last_playback, false);
// Query and validate usage information
int64_t license_duration_remaining = 0;
int64_t playback_duration_remaining = 0;
QueryKeyStatus(true, true, &license_duration_remaining,
&playback_duration_remaining);
if (initial_license_duration_remaining == kUnlimitedDurationValue) {
EXPECT_EQ(license_duration_remaining, kUnlimitedDurationValue);
} else if (license_duration_remaining != kUnlimitedDurationValue) {
// Possible that the initial license duration was limited due to
// the finit rental duration; however, if the license has a soft
// rental duration, it will be considered unlimited after playback
// has begun.
EXPECT_NEAR(initial_license_duration_remaining - license_duration_remaining,
expected_seconds_since_license_received, kClockTolerance)
<< "initial_license_duration_remaining = "
<< initial_license_duration_remaining
<< ", license_duration_remaining = " << license_duration_remaining;
}
if (initial_playback_duration_remaining == kUnlimitedDurationValue) {
EXPECT_EQ(playback_duration_remaining, kUnlimitedDurationValue);
} else {
EXPECT_NEAR(initial_playback_duration_remaining - playback_duration_remaining,
expected_seconds_since_initial_playback, kClockTolerance)
<< "initial_playback_duration_remaining = "
<< initial_playback_duration_remaining
<< ", playback_duration_remaining = " << playback_duration_remaining;
}
decryptor_->CloseSession(session_id_);
}
INSTANTIATE_TEST_CASE_P(Cdm, WvCdmStreamingNoPstTest,
::testing::Values(0, 1, 2));
class WvCdmStreamingPstTest : public WvCdmExtendedDurationTest,
public ::testing::WithParamInterface<size_t> {};
TEST_P(WvCdmStreamingPstTest, UsageTest) {
Unprovision();
Provision();
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
nullptr, &session_id_);
GenerateKeyRequest(kStreamingClip8PstInitData, kLicenseTypeStreaming);
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth(),
false);
ValidateResponse(video_widevine::STREAMING, true);
int64_t initial_license_duration_remaining = 0;
int64_t initial_playback_duration_remaining = 0;
QueryKeyStatus(true, false, &initial_license_duration_remaining,
&initial_playback_duration_remaining);
sleep(kMinute);
int64_t expected_seconds_since_license_received = kMinute;
int64_t expected_seconds_since_initial_playback = 0;
int64_t expected_seconds_since_last_playback = 0;
const size_t minutes = GetParam();
for (size_t m = 0; m < minutes; ++m) {
// Decrypt data
const SubSampleInfo& data = kEncryptedStreamingClip8SubSample;
std::vector<uint8_t> decrypt_buffer(data.encrypt_data.size());
CdmDecryptionParameters decryption_parameters(
&data.key_id, &data.encrypt_data.front(),
data.encrypt_data.size(), &data.iv,
data.block_offset, decrypt_buffer.data());
decryption_parameters.is_encrypted = data.is_encrypted;
decryption_parameters.is_secure = data.is_secure;
decryption_parameters.subsample_flags = data.subsample_flags;
EXPECT_EQ(NO_ERROR,
decryptor_->Decrypt(session_id_, data.validate_key_id,
decryption_parameters));
EXPECT_EQ(data.decrypt_data, decrypt_buffer);
sleep(kMinute);
expected_seconds_since_license_received += kMinute;
expected_seconds_since_initial_playback += kMinute;
expected_seconds_since_last_playback = kMinute;
}
// Query and validate usage information
int64_t license_duration_remaining = 0;
int64_t playback_duration_remaining = 0;
QueryKeyStatus(true, false, &license_duration_remaining,
&playback_duration_remaining);
if (initial_license_duration_remaining == kUnlimitedDurationValue) {
EXPECT_EQ(license_duration_remaining, kUnlimitedDurationValue);
} else if (license_duration_remaining != kUnlimitedDurationValue) {
// Possible that the initial license duration was limited due to
// the finit rental duration; however, if the license has a soft
// rental duration, it will be considered unlimited after playback
// has begun.
EXPECT_NEAR(initial_license_duration_remaining - license_duration_remaining,
expected_seconds_since_license_received, kClockTolerance)
<< "initial_license_duration_remaining = "
<< initial_license_duration_remaining
<< ", license_duration_remaining = " << license_duration_remaining;
}
if (initial_playback_duration_remaining == kUnlimitedDurationValue) {
EXPECT_EQ(playback_duration_remaining, kUnlimitedDurationValue);
} else {
EXPECT_NEAR(initial_playback_duration_remaining - playback_duration_remaining,
expected_seconds_since_initial_playback, kClockTolerance)
<< "initial_playback_duration_remaining = "
<< initial_playback_duration_remaining
<< ", playback_duration_remaining = " << playback_duration_remaining;
}
decryptor_->CloseSession(session_id_);
}
INSTANTIATE_TEST_CASE_P(Cdm, WvCdmStreamingPstTest, ::testing::Values(0, 1, 2));
class WvCdmStreamingUsageReportTest
: public WvCdmExtendedDurationTest,
public ::testing::WithParamInterface<size_t> {};
TEST_P(WvCdmStreamingUsageReportTest, UsageTest) {
Unprovision();
Provision();
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
nullptr, &session_id_);
GenerateKeyRequest(kStreamingClip8PstInitData, kLicenseTypeStreaming);
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth(),
false);
ValidateResponse(video_widevine::STREAMING, true);
int64_t initial_license_duration_remaining = 0;
int64_t initial_playback_duration_remaining = 0;
QueryKeyStatus(true, false, &initial_license_duration_remaining,
&initial_playback_duration_remaining);
sleep(kMinute);
int64_t expected_seconds_since_license_received = kMinute;
int64_t expected_seconds_since_initial_playback = 0;
int64_t expected_seconds_since_last_playback = 0;
const size_t minutes = GetParam();
for (size_t m = 0; m < minutes; ++m) {
// Decrypt data
const SubSampleInfo& data = kEncryptedStreamingClip8SubSample;
std::vector<uint8_t> decrypt_buffer(data.encrypt_data.size());
CdmDecryptionParameters decryption_parameters(
&data.key_id, &data.encrypt_data.front(),
data.encrypt_data.size(), &data.iv,
data.block_offset, decrypt_buffer.data());
decryption_parameters.is_encrypted = data.is_encrypted;
decryption_parameters.is_secure = data.is_secure;
decryption_parameters.subsample_flags = data.subsample_flags;
EXPECT_EQ(NO_ERROR,
decryptor_->Decrypt(session_id_, data.validate_key_id,
decryption_parameters));
EXPECT_EQ(data.decrypt_data, decrypt_buffer);
sleep(kMinute);
expected_seconds_since_license_received += kMinute;
expected_seconds_since_initial_playback += kMinute;
expected_seconds_since_last_playback = kMinute;
}
// Query and validate usage information
int64_t license_duration_remaining = 0;
int64_t playback_duration_remaining = 0;
QueryKeyStatus(true, false, &license_duration_remaining,
&playback_duration_remaining);
// For unlimited "rental durations", the "license duration" will
// effectively be unlimited. Remaining license duration in this
// case is represented by |kUnlimitedDurationValue| and will not
// change over time.
if (initial_license_duration_remaining == kUnlimitedDurationValue) {
EXPECT_EQ(license_duration_remaining, kUnlimitedDurationValue);
} else if (license_duration_remaining != kUnlimitedDurationValue) {
// Possible that the initial license duration was limited due to
// the finit rental duration; however, if the license has a soft
// rental duration, it will be considered unlimited after playback
// has begun.
EXPECT_NEAR(initial_license_duration_remaining - license_duration_remaining,
expected_seconds_since_license_received, kClockTolerance)
<< "initial_license_duration_remaining = "
<< initial_license_duration_remaining
<< ", license_duration_remaining = " << license_duration_remaining;
}
if (initial_playback_duration_remaining == kUnlimitedDurationValue) {
EXPECT_EQ(playback_duration_remaining, kUnlimitedDurationValue);
} else {
EXPECT_NEAR(initial_playback_duration_remaining - playback_duration_remaining,
expected_seconds_since_initial_playback, kClockTolerance)
<< "initial_playback_duration_remaining = "
<< initial_playback_duration_remaining
<< ", playback_duration_remaining = " << playback_duration_remaining;
}
decryptor_->CloseSession(session_id_);
// Create usage report and validate
CdmUsageReportList usage_reports;
CdmResponseType status =
decryptor_->GetUsageInfo("", kDefaultCdmIdentifier, &usage_reports);
EXPECT_EQ(usage_reports.empty() ? NO_ERROR : KEY_MESSAGE, status);
int error_count = 0;
while (!usage_reports.empty()) {
for (size_t i = 0; i < usage_reports.size(); ++i) {
ValidateReleaseRequest(usage_reports[i],
expected_seconds_since_initial_playback != 0,
expected_seconds_since_license_received,
expected_seconds_since_initial_playback,
expected_seconds_since_last_playback);
const CdmKeyResponse release_msg = GetSecureStopResponse(config_.license_server(),
config_.client_auth(), usage_reports[i]);
EXPECT_EQ(NO_ERROR,
decryptor_->ReleaseUsageInfo(release_msg, kDefaultCdmIdentifier))
<< i << "/" << usage_reports.size() << " (err " << (error_count++) << ")"
<< release_msg;
}
ASSERT_LE(error_count, 100); // Give up after 100 failures.
status = decryptor_->GetUsageInfo("", kDefaultCdmIdentifier, &usage_reports);
switch (status.code()) {
case KEY_MESSAGE:
EXPECT_FALSE(usage_reports.empty());
break;
case NO_ERROR:
EXPECT_TRUE(usage_reports.empty());
break;
default:
FAIL() << "GetUsageInfo failed with error " << static_cast<int>(status);
break;
}
}
// Validate that update usage table entry is exercised.
drm_metrics::WvCdmMetrics metrics;
ASSERT_EQ(NO_ERROR, decryptor_->GetCurrentMetrics(
kDefaultCdmIdentifier, &metrics));
ValidateHasUpdateUsageEntry(metrics);
}
INSTANTIATE_TEST_CASE_P(Cdm, WvCdmStreamingUsageReportTest,
::testing::Values(0, 1, 2));
class WvCdmOfflineUsageReportTest
: public WvCdmExtendedDurationTest,
public ::testing::WithParamInterface<size_t> {};
TEST_P(WvCdmOfflineUsageReportTest, UsageTest) {
Unprovision();
Provision();
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
nullptr, &session_id_);
GenerateKeyRequest(kOfflineClip2PstInitData, kLicenseTypeOffline);
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth(),
false);
ValidateResponse(video_widevine::OFFLINE, true);
CdmKeySetId key_set_id = key_set_id_;
EXPECT_FALSE(key_set_id_.empty());
int64_t initial_license_duration_remaining = 0;
int64_t initial_playback_duration_remaining = 0;
QueryKeyStatus(false, true, &initial_license_duration_remaining,
&initial_playback_duration_remaining);
decryptor_->CloseSession(session_id_);
sleep(kMinute);
int64_t expected_seconds_since_license_received = kMinute;
int64_t expected_seconds_since_initial_playback = 0;
int64_t expected_seconds_since_last_playback = 0;
for (size_t i = 0; i < GetParam(); ++i) {
session_id_.clear();
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
nullptr, &session_id_);
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_->RestoreKey(session_id_, key_set_id));
// Query and validate usage information
int64_t license_duration_remaining = 0;
int64_t playback_duration_remaining = 0;
QueryKeyStatus(false, true, &license_duration_remaining,
&playback_duration_remaining);
if (initial_license_duration_remaining == kUnlimitedDurationValue) {
EXPECT_EQ(license_duration_remaining, kUnlimitedDurationValue)
<< "i = " << i;
} else {
EXPECT_NEAR(initial_license_duration_remaining - license_duration_remaining,
expected_seconds_since_license_received, kClockTolerance)
<< "initial_license_duration_remaining = "
<< initial_license_duration_remaining
<< ", license_duration_remaining = " << license_duration_remaining
<< ", i = " << i;
}
if (initial_playback_duration_remaining == kUnlimitedDurationValue) {
EXPECT_EQ(playback_duration_remaining, kUnlimitedDurationValue)
<< "i = " << i;
} else {
EXPECT_NEAR(initial_playback_duration_remaining - playback_duration_remaining,
expected_seconds_since_initial_playback, kClockTolerance)
<< "initial_playback_duration_remaining = "
<< initial_playback_duration_remaining
<< ", playback_duration_remaining = " << playback_duration_remaining
<< ", i = " << i;
}
// Decrypt data
const SubSampleInfo* data = &kEncryptedOfflineClip2SubSample;
for (size_t i = 0; i < data->num_of_subsamples; i++) {
std::vector<uint8_t> decrypt_buffer((data + i)->encrypt_data.size());
CdmDecryptionParameters decryption_parameters(
&(data + i)->key_id, &(data + i)->encrypt_data.front(),
(data + i)->encrypt_data.size(), &(data + i)->iv,
(data + i)->block_offset, &decrypt_buffer[0]);
decryption_parameters.is_encrypted = (data + i)->is_encrypted;
decryption_parameters.is_secure = (data + i)->is_secure;
decryption_parameters.subsample_flags = (data + i)->subsample_flags;
EXPECT_EQ(NO_ERROR,
decryptor_->Decrypt(session_id_, (data + i)->validate_key_id,
decryption_parameters));
EXPECT_EQ((data + i)->decrypt_data, decrypt_buffer);
}
sleep(10);
decryptor_->CloseSession(session_id_);
sleep(kMinute - 10);
expected_seconds_since_license_received += kMinute;
expected_seconds_since_initial_playback += kMinute;
expected_seconds_since_last_playback = kMinute;
}
session_id_.clear();
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
nullptr, &session_id_);
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_->RestoreKey(session_id_, key_set_id));
// Query and validate usage information
int64_t license_duration_remaining = 0;
int64_t playback_duration_remaining = 0;
QueryKeyStatus(false, true, &license_duration_remaining,
&playback_duration_remaining);
if (initial_license_duration_remaining == kUnlimitedDurationValue) {
EXPECT_EQ(license_duration_remaining, kUnlimitedDurationValue);
} else if (license_duration_remaining != kUnlimitedDurationValue) {
// Possible that the initial license duration was limited due to
// the finit rental duration; however, if the license has a soft
// rental duration, it will be considered unlimited after playback
// has begun.
EXPECT_NEAR(initial_license_duration_remaining - license_duration_remaining,
expected_seconds_since_license_received, kClockTolerance)
<< "initial_license_duration_remaining = "
<< initial_license_duration_remaining
<< ", license_duration_remaining = " << license_duration_remaining;
}
if (initial_playback_duration_remaining == kUnlimitedDurationValue) {
EXPECT_EQ(playback_duration_remaining, kUnlimitedDurationValue);
} else {
EXPECT_NEAR(initial_playback_duration_remaining - playback_duration_remaining,
expected_seconds_since_initial_playback, kClockTolerance)
<< "initial_playback_duration_remaining = "
<< initial_playback_duration_remaining
<< ", playback_duration_remaining = " << playback_duration_remaining;
}
decryptor_->CloseSession(session_id_);
session_id_.clear();
key_set_id_.clear();
GenerateKeyRelease(key_set_id);
ValidateReleaseRequest(key_msg_, expected_seconds_since_initial_playback != 0,
expected_seconds_since_license_received,
expected_seconds_since_initial_playback,
expected_seconds_since_last_playback);
key_set_id_ = key_set_id;
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth(),
false);
}
INSTANTIATE_TEST_CASE_P(Cdm, WvCdmOfflineUsageReportTest,
::testing::Values(0, 1, 2));
// This test verifies that a device can still work if the maximum capacity
// of the usage entry table is reached. New usage entries, for offline
// licenses, can still be added and existing licenses can still be played back.
TEST_F(WvCdmExtendedDurationTest, MaxUsageEntryOfflineRecoveryTest) {
Unprovision();
Provision();
// override default settings unless configured through the command line
std::string key_id;
std::string client_auth;
GetOfflineConfiguration(&key_id, &client_auth);
std::vector<CdmKeySetId> key_set_ids;
// Download large number of offline licenses. If OEMCrypto returns
// OEMCrypto_ERROR_INSUFFICIENT_RESOURCES when usage table is at capacity,
// licenses will be deleted internally to make space and we will
// not encounter an error.
for (size_t i = 0; i < 2000; ++i) {
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
nullptr, &session_id_);
GenerateKeyRequest(kOfflineClip2PstInitData, kLicenseTypeOffline);
VerifyKeyRequestResponse(config_.license_server(), client_auth, false);
key_set_ids.push_back(key_set_id_);
decryptor_->CloseSession(session_id_);
}
uint32_t number_of_valid_offline_sessions = 0;
for (size_t i = 0; i < key_set_ids.size(); ++i) {
session_id_.clear();
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
nullptr, &session_id_);
CdmResponseType result = decryptor_->RestoreKey(session_id_,
key_set_ids[i]);
if (result == KEY_ADDED) {
++number_of_valid_offline_sessions;
// Decrypt data
const SubSampleInfo* data = &kEncryptedOfflineClip2SubSample;
std::vector<uint8_t> decrypt_buffer(data->encrypt_data.size());
CdmDecryptionParameters decryption_parameters(
&data->key_id, &data->encrypt_data.front(),
data->encrypt_data.size(), &data->iv,
data->block_offset, &decrypt_buffer[0]);
decryption_parameters.is_encrypted = data->is_encrypted;
decryption_parameters.is_secure = data->is_secure;
decryption_parameters.subsample_flags = data->subsample_flags;
EXPECT_EQ(NO_ERROR,
decryptor_->Decrypt(session_id_, data->validate_key_id,
decryption_parameters));
EXPECT_EQ(data->decrypt_data, decrypt_buffer);
decryptor_->CloseSession(session_id_);
// Release the license
session_id_.clear();
key_set_id_.clear();
GenerateKeyRelease(key_set_ids[i]);
key_set_id_ = key_set_ids[i];
VerifyKeyRequestResponse(config_.license_server(), client_auth, false);
} else {
decryptor_->CloseSession(session_id_);
}
}
EXPECT_GE(number_of_valid_offline_sessions, 200u);
}
} // namespace wvcdm