Fixes missing or broken metrics in Widevine CDM

A few metrics were missing or not properly collected in the CDM metrics.
This CL addresses them.

Bug: 64570194
Bug: 72866232

Test: Unit tests and Google Play manual test.
Change-Id: I3a3aa4fb3eb8422c9c8c398016f02409307beb33
This commit is contained in:
Adam Stone
2018-03-14 15:20:03 -07:00
parent b19f0d106f
commit e1fe90372f
10 changed files with 157 additions and 61 deletions

View File

@@ -18,7 +18,6 @@
#include "oemcrypto_adapter.h"
#include "scoped_ptr.h"
#include "service_certificate.h"
#include "timer_metric.h"
#include "wv_cdm_constants.h"
#include "wv_cdm_types.h"
@@ -316,7 +315,6 @@ class CdmEngine {
* it is published.
*/
metrics::EngineMetrics metrics_;
metrics::TimerMetric life_span_;
CdmSessionMap session_map_;
CdmReleaseKeySetMap release_key_sets_;

View File

@@ -51,22 +51,23 @@ class UsageTableHeader {
bool Init(CdmSecurityLevel security_level, CryptoSession* crypto_session);
// |persistent_license| false indicates usage info record
CdmResponseType AddEntry(CryptoSession* crypto_session,
bool persistent_license,
const CdmKeySetId& key_set_id,
const std::string& usage_info_filename,
uint32_t* usage_entry_number);
CdmResponseType LoadEntry(CryptoSession* crypto_session,
const CdmUsageEntry& usage_entry,
uint32_t usage_entry_number);
CdmResponseType UpdateEntry(CryptoSession* crypto_session,
CdmUsageEntry* usage_entry);
virtual CdmResponseType AddEntry(CryptoSession* crypto_session,
bool persistent_license,
const CdmKeySetId& key_set_id,
const std::string& usage_info_filename,
uint32_t* usage_entry_number);
virtual CdmResponseType LoadEntry(CryptoSession* crypto_session,
const CdmUsageEntry& usage_entry,
uint32_t usage_entry_number);
virtual CdmResponseType UpdateEntry(CryptoSession* crypto_session,
CdmUsageEntry* usage_entry);
// The licenses or usage info records specified by |usage_entry_number|
// should not be in use by any open CryptoSession objects when calls
// to DeleteEntry and MoveEntry are made.
CdmResponseType DeleteEntry(uint32_t usage_entry_number, DeviceFiles* handle,
metrics::CryptoMetrics* metrics);
virtual CdmResponseType DeleteEntry(uint32_t usage_entry_number,
DeviceFiles* handle,
metrics::CryptoMetrics* metrics);
private:
CdmResponseType MoveEntry(uint32_t from /* usage entry number */,

View File

@@ -70,7 +70,6 @@ CdmEngine::CdmEngine(FileSystem* file_system, const std::string& spoid)
seeded_ = true;
}
life_span_.Start();
metrics_.cdm_engine_creation_time_millis_.Record(clock_.GetCurrentTime());
std::string cdm_version;
@@ -82,9 +81,7 @@ CdmEngine::CdmEngine(FileSystem* file_system, const std::string& spoid)
}
}
CdmEngine::~CdmEngine() {
M_RECORD(&metrics_, cdm_engine_life_span_, life_span_.AsMs());
}
CdmEngine::~CdmEngine() {}
CdmResponseType CdmEngine::SetProvisioningServiceCertificate(
const std::string& certificate) {

View File

@@ -916,8 +916,13 @@ CdmResponseType CdmSession::UpdateUsageEntryInformation() {
return INCORRECT_USAGE_SUPPORT_TYPE_2;
}
CdmResponseType sts = usage_table_header_->UpdateEntry(crypto_session_.get(),
&usage_entry_);
CdmResponseType sts = NO_ERROR;
M_TIME(
sts = usage_table_header_->UpdateEntry(crypto_session_.get(),
&usage_entry_),
crypto_metrics_,
crypto_session_update_usage_entry_,
sts);
if (sts != NO_ERROR) return sts;

View File

@@ -2093,6 +2093,7 @@ CdmResponseType CryptoSession::UpdateUsageEntry(
size_t usage_entry_len = 0;
OEMCryptoResult result = OEMCrypto_UpdateUsageEntry(
oec_session_id_, NULL, &usage_table_header_len, NULL, &usage_entry_len);
metrics_->oemcrypto_update_usage_entry_.Increment(result);
if (result == OEMCrypto_ERROR_SHORT_BUFFER) {
usage_table_header->resize(usage_table_header_len);
@@ -2105,6 +2106,7 @@ CdmResponseType CryptoSession::UpdateUsageEntry(
&usage_table_header_len,
reinterpret_cast<uint8_t*>(const_cast<char*>(usage_entry->data())),
&usage_entry_len);
metrics_->oemcrypto_update_usage_entry_.Increment(result);
}
if (result != OEMCrypto_SUCCESS) {

View File

@@ -4,13 +4,25 @@
#include <gtest/gtest.h>
#include "cdm_session.h"
#include "crypto_key.h"
#include "metrics.pb.h"
#include "properties.h"
#include "scoped_ptr.h"
#include "service_certificate.h"
#include "string_conversions.h"
#include "test_printers.h"
#include "usage_table_header.h"
#include "wv_cdm_constants.h"
using ::testing::_;
using ::testing::Eq;
using ::testing::Invoke;
using ::testing::NiceMock;
using ::testing::NotNull;
using ::testing::Return;
using ::testing::SetArgPointee;
using ::testing::Sequence;
using ::testing::StrEq;
namespace wvcdm {
namespace {
@@ -100,10 +112,23 @@ class MockDeviceFiles : public DeviceFiles {
std::string*, uint32_t*));
};
class MockUsageTableHeader : public UsageTableHeader {
public:
MockUsageTableHeader() : UsageTableHeader() {}
MOCK_METHOD2(UpdateEntry, CdmResponseType(CryptoSession* crypto_session,
CdmUsageEntry* usage_entry));
};
class MockCryptoSession : public CryptoSession {
public:
MockCryptoSession(metrics::CryptoMetrics* crypto_metrics)
: CryptoSession(crypto_metrics) { }
: CryptoSession(crypto_metrics) {
// By default, call the concrete implementation of GetUsageSupportType.
ON_CALL(*this, GetUsageSupportType(_))
.WillByDefault(
Invoke(this, &MockCryptoSession::BaseGetUsageSupportType));
}
MOCK_METHOD1(GetClientToken, bool(std::string*));
MOCK_METHOD1(GetProvisioningToken, bool(std::string*));
MOCK_METHOD0(GetPreProvisionTokenType, CdmClientTokenType());
@@ -112,6 +137,12 @@ class MockCryptoSession : public CryptoSession {
MOCK_METHOD1(Open, CdmResponseType(SecurityLevel));
MOCK_METHOD1(LoadCertificatePrivateKey, bool(std::string&));
MOCK_METHOD0(DeleteAllUsageReports, CdmResponseType());
MOCK_METHOD1(GetUsageSupportType, CdmResponseType(CdmUsageSupportType* type));
MOCK_METHOD0(GetUsageTableHeader, UsageTableHeader*());
CdmResponseType BaseGetUsageSupportType(CdmUsageSupportType* type) {
return CryptoSession::GetUsageSupportType(type);
}
};
class MockPolicyEngine : public PolicyEngine {
@@ -129,19 +160,11 @@ class MockCdmLicense : public CdmLicense {
MOCK_METHOD7(Init, bool(const std::string&, CdmClientTokenType,
const std::string&, bool, const std::string&,
CryptoSession*, PolicyEngine*));
MOCK_METHOD0(provider_session_token, std::string());
};
} // namespace
// gmock methods
using ::testing::_;
using ::testing::Eq;
using ::testing::NotNull;
using ::testing::Return;
using ::testing::SetArgPointee;
using ::testing::Sequence;
using ::testing::StrEq;
class CdmSessionTest : public ::testing::Test {
protected:
virtual void SetUp() {
@@ -149,7 +172,7 @@ class CdmSessionTest : public ::testing::Test {
// Inject testing mocks.
license_parser_ = new MockCdmLicense(cdm_session_->session_id());
cdm_session_->set_license_parser(license_parser_);
crypto_session_ = new MockCryptoSession(&crypto_metrics_);
crypto_session_ = new NiceMock<MockCryptoSession>(&crypto_metrics_);
cdm_session_->set_crypto_session(crypto_session_);
policy_engine_ = new MockPolicyEngine();
cdm_session_->set_policy_engine(policy_engine_);
@@ -167,9 +190,10 @@ class CdmSessionTest : public ::testing::Test {
scoped_ptr<CdmSession> cdm_session_;
MockCdmLicense* license_parser_;
metrics::CryptoMetrics crypto_metrics_;
MockCryptoSession* crypto_session_;
NiceMock<MockCryptoSession>* crypto_session_;
MockPolicyEngine* policy_engine_;
MockDeviceFiles* file_handle_;
MockUsageTableHeader usage_table_header_;
};
TEST_F(CdmSessionTest, InitWithBuiltInCertificate) {
@@ -282,4 +306,56 @@ TEST_F(CdmSessionTest, InitNeedsProvisioning) {
ASSERT_EQ(NEED_PROVISIONING, cdm_session_->Init(NULL));
}
TEST_F(CdmSessionTest, UpdateUsageEntry) {
// Setup common expectations for initializing the CdmSession object.
Sequence crypto_session_seq;
CdmSecurityLevel level = kSecurityLevelL1;
EXPECT_CALL(*crypto_session_, Open(Eq(kLevelDefault)))
.InSequence(crypto_session_seq)
.WillOnce(Return(NO_ERROR));
EXPECT_CALL(*crypto_session_, GetSecurityLevel())
.InSequence(crypto_session_seq)
.WillOnce(Return(level));
EXPECT_CALL(*crypto_session_, GetPreProvisionTokenType())
.WillOnce(Return(kClientTokenKeybox));
EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true));
EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull(),
NotNull(), _))
.WillOnce(DoAll(SetArgPointee<0>(kToken), SetArgPointee<1>(kWrappedKey),
Return(true)));
EXPECT_CALL(*crypto_session_, LoadCertificatePrivateKey(StrEq(kWrappedKey)))
.InSequence(crypto_session_seq)
.WillOnce(Return(true));
EXPECT_CALL(*crypto_session_, GetUsageTableHeader())
.WillOnce(Return(&usage_table_header_));
EXPECT_CALL(*license_parser_,
Init(Eq(kToken), Eq(kClientTokenDrmCert), Eq(kEmptyString),
false, Eq(kEmptyString), Eq(crypto_session_),
Eq(policy_engine_)))
.WillOnce(Return(true));
// Set up mocks and expectations for the UpdateUsageEntryInformation call.
EXPECT_CALL(*crypto_session_, GetUsageSupportType(_))
.WillOnce(DoAll(SetArgPointee<0>(kUsageEntrySupport), Return(NO_ERROR)));
EXPECT_CALL(*license_parser_, provider_session_token())
.WillOnce(Return("Mock provider session token"));
EXPECT_CALL(usage_table_header_, UpdateEntry(NotNull(), NotNull()))
.WillOnce(Return(NO_ERROR));
EXPECT_EQ(NO_ERROR, cdm_session_->Init(NULL));
EXPECT_EQ(kUsageEntrySupport, cdm_session_->get_usage_support_type())
<< "Usage support type: " << cdm_session_->get_usage_support_type();
EXPECT_EQ(NO_ERROR, cdm_session_->UpdateUsageEntryInformation());
// Verify the UsageEntry metric is set.
drm_metrics::WvCdmMetrics::SessionMetrics metrics;
cdm_session_->GetMetrics()->Serialize(&metrics);
std::string serialized_metrics;
ASSERT_TRUE(metrics.SerializeToString(&serialized_metrics));
EXPECT_GT(metrics.crypto_metrics()
.crypto_session_update_usage_entry_time_us().size(), 0)
<< "Missing update usage entry metric. Metrics: "
<< wvcdm::b2a_hex(serialized_metrics);
}
} // namespace wvcdm

View File

@@ -14,6 +14,7 @@
#include "counter_metric.h"
#include "event_metric.h"
#include "metrics.pb.h"
#include "timer_metric.h"
#include "value_metric.h"
#include "wv_cdm_types.h"
@@ -144,6 +145,8 @@ class CryptoMetrics {
ValueMetric<uint32_t> crypto_session_system_id_;
EventMetric<kErrorCodeFieldNumber, CdmResponseType>
crypto_session_update_usage_information_;
EventMetric<kErrorCodeFieldNumber, CdmResponseType>
crypto_session_update_usage_entry_;
ValueMetric<bool> crypto_session_usage_information_support_;
/* OEMCRYPTO */
ValueMetric<uint32_t> oemcrypto_api_version_;
@@ -225,6 +228,8 @@ class CryptoMetrics {
ValueMetric<CdmUsageSupportType> oemcrypto_usage_table_support_;
CounterMetric<kOemCryptoResultFieldNumber, OEMCryptoResult>
oemcrypto_update_usage_table_;
CounterMetric<kOemCryptoResultFieldNumber, OEMCryptoResult>
oemcrypto_update_usage_entry_;
};
// This class contains session-scoped metrics. All properties and
@@ -371,7 +376,6 @@ class EngineMetrics {
cdm_engine_get_usage_info_;
EventMetric<kErrorCodeFieldNumber, CdmResponseType>
cdm_engine_handle_provisioning_response_;
ValueMetric<double> cdm_engine_life_span_; // Milliseconds
CounterMetric<kErrorCodeFieldNumber, CdmResponseType>
cdm_engine_open_key_set_session_;
CounterMetric<kErrorCodeFieldNumber, CdmResponseType>
@@ -395,6 +399,8 @@ class EngineMetrics {
private:
Lock session_metrics_lock_;
std::vector<metrics::SessionMetrics *> session_metrics_list_;
// This is used to populate the engine lifespan metric
metrics::TimerMetric life_span_internal_;
CryptoMetrics crypto_metrics_;
std::string app_package_name_;

View File

@@ -88,7 +88,7 @@ message WvCdmMetrics {
// This contains metrics that were captured at the CryptoSession level. These
// include CryptoSession metrics and most OEMCrypto metrics.
// next id: 56
// next id: 58
message CryptoMetrics {
// Crypto Session Metrics.
optional ValueMetric crypto_session_security_level = 1;
@@ -105,6 +105,7 @@ message WvCdmMetrics {
repeated DistributionMetric crypto_session_open_time_us = 12;
optional ValueMetric crypto_session_system_id = 13;
repeated DistributionMetric crypto_session_update_usage_information_time_us = 14;
repeated DistributionMetric crypto_session_update_usage_entry_time_us = 56;
optional ValueMetric crypto_session_usage_information_support = 15;
// OemCrypto metrics.
optional ValueMetric oemcrypto_api_version = 16;
@@ -147,6 +148,7 @@ message WvCdmMetrics {
repeated DistributionMetric oemcrypto_select_key_time_us = 53;
optional ValueMetric oemcrypto_usage_table_support = 54;
repeated CounterMetric oemcrypto_update_usage_table = 55;
repeated CounterMetric oemcrypto_update_usage_entry = 57;
}
// This contains metrics that were captured within a CdmSession. This contains
@@ -184,7 +186,7 @@ message WvCdmMetrics {
repeated CounterMetric cdm_engine_get_secure_stop_ids = 15;
repeated DistributionMetric cdm_engine_get_usage_info_time_us = 16;
repeated DistributionMetric cdm_engine_handle_provisioning_response_time_us = 17;
optional ValueMetric cdm_engine_life_span = 18;
optional ValueMetric cdm_engine_life_span_ms = 18;
repeated CounterMetric cdm_engine_open_key_set_session = 19;
repeated CounterMetric cdm_engine_open_session = 20;
repeated DistributionMetric cdm_engine_query_key_status_time_us = 21;

View File

@@ -62,6 +62,8 @@ void CryptoMetrics::Serialize(WvCdmMetrics::CryptoMetrics *crypto_metrics) {
crypto_session_update_usage_information_.ToProto(
crypto_metrics
->mutable_crypto_session_update_usage_information_time_us());
crypto_session_update_usage_entry_.ToProto(
crypto_metrics->mutable_crypto_session_update_usage_entry_time_us());
crypto_metrics->set_allocated_crypto_session_usage_information_support(
crypto_session_usage_information_support_.ToProto());
@@ -145,6 +147,8 @@ void CryptoMetrics::Serialize(WvCdmMetrics::CryptoMetrics *crypto_metrics) {
oemcrypto_usage_table_support_.ToProto());
oemcrypto_update_usage_table_.ToProto(
crypto_metrics->mutable_oemcrypto_update_usage_table());
oemcrypto_update_usage_entry_.ToProto(
crypto_metrics->mutable_oemcrypto_update_usage_entry());
}
SessionMetrics::SessionMetrics()
@@ -227,30 +231,9 @@ OemCryptoDynamicAdapterMetrics &GetDynamicAdapterMetricsInstance() {
return *adapter_metrics;
}
EngineMetrics::EngineMetrics()
: cdm_engine_add_key_(),
cdm_engine_cdm_version_(),
cdm_engine_close_session_(),
cdm_engine_creation_time_millis_(),
cdm_engine_decrypt_(),
cdm_engine_find_session_for_key_(),
cdm_engine_generate_key_request_(),
cdm_engine_get_provisioning_request_(),
cdm_engine_get_secure_stop_ids_(),
cdm_engine_get_usage_info_(),
cdm_engine_handle_provisioning_response_(),
cdm_engine_life_span_(),
cdm_engine_open_key_set_session_(),
cdm_engine_open_session_(),
cdm_engine_query_key_status_(),
cdm_engine_release_all_usage_info_(),
cdm_engine_release_usage_info_(),
cdm_engine_remove_all_usage_info_(),
cdm_engine_remove_keys_(),
cdm_engine_remove_usage_info_(),
cdm_engine_restore_key_(),
cdm_engine_unprovision_(),
app_package_name_("") {}
EngineMetrics::EngineMetrics() {
life_span_internal_.Start();
}
EngineMetrics::~EngineMetrics() {
AutoLock lock(session_metrics_lock_);
@@ -309,6 +292,9 @@ void EngineMetrics::SetAppPackageName(const std::string &app_package_name) {
void EngineMetrics::SerializeEngineMetrics(
WvCdmMetrics::EngineMetrics *engine_metrics) {
// Set the engine lifespan at the time of serialization.
engine_metrics->mutable_cdm_engine_life_span_ms()->set_int_value(
life_span_internal_.AsMs());
cdm_engine_add_key_.ToProto(
engine_metrics->mutable_cdm_engine_add_key_time_us());
engine_metrics->set_allocated_cdm_engine_cdm_version(
@@ -332,8 +318,6 @@ void EngineMetrics::SerializeEngineMetrics(
cdm_engine_handle_provisioning_response_.ToProto(
engine_metrics
->mutable_cdm_engine_handle_provisioning_response_time_us());
engine_metrics->set_allocated_cdm_engine_life_span(
cdm_engine_life_span_.ToProto()),
cdm_engine_open_key_set_session_.ToProto(
engine_metrics->mutable_cdm_engine_open_key_set_session());
cdm_engine_open_session_.ToProto(

View File

@@ -605,6 +605,26 @@ class WvCdmExtendedDurationTest : public WvCdmTestBase {
EXPECT_TRUE(license_renewal.has_key_control_nonce());
}
void ValidateHasUpdateUsageEntry(const std::string& serialized_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;
}
}
EXPECT_TRUE(has_update_usage_entry_metrics)
<< "metrics: " << wvcdm::b2a_hex(serialized_metrics);
}
void QueryKeyStatus(bool streaming, bool expect_renewal,
int64_t* license_duration_remaining,
int64_t* playback_duration_remaining) {
@@ -1430,6 +1450,11 @@ TEST_P(WvCdmStreamingUsageReportTest, UsageTest) {
break;
}
}
// Validate that update usage table entry is exercised.
std::string serialized_metrics;
decryptor_.GetSerializedMetrics(&serialized_metrics);
ValidateHasUpdateUsageEntry(serialized_metrics);
}
INSTANTIATE_TEST_CASE_P(Cdm, WvCdmStreamingUsageReportTest,