Create unique cdm engines per WVDrmPlugin instance

This change creates a unique id in the cdm identifier in order to force
a one-to-one mapping between WVDrmPlugin instances and CDM Engines. This
change simplifies some assumptions. This includes ensuring that the
metrics for a given MediaDrm instance map to a given CdmEngine instance.

Bug: 73724453
Test: Updated unit tests. GTS test pass. Shaka Player, Netflix and Google Play test.
Change-Id: I7e041b6cdf3e272d067da49d25a297b4a4663f1f
This commit is contained in:
Adam Stone
2018-03-29 12:28:57 -07:00
parent 084c370db6
commit 58234a69f2
11 changed files with 339 additions and 199 deletions

View File

@@ -30,12 +30,28 @@ struct CdmIdentifier {
// provide a friendly name of the application package for the purposes of
// logging and metrics.
std::string app_package_name;
// The unique identifier guarantees that no two identifiers share the same
// CdmEngine instance. We're moving to a model where a plugin maps 1 to 1
// with a CdmEngine instance. This is a simple way to implement that.
uint32_t unique_id;
// This method is needed to check to see if the identifier is equivalent
// to the default cdm. E.g. no spoid, origin or app package name. Use this
// comparison in lieu of the == operator when checking to see if the
// identifier would cause the default provisioned certificate to be used.
bool IsEquivalentToDefault() {
return spoid == EMPTY_SPOID
&& origin == EMPTY_ORIGIN
&& app_package_name == EMPTY_APP_PACKAGE_NAME;
}
};
// Provide comparison operators
inline bool operator==(const CdmIdentifier& lhs, const CdmIdentifier& rhs) {
return lhs.spoid == rhs.spoid && lhs.origin == rhs.origin
&& lhs.app_package_name == rhs.app_package_name;
&& lhs.app_package_name == rhs.app_package_name
&& lhs.unique_id == rhs.unique_id;
}
inline bool operator!=(const CdmIdentifier& lhs, const CdmIdentifier& rhs) {
@@ -47,7 +63,9 @@ inline bool operator<(const CdmIdentifier& lhs, const CdmIdentifier& rhs) {
|| ((lhs.spoid == rhs.spoid)
&& (lhs.origin < rhs.origin
|| (lhs.origin == rhs.origin
&& lhs.app_package_name < rhs.app_package_name)));
&& (lhs.app_package_name < rhs.app_package_name
|| (lhs.app_package_name == rhs.app_package_name
&& lhs.unique_id < rhs.unique_id)))));
}
inline bool operator>(const CdmIdentifier& lhs, const CdmIdentifier& rhs) {
@@ -66,7 +84,8 @@ inline bool operator>=(const CdmIdentifier& lhs, const CdmIdentifier& rhs) {
static const CdmIdentifier kDefaultCdmIdentifier = {
EMPTY_SPOID,
EMPTY_ORIGIN,
EMPTY_APP_PACKAGE_NAME
EMPTY_APP_PACKAGE_NAME,
0
};
} // namespace wvcdm

View File

@@ -127,9 +127,14 @@ class WvContentDecryptionModule : public android::RefBase, public TimerHandler {
// Validate a passed-in service certificate
virtual bool IsValidServiceCertificate(const std::string& certificate);
// Retrieve the serialized metrics from CdmEngine and CdmSession instances
// that have been closed.
virtual void GetSerializedMetrics(std::string* serialized_metrics);
// Fill the metrics parameter with the metrics data for the CdmEngine
// associated with the given CdmIdentifier. If the CdmEngine instance does
// not exist, this will return an error.
virtual CdmResponseType GetMetrics(const CdmIdentifier& identifier,
drm_metrics::WvCdmMetrics* metrics);
// Closes the CdmEngine and sessions associated with the given CdmIdentifier.
virtual CdmResponseType CloseCdm(const CdmIdentifier& identifier);
private:
struct CdmInfo {
@@ -145,10 +150,10 @@ class WvContentDecryptionModule : public android::RefBase, public TimerHandler {
// Finds the CdmEngine instance for the given session id, returning NULL if
// not found.
CdmEngine* GetCdmForSessionId(const std::string& session_id);
// Closes CdmEngine instances that don't have any open sessions. Also stores
// metrics data for closed CdmEngine instances.
// Callers must acquire the cdms_lock_ before calling this method.
void CloseCdmsWithoutSessions();
// Close all of the open CdmEngine instances. This is used when ready to close
// the WvContentDecryptionModule instance.
void CloseAllCdms();
uint32_t GenerateSessionSharingId();
@@ -170,9 +175,6 @@ class WvContentDecryptionModule : public android::RefBase, public TimerHandler {
// This contains weak pointers to the CDM instances contained in |cdms_|.
std::map<std::string, CdmEngine*> cdm_by_session_id_;
// The metrics for cdm engines and sessions that have been closed.
drm_metrics::WvCdmMetricsGroup metrics_group_;
CORE_DISALLOW_COPY_AND_ASSIGN(WvContentDecryptionModule);
};

View File

@@ -24,6 +24,7 @@ Lock WvContentDecryptionModule::session_sharing_id_generation_lock_;
WvContentDecryptionModule::WvContentDecryptionModule() {}
WvContentDecryptionModule::~WvContentDecryptionModule() {
CloseAllCdms();
ForceDisablePolicyTimer();
}
@@ -75,8 +76,6 @@ CdmResponseType WvContentDecryptionModule::CloseSession(
cdm_by_session_id_.erase(session_id);
}
DisablePolicyTimer();
return sts;
}
@@ -330,12 +329,21 @@ bool WvContentDecryptionModule::IsValidServiceCertificate(
return cert.has_certificate();
}
void WvContentDecryptionModule::GetSerializedMetrics(
std::string* serialized_metrics) {
CdmResponseType WvContentDecryptionModule::GetMetrics(
const CdmIdentifier& identifier, drm_metrics::WvCdmMetrics* metrics) {
if (!metrics) {
return PARAMETER_NULL;
}
AutoLock auto_lock(cdms_lock_);
CloseCdmsWithoutSessions();
metrics_group_.SerializeToString(serialized_metrics);
metrics_group_.Clear();
auto it = cdms_.find(identifier);
if (it == cdms_.end()) {
LOGE("WVContentDecryptionModule::Close. cdm_identifier not found");
// TODO(blueeyes): Add a better error.
return UNKNOWN_ERROR;
}
it->second.cdm_engine->GetMetrics()->Serialize(metrics);
return NO_ERROR;
}
WvContentDecryptionModule::CdmInfo::CdmInfo()
@@ -371,60 +379,44 @@ CdmEngine* WvContentDecryptionModule::GetCdmForSessionId(
return it->second;
}
// This method requires that the caller first acquire cdms_lock_.
void WvContentDecryptionModule::CloseCdmsWithoutSessions() {
void WvContentDecryptionModule::CloseAllCdms() {
AutoLock auto_lock(cdms_lock_);
for (auto it = cdms_.begin(); it != cdms_.end();) {
if (it->second.cdm_engine->SessionSize() != 0) {
++it;
} else {
// Retrieve the metrics from the engine and any completed
// sessions. Clear the metrics from any completed sessions.
metrics::EngineMetrics* engine_metrics =
it->second.cdm_engine->GetMetrics();
// engine_metrics should never be null.
if (engine_metrics != NULL) {
engine_metrics->Serialize(metrics_group_.add_metrics());
} else {
// Engine metrics should never be null.
LOGI(
"WvContentDecryptionModule::CloseCdmsWithoutSessions."
"engine_metrics was unexpectedly NULL.");
}
// The CDM is no longer used for this identifier, delete it.
it = cdms_.erase(it);
}
it = cdms_.erase(it);
}
}
CdmResponseType WvContentDecryptionModule::CloseCdm(
const CdmIdentifier& cdm_identifier) {
AutoLock auto_lock(cdms_lock_);
auto it = cdms_.find(cdm_identifier);
if (it == cdms_.end()) {
LOGE("WVContentDecryptionModule::Close. cdm_identifier not found");
// TODO(blueeyes): Create a better error.
return UNKNOWN_ERROR;
}
cdms_.erase(it);
DisablePolicyTimer();
return NO_ERROR;
}
void WvContentDecryptionModule::EnablePolicyTimer() {
AutoLock auto_lock(policy_timer_lock_);
if (!policy_timer_.IsRunning())
policy_timer_.Start(this, kCdmPolicyTimerDurationSeconds);
}
// This implementation assumes that the caller has already acquired the
// cdms_lock_.
void WvContentDecryptionModule::DisablePolicyTimer() {
bool cdms_is_empty = false;
{
AutoLock auto_lock(cdms_lock_);
CloseCdmsWithoutSessions();
cdms_is_empty = cdms_.empty();
}
AutoLock auto_lock(policy_timer_lock_);
if (cdms_is_empty) {
if (policy_timer_.IsRunning()) {
policy_timer_.Stop();
}
if (cdms_.empty() && policy_timer_.IsRunning()) {
policy_timer_.Stop();
}
}
void WvContentDecryptionModule::ForceDisablePolicyTimer() {
{
AutoLock auto_lock(cdms_lock_);
CloseCdmsWithoutSessions();
}
AutoLock auto_lock(policy_timer_lock_);
if (policy_timer_.IsRunning()) {
policy_timer_.Stop();

View File

@@ -605,24 +605,21 @@ class WvCdmExtendedDurationTest : public WvCdmTestBase {
EXPECT_TRUE(license_renewal.has_key_control_nonce());
}
void ValidateHasUpdateUsageEntry(const std::string& serialized_metrics)
void ValidateHasUpdateUsageEntry(const drm_metrics::WvCdmMetrics& metrics)
const {
drm_metrics::WvCdmMetricsGroup group;
ASSERT_TRUE(group.ParseFromString(serialized_metrics));
bool has_update_usage_entry_metrics = false;
for (const auto& metrics : group.metrics()) {
for (const auto& session : metrics.session_metrics()) {
has_update_usage_entry_metrics |=
session.crypto_metrics()
.crypto_session_update_usage_entry_time_us().size() > 0;
has_update_usage_entry_metrics |=
session.crypto_metrics().oemcrypto_update_usage_entry().size() > 0;
}
for (const auto& session : metrics.session_metrics()) {
has_update_usage_entry_metrics |=
session.crypto_metrics()
.crypto_session_update_usage_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: " << wvcdm::b2a_hex(serialized_metrics);
}
void QueryKeyStatus(bool streaming, bool expect_renewal,
@@ -1452,9 +1449,9 @@ TEST_P(WvCdmStreamingUsageReportTest, UsageTest) {
}
// Validate that update usage table entry is exercised.
std::string serialized_metrics;
decryptor_.GetSerializedMetrics(&serialized_metrics);
ValidateHasUpdateUsageEntry(serialized_metrics);
drm_metrics::WvCdmMetrics metrics;
ASSERT_EQ(NO_ERROR, decryptor_.GetMetrics(kDefaultCdmIdentifier, &metrics));
ValidateHasUpdateUsageEntry(metrics);
}
INSTANTIATE_TEST_CASE_P(Cdm, WvCdmStreamingUsageReportTest,

View File

@@ -51,7 +51,8 @@ const int kHttpInternalServerError = 500;
const wvcdm::CdmIdentifier kExampleIdentifier = {
wvcdm::EMPTY_SPOID,
"com.example",
"com.example"
"com.example",
7
};
// Protobuf generated classes
@@ -392,15 +393,19 @@ const uint32_t kSingleEncryptedSubSampleIcpLicenseExpirationWindow = 2;
struct SessionSharingSubSampleInfo {
SubSampleInfo* sub_sample;
bool session_sharing_enabled;
wvcdm::CdmIdentifier cdm_identifier;
};
SessionSharingSubSampleInfo session_sharing_sub_samples[] = {
{&clear_sub_sample, false},
{&clear_sub_sample, true},
{&clear_sub_sample_no_key, false},
{&clear_sub_sample_no_key, true},
{&single_encrypted_sub_sample, false},
{&single_encrypted_sub_sample, true}};
{&clear_sub_sample, false, wvcdm::kDefaultCdmIdentifier},
{&clear_sub_sample, true, wvcdm::kDefaultCdmIdentifier},
{&clear_sub_sample_no_key, false, wvcdm::kDefaultCdmIdentifier},
{&clear_sub_sample_no_key, true, wvcdm::kDefaultCdmIdentifier},
{&single_encrypted_sub_sample, false, wvcdm::kDefaultCdmIdentifier},
{&single_encrypted_sub_sample, true, wvcdm::kDefaultCdmIdentifier},
// The last entry simulates session sharing using the non default
// identifier.
{&single_encrypted_sub_sample, true, kExampleIdentifier}};
struct UsageInfoSubSampleInfo {
SubSampleInfo* sub_sample;
@@ -1301,6 +1306,14 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase {
GenerateKeyRequest(init_data, license_type, NULL);
}
void GenerateKeyRequest(const std::string& init_data,
CdmLicenseType license_type,
const CdmIdentifier& identifier) {
CdmAppParameterMap app_parameters;
GenerateKeyRequest(wvcdm::KEY_MESSAGE, "video/mp4", init_data,
app_parameters, license_type, identifier, NULL);
}
void GenerateKeyRequest(const std::string& init_data,
CdmLicenseType license_type,
CdmClientPropertySet* property_set) {
@@ -1342,7 +1355,13 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase {
decryptor_.GenerateKeyRequest(
session_id_, key_set_id, init_data_type, init_data,
license_type, app_parameters, property_set,
cdm_identifier, &key_request));
cdm_identifier, &key_request))
<< "session_id_ " << session_id_ << std::endl
<< "init_data (hex) " << wvcdm::b2a_hex(init_data) << std::endl
<< "key_set_id " << key_set_id << std::endl
<< "cdm_identifier.origin " << cdm_identifier.origin << std::endl
<< "cdm_identifier.app_package_name " << cdm_identifier.app_package_name << std::endl
<< "cdm_identifier.unique_id " << cdm_identifier.unique_id << std::endl;
key_msg_ = key_request.message;
EXPECT_EQ(0u, key_request.url.size());
}
@@ -3862,14 +3881,20 @@ class WvCdmSessionSharingTest
TEST_P(WvCdmSessionSharingTest, SessionSharingTest) {
SessionSharingSubSampleInfo* session_sharing_info = GetParam();
CdmIdentifier cdm_identifier = session_sharing_info->cdm_identifier;
if (!cdm_identifier.IsEquivalentToDefault()) {
Provision(session_sharing_info->cdm_identifier, kLevelDefault);
}
TestWvCdmClientPropertySet property_set;
property_set.set_session_sharing_mode(
session_sharing_info->session_sharing_enabled);
decryptor_.OpenSession(g_key_system, &property_set, kDefaultCdmIdentifier,
NULL, &session_id_);
ASSERT_EQ(NO_ERROR, decryptor_.OpenSession(g_key_system, &property_set,
cdm_identifier, NULL,
&session_id_));
CdmSessionId gp_session_id_1 = session_id_;
GenerateKeyRequest(g_key_id, kLicenseTypeStreaming);
GenerateKeyRequest(g_key_id, kLicenseTypeStreaming, cdm_identifier);
VerifyKeyRequestResponse(g_license_server, g_client_auth);
// TODO(rfrias): Move content information to ConfigTestEnv
@@ -3880,10 +3905,11 @@ TEST_P(WvCdmSessionSharingTest, SessionSharingTest) {
"edef8ba979d64acea3c827dcd51d21ed00000014" // Widevine system id
"08011210bdf1cb4fffc6506b8b7945b0bd2917fb"); // pssh data
decryptor_.OpenSession(g_key_system, &property_set, kDefaultCdmIdentifier,
NULL, &session_id_);
ASSERT_EQ(NO_ERROR, decryptor_.OpenSession(g_key_system, &property_set,
cdm_identifier, NULL,
&session_id_));
CdmSessionId gp_session_id_2 = session_id_;
GenerateKeyRequest(gp_key_id2, kLicenseTypeStreaming);
GenerateKeyRequest(gp_key_id2, kLicenseTypeStreaming, cdm_identifier);
VerifyKeyRequestResponse(g_license_server, gp_client_auth2);
SubSampleInfo* data = session_sharing_info->sub_sample;
@@ -3898,7 +3924,10 @@ TEST_P(WvCdmSessionSharingTest, SessionSharingTest) {
if (session_sharing_info->session_sharing_enabled || !data->is_encrypted) {
EXPECT_EQ(NO_ERROR,
decryptor_.Decrypt(gp_session_id_2, data->validate_key_id,
decryption_parameters));
decryption_parameters))
<< "session_sharing_info->session_sharing_enabled "
<< session_sharing_info->session_sharing_enabled << std::endl
<< "data->is_encrypted " << data->is_encrypted << std::endl;
EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(),
decrypt_buffer.begin()));
} else {
@@ -3907,13 +3936,19 @@ TEST_P(WvCdmSessionSharingTest, SessionSharingTest) {
decryption_parameters));
}
if (!cdm_identifier.IsEquivalentToDefault()) {
// Unprovision both security level certs.
decryptor_.Unprovision(kSecurityLevelL1, cdm_identifier);
decryptor_.Unprovision(kSecurityLevelL3, cdm_identifier);
}
decryptor_.CloseSession(gp_session_id_1);
decryptor_.CloseSession(gp_session_id_2);
}
INSTANTIATE_TEST_CASE_P(Cdm, WvCdmSessionSharingTest,
::testing::Range(&session_sharing_sub_samples[0],
&session_sharing_sub_samples[6]));
&session_sharing_sub_samples[7]));
TEST_F(WvCdmRequestLicenseTest, SessionSharingTest) {
TestWvCdmClientPropertySet property_set;

View File

@@ -30,11 +30,10 @@ class WvContentDecryptionModuleMetricsTest : public ::testing::Test {
wvcdm::WvContentDecryptionModule decryptor_;
};
TEST_F(WvContentDecryptionModuleMetricsTest, NoMetrics) {
// Get metrics before any operations are performed.
std::string serialized_metrics;
decryptor_.GetSerializedMetrics(&serialized_metrics);
EXPECT_TRUE(serialized_metrics.empty());
TEST_F(WvContentDecryptionModuleMetricsTest, IdentifierNotFound) {
drm_metrics::WvCdmMetrics metrics;
ASSERT_EQ(wvcdm::UNKNOWN_ERROR,
decryptor_.GetMetrics(kDefaultCdmIdentifier, &metrics));
}
TEST_F(WvContentDecryptionModuleMetricsTest, EngineOnlyMetrics) {
@@ -48,27 +47,21 @@ TEST_F(WvContentDecryptionModuleMetricsTest, EngineOnlyMetrics) {
decryptor_.GetProvisioningRequest(cert_type, cert_authority,
kDefaultCdmIdentifier, &request,
&provisioning_server_url));
std::string serialized_metrics;
decryptor_.GetSerializedMetrics(&serialized_metrics);
drm_metrics::WvCdmMetricsGroup metrics_group;
ASSERT_TRUE(metrics_group.ParseFromString(serialized_metrics))
<< "Unexpected failure deserializing metrics: "
<< wvcdm::b2a_hex(serialized_metrics);
ASSERT_THAT(metrics_group.metrics().size(), Eq(1));
drm_metrics::WvCdmMetrics metrics;
ASSERT_EQ(wvcdm::NO_ERROR,
decryptor_.GetMetrics(kDefaultCdmIdentifier, &metrics));
// 100 is an arbitrary high value that shouldn't ever occur.
EXPECT_THAT(
metrics_group.metrics(0).engine_metrics()
.oemcrypto_initialization_mode().int_value(),
metrics.engine_metrics().oemcrypto_initialization_mode().int_value(),
Lt(100));
EXPECT_THAT(
metrics_group.metrics(0).engine_metrics()
.oemcrypto_initialization_mode().int_value(),
metrics.engine_metrics().oemcrypto_initialization_mode().int_value(),
Ge(0));
ASSERT_THAT(metrics_group.metrics(0).engine_metrics()
ASSERT_THAT(metrics.engine_metrics()
.cdm_engine_get_provisioning_request_time_us().size(), Eq(1));
EXPECT_THAT(metrics_group.metrics(0).engine_metrics()
EXPECT_THAT(metrics.engine_metrics()
.cdm_engine_get_provisioning_request_time_us(0)
.operation_count(),
Eq(1u));
@@ -85,81 +78,80 @@ TEST_F(WvContentDecryptionModuleMetricsTest, EngineAndSessionMetrics) {
decryptor_.OpenSession(key_system, NULL,
kDefaultCdmIdentifier, NULL, &session_id));
// The metrics will have a single engine and single session stats.
drm_metrics::WvCdmMetrics metrics;
ASSERT_EQ(wvcdm::NO_ERROR,
decryptor_.GetMetrics(kDefaultCdmIdentifier, &metrics));
std::string serialized_metrics;
decryptor_.GetSerializedMetrics(&serialized_metrics);
ASSERT_TRUE(metrics.SerializeToString(&serialized_metrics));
// Spot check some metric values.
drm_metrics::WvCdmMetricsGroup metrics_group;
ASSERT_TRUE(metrics_group.ParseFromString(serialized_metrics))
<< "Unexpected failure deserializing metrics: "
<< wvcdm::b2a_hex(serialized_metrics);
ASSERT_THAT(metrics_group.metrics().size(), Eq(1));
// Validate engine-level metrics.
EXPECT_TRUE(metrics_group.metrics(0).engine_metrics()
.has_oemcrypto_initialization_mode());
ASSERT_THAT(
metrics_group.metrics(0).engine_metrics().cdm_engine_open_session().size(),
Eq(1));
EXPECT_THAT(metrics_group.metrics(0).engine_metrics()
.cdm_engine_open_session(0).count(), Eq(1));
EXPECT_THAT(metrics_group.metrics(0).engine_metrics()
EXPECT_TRUE(metrics.engine_metrics().has_oemcrypto_initialization_mode());
ASSERT_THAT(metrics.engine_metrics().cdm_engine_open_session().size(), Eq(1));
EXPECT_THAT(metrics.engine_metrics().cdm_engine_open_session(0).count(),
Eq(1));
EXPECT_THAT(metrics.engine_metrics()
.cdm_engine_open_session(0).attributes().error_code(),
Eq(CdmResponseType::NEED_PROVISIONING));
// Validate a session-level metric.
ASSERT_THAT(metrics_group.metrics(0).session_metrics().size(), Eq(1));
ASSERT_THAT(metrics.session_metrics().size(), Eq(1));
EXPECT_THAT(
metrics_group.metrics(0).session_metrics(0)
.cdm_session_life_span_ms().double_value(),
metrics.session_metrics(0).cdm_session_life_span_ms().double_value(),
Gt(0.0))
<< "Unexpected failure with session_metrics: "
<< wvcdm::b2a_hex(serialized_metrics);
}
TEST_F(WvContentDecryptionModuleMetricsTest, MultipleEngineMetric) {
TEST_F(WvContentDecryptionModuleMetricsTest,
DifferentCdmIdentifiersHaveDifferentMetrics) {
CdmSessionId session_id;
wvcdm::CdmKeySystem key_system("com.widevine");
CdmIdentifier identifier = { "foo", "bar", "baz" };
CdmIdentifier identifiers[] = { kDefaultCdmIdentifier,
{ "foo", "bar", "baz", 7 },
// Note that this has all the same parameters
// as the one above except for the unique_id.
{ "foo", "bar", "baz", 8 }};
const int cdm_engine_count = 3;
for (int i = 0; i < cdm_engine_count; i++) {
// To make sure we can detect different engine metrics,
// make the open session call a different number of times for
// each identifier.
for (int j = 0; j <= i; j ++) {
EXPECT_EQ(CdmResponseType::NEED_PROVISIONING,
decryptor_.OpenSession(key_system, NULL,
identifiers[i], NULL, &session_id));
}
}
// Openning the session will fail with NEEDS_PROVISIONING error. But it will
// still create some session-level stats.
EXPECT_EQ(CdmResponseType::NEED_PROVISIONING,
decryptor_.OpenSession(key_system, NULL,
kDefaultCdmIdentifier, NULL, &session_id));
// Open a second engine with a custom identifier.
EXPECT_EQ(CdmResponseType::NEED_PROVISIONING,
decryptor_.OpenSession(key_system, NULL,
identifier, NULL, &session_id));
for (int i = 0; i < cdm_engine_count; i++) {
drm_metrics::WvCdmMetrics metrics;
metrics.Clear();
ASSERT_EQ(wvcdm::NO_ERROR,
decryptor_.GetMetrics(identifiers[i], &metrics));
std::string serialized_metrics;
ASSERT_TRUE(metrics.SerializeToString(&serialized_metrics));
// The metrics will now have two engines with single session stats each.
std::string serialized_metrics;
decryptor_.GetSerializedMetrics(&serialized_metrics);
// Spot check some metric values.
drm_metrics::WvCdmMetricsGroup metrics_group;
ASSERT_TRUE(metrics_group.ParseFromString(serialized_metrics));
// Two engine-level metrics are expected.
ASSERT_THAT(metrics_group.metrics().size(), Eq(2));
for (int i = 0; i < metrics_group.metrics().size(); i++) {
drm_metrics::WvCdmMetrics::EngineMetrics engine_metrics =
metrics_group.metrics(i).engine_metrics();
// Validate an engine-level metric.
EXPECT_TRUE(engine_metrics.has_oemcrypto_initialization_mode());
ASSERT_THAT(engine_metrics.cdm_engine_open_session().size(), Eq(1));
EXPECT_THAT(engine_metrics.cdm_engine_open_session(0).count(), Eq(1));
ASSERT_THAT(metrics.engine_metrics().cdm_engine_open_session().size(),
Eq(1));
// The number of times open session was called should match the index
// of the identifier
EXPECT_THAT(metrics.engine_metrics().cdm_engine_open_session(0).count(),
Eq(i + 1));
EXPECT_THAT(
engine_metrics.cdm_engine_open_session(0).attributes().error_code(),
metrics.engine_metrics()
.cdm_engine_open_session(0).attributes().error_code(),
Eq(CdmResponseType::NEED_PROVISIONING));
// Validate a session-level metric.
ASSERT_THAT(metrics_group.metrics(i).session_metrics().size(), Eq(1));
EXPECT_THAT(metrics_group.metrics(i).session_metrics(0)
.cdm_session_life_span_ms().double_value(), Gt(0.0));
// Spot check a session-level metric.
ASSERT_THAT(metrics.session_metrics().size(), Eq(i + 1))
<< "Unexpected failure with session_metrics: "
<< wvcdm::b2a_hex(serialized_metrics);
EXPECT_THAT(metrics.session_metrics(0)
.cdm_session_life_span_ms().double_value(), Gt(0.0))
<< "Unexpected failure with session_metrics: "
<< wvcdm::b2a_hex(serialized_metrics);
}
}
}
} // wvcdm namespace