diff --git a/libwvdrmengine/cdm/core/include/cdm_engine.h b/libwvdrmengine/cdm/core/include/cdm_engine.h index 754960e4..657b4c8c 100644 --- a/libwvdrmengine/cdm/core/include/cdm_engine.h +++ b/libwvdrmengine/cdm/core/include/cdm_engine.h @@ -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_; diff --git a/libwvdrmengine/cdm/core/include/cdm_session.h b/libwvdrmengine/cdm/core/include/cdm_session.h index e14cc797..58f55668 100644 --- a/libwvdrmengine/cdm/core/include/cdm_session.h +++ b/libwvdrmengine/cdm/core/include/cdm_session.h @@ -199,6 +199,12 @@ class CdmSession { bool UpdateUsageInfo(); + CdmResponseType GenerateKeyRequestInternal( + const InitializationData& init_data, CdmLicenseType license_type, + const CdmAppParameterMap& app_parameters, CdmKeyRequest* key_request); + virtual CdmResponseType AddKeyInternal(const CdmKeyResponse& key_response); + void UpdateRequestLatencyTiming(CdmResponseType sts); + // These setters are for testing only. Takes ownership of the pointers. void set_license_parser(CdmLicense* license_parser); void set_crypto_session(CryptoSession* crypto_session); @@ -209,6 +215,8 @@ class CdmSession { metrics::SessionMetrics* metrics_; metrics::CryptoMetrics* crypto_metrics_; metrics::TimerMetric life_span_; + metrics::TimerMetric license_request_latency_; + CdmKeyRequestType key_request_type_; bool initialized_; bool closed_; // Session closed, but final shared_ptr has not been released. diff --git a/libwvdrmengine/cdm/core/include/usage_table_header.h b/libwvdrmengine/cdm/core/include/usage_table_header.h index 78f3b9ec..76be9c36 100644 --- a/libwvdrmengine/cdm/core/include/usage_table_header.h +++ b/libwvdrmengine/cdm/core/include/usage_table_header.h @@ -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 */, diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index 49ba61b0..d849da28 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -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) { diff --git a/libwvdrmengine/cdm/core/src/cdm_session.cpp b/libwvdrmengine/cdm/core/src/cdm_session.cpp index 3de40213..5cb77013 100644 --- a/libwvdrmengine/cdm/core/src/cdm_session.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_session.cpp @@ -324,7 +324,23 @@ CdmResponseType CdmSession::RestoreUsageSession( return KEY_ADDED; } +// This is a thin wrapper that initiates the latency metric. CdmResponseType CdmSession::GenerateKeyRequest( + const InitializationData& init_data, CdmLicenseType license_type, + const CdmAppParameterMap& app_parameters, CdmKeyRequest* key_request) { + CdmResponseType result = GenerateKeyRequestInternal( + init_data, license_type, app_parameters, key_request); + // Note that GenerateReleaseRequest and GenerateRenewalRequest will initialize + // the timer themselves. This is duplicate because there are duplicate paths + // for calling GenerateReleaseRequest and GenerateRenewalRequest. + if (result == KEY_MESSAGE) { + key_request_type_ = key_request->type; + license_request_latency_.Start(); // Start or restart timer. + } + return result; +} + +CdmResponseType CdmSession::GenerateKeyRequestInternal( const InitializationData& init_data, CdmLicenseType license_type, const CdmAppParameterMap& app_parameters, CdmKeyRequest* key_request) { @@ -410,8 +426,15 @@ CdmResponseType CdmSession::GenerateKeyRequest( } } -// AddKey() - Accept license response and extract key info. +// This thin wrapper allows us to update metrics. CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response) { + CdmResponseType sts = AddKeyInternal(key_response); + UpdateRequestLatencyTiming(sts); + return sts; +} + +// AddKeyInternal() - Accept license response and extract key info. +CdmResponseType CdmSession::AddKeyInternal(const CdmKeyResponse& key_response) { if (!initialized_) { LOGE("CdmSession::AddKey: not initialized"); return NOT_INITIALIZED_ERROR; @@ -602,6 +625,8 @@ CdmResponseType CdmSession::GenerateRenewalRequest( if (is_offline_) { offline_key_renewal_request_ = key_request->message; } + key_request_type_ = key_request->type; + license_request_latency_.Start(); // Start or restart timer. return KEY_MESSAGE; } @@ -613,6 +638,10 @@ CdmResponseType CdmSession::RenewKey(const CdmKeyResponse& key_response) { } CdmResponseType sts = license_parser_->HandleKeyUpdateResponse(true, key_response); + + // Record the timing on success. + UpdateRequestLatencyTiming(sts); + if (sts != KEY_ADDED) return (sts == KEY_ERROR) ? RENEW_KEY_ERROR_1 : sts; if (is_offline_) { @@ -630,6 +659,7 @@ CdmResponseType CdmSession::GenerateReleaseRequest( return NOT_INITIALIZED_ERROR; } is_release_ = true; + license_request_latency_.Clear(); CdmResponseType status = license_parser_->PrepareKeyUpdateRequest( false, app_parameters_, usage_table_header_ == NULL ? NULL : this, &key_request->message, &key_request->url); @@ -658,6 +688,10 @@ CdmResponseType CdmSession::GenerateReleaseRequest( return RELEASE_USAGE_INFO_FAILED; } } + + key_request_type_ = key_request->type; + license_request_latency_.Start(); // Start or restart timer. + return KEY_MESSAGE; } @@ -669,6 +703,9 @@ CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) { } CdmResponseType sts = license_parser_->HandleKeyUpdateResponse(false, key_response); + // Record the timing on success. + UpdateRequestLatencyTiming(sts); + if (sts != KEY_ADDED) return (sts == KEY_ERROR) ? RELEASE_KEY_ERROR : sts; if (is_offline_ || has_provider_session_token()) { @@ -916,8 +953,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; @@ -1041,6 +1083,14 @@ bool CdmSession::UpdateUsageInfo() { usage_data); } +void CdmSession::UpdateRequestLatencyTiming(CdmResponseType sts) { + if (sts == KEY_ADDED && license_request_latency_.IsStarted()) { + metrics_->cdm_session_license_request_latency_ms_.Record( + license_request_latency_.AsMs(), key_request_type_); + } + license_request_latency_.Clear(); +} + // For testing only - takes ownership of pointers void CdmSession::set_license_parser(CdmLicense* license_parser) { diff --git a/libwvdrmengine/cdm/core/src/crypto_session.cpp b/libwvdrmengine/cdm/core/src/crypto_session.cpp index e7a7be58..d3dfca58 100644 --- a/libwvdrmengine/cdm/core/src/crypto_session.cpp +++ b/libwvdrmengine/cdm/core/src/crypto_session.cpp @@ -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(const_cast(usage_entry->data())), &usage_entry_len); + metrics_->oemcrypto_update_usage_entry_.Increment(result); } if (result != OEMCrypto_SUCCESS) { diff --git a/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp b/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp index 89fad2b5..4e5232a0 100644 --- a/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp +++ b/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp @@ -15,6 +15,7 @@ #include "file_store.h" #include "license_request.h" #include "log.h" +#include "metrics.pb.h" #include "OEMCryptoCENC.h" #include "properties.h" #include "scoped_ptr.h" @@ -26,6 +27,9 @@ namespace wvcdm { +using drm_metrics::WvCdmMetrics; +using drm_metrics::DistributionMetric; + namespace { // Http OK response code. @@ -460,12 +464,43 @@ class WvCdmEngineTest : public WvCdmEnginePreProvTest { CdmKeySetId key_set_id; EXPECT_EQ(KEY_ADDED, cdm_engine_.AddKey(session_id_, resp, &key_set_id)); + VerifyLicenseRequestLatency(CdmKeyRequestType::kKeyRequestTypeInitial, + *cdm_engine_.GetMetrics()); } void VerifyRenewalKeyResponse(const std::string& server_url, const std::string& client_auth) { std::string resp = GetKeyRequestResponse(server_url, client_auth); EXPECT_EQ(KEY_ADDED, cdm_engine_.RenewKey(session_id_, resp)); + VerifyLicenseRequestLatency(CdmKeyRequestType::kKeyRequestTypeRenewal, + *cdm_engine_.GetMetrics()); + } + + void VerifyLicenseRequestLatency( + CdmKeyRequestType key_request_type, + const metrics::EngineMetrics& engine_metrics) { + WvCdmMetrics metrics_proto; + engine_metrics.Serialize(&metrics_proto); + bool has_request_type = false; + for (int i = 0; i < metrics_proto.session_metrics_size(); i++) { + WvCdmMetrics::SessionMetrics session_metrics = + metrics_proto.session_metrics(i); + for (int j = 0; + j < session_metrics.cdm_session_license_request_latency_ms_size(); + j++) { + DistributionMetric latency_distribution = + session_metrics.cdm_session_license_request_latency_ms(j); + if (latency_distribution.attributes().key_request_type() + == key_request_type && latency_distribution.operation_count() > 0) { + has_request_type = true; + } + } + } + std::string serialized_metrics; + ASSERT_TRUE(metrics_proto.SerializeToString(&serialized_metrics)); + EXPECT_TRUE(has_request_type) + << "Expected request type " << key_request_type << " was not found. " + << "metrics: " << wvcdm::b2a_hex(serialized_metrics); } std::string server_url_; diff --git a/libwvdrmengine/cdm/core/test/cdm_session_unittest.cpp b/libwvdrmengine/cdm/core/test/cdm_session_unittest.cpp index c7538e2b..5661e522 100644 --- a/libwvdrmengine/cdm/core/test/cdm_session_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/cdm_session_unittest.cpp @@ -4,13 +4,25 @@ #include #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(&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 cdm_session_; MockCdmLicense* license_parser_; metrics::CryptoMetrics crypto_metrics_; - MockCryptoSession* crypto_session_; + NiceMock* 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 diff --git a/libwvdrmengine/cdm/metrics/include/counter_metric.h b/libwvdrmengine/cdm/metrics/include/counter_metric.h index 0b21acb3..da4d5f95 100644 --- a/libwvdrmengine/cdm/metrics/include/counter_metric.h +++ b/libwvdrmengine/cdm/metrics/include/counter_metric.h @@ -106,7 +106,7 @@ class CounterMetric : public BaseCounterMetric { } void ToProto(::google::protobuf::RepeatedPtrField - *counters); + *counters) const; private: friend class CounterMetricTest; @@ -121,7 +121,7 @@ template <> inline void CounterMetric<0, util::Unused, 0, util::Unused, 0, util::Unused, 0, util::Unused>:: ToProto(::google::protobuf::RepeatedPtrField - *counters) { + *counters) const { const std::map* values = GetValues(); for (std::map::const_iterator it = values->begin(); @@ -135,7 +135,7 @@ template inline void CounterMetric::ToProto( ::google::protobuf::RepeatedPtrField - *counters) { + *counters) const { const std::map* values = GetValues(); for (std::map::const_iterator it = values->begin(); it != values->end(); it++) { diff --git a/libwvdrmengine/cdm/metrics/include/event_metric.h b/libwvdrmengine/cdm/metrics/include/event_metric.h index a1142055..52fb1c24 100644 --- a/libwvdrmengine/cdm/metrics/include/event_metric.h +++ b/libwvdrmengine/cdm/metrics/include/event_metric.h @@ -110,7 +110,7 @@ class EventMetric : public BaseEventMetric { void ToProto( ::google::protobuf::RepeatedPtrField - *distributions_proto); + *distributions_proto) const; private: friend class EventMetricTest; @@ -118,7 +118,7 @@ class EventMetric : public BaseEventMetric { inline void SetDistributionValues( const Distribution &distribution, - drm_metrics::DistributionMetric *metric_proto) { + drm_metrics::DistributionMetric *metric_proto) const { metric_proto->set_mean(distribution.Mean()); metric_proto->set_operation_count(distribution.Count()); if (distribution.Count() > 1) { @@ -140,7 +140,7 @@ inline void EventMetric<0, util::Unused, 0, util::Unused, 0, util::Unused, 0, util::Unused>:: ToProto( ::google::protobuf::RepeatedPtrField - *distributions_proto) { + *distributions_proto) const { const std::map* distributions = GetDistributions(); for (std::map::const_iterator it = @@ -154,7 +154,7 @@ template inline void EventMetric::ToProto( ::google::protobuf::RepeatedPtrField - *distributions_proto) { + *distributions_proto) const { const std::map* distributions = GetDistributions(); for (std::map::const_iterator it = diff --git a/libwvdrmengine/cdm/metrics/include/metrics_collections.h b/libwvdrmengine/cdm/metrics/include/metrics_collections.h index 20ac573f..141e9695 100644 --- a/libwvdrmengine/cdm/metrics/include/metrics_collections.h +++ b/libwvdrmengine/cdm/metrics/include/metrics_collections.h @@ -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" @@ -81,6 +82,8 @@ const int kKeyStatusTypeFieldNumber = ::drm_metrics::Attributes::kKeyStatusTypeFieldNumber; const int kEventTypeFieldNumber = ::drm_metrics::Attributes::kEventTypeFieldNumber; +const int kKeyRequestTypeFieldNumber = + ::drm_metrics::Attributes::kKeyRequestTypeFieldNumber; } // anonymous namespace @@ -110,7 +113,8 @@ typedef enum OEMCryptoInitializationMode { // This class contains metrics for Crypto Session and OEM Crypto. class CryptoMetrics { public: - void Serialize(drm_metrics::WvCdmMetrics::CryptoMetrics *crypto_metrics); + void Serialize(drm_metrics::WvCdmMetrics::CryptoMetrics *crypto_metrics) + const; /* CRYPTO SESSION */ // TODO(blueeyes): Convert this to crypto_session_default_security_level_. @@ -144,6 +148,8 @@ class CryptoMetrics { ValueMetric crypto_session_system_id_; EventMetric crypto_session_update_usage_information_; + EventMetric + crypto_session_update_usage_entry_; ValueMetric crypto_session_usage_information_support_; /* OEMCRYPTO */ ValueMetric oemcrypto_api_version_; @@ -225,6 +231,8 @@ class CryptoMetrics { ValueMetric oemcrypto_usage_table_support_; CounterMetric oemcrypto_update_usage_table_; + CounterMetric + oemcrypto_update_usage_entry_; }; // This class contains session-scoped metrics. All properties and @@ -262,13 +270,17 @@ class SessionMetrics { CounterMetric cdm_session_restore_usage_session_; + EventMetric + cdm_session_license_request_latency_ms_; + // Serialize the session metrics to the provided |metric_group|. // |metric_group| is owned by the caller and must not be null. - void Serialize(drm_metrics::WvCdmMetrics::SessionMetrics *session_metrics); + void Serialize(drm_metrics::WvCdmMetrics::SessionMetrics *session_metrics) + const; private: void SerializeSessionMetrics( - drm_metrics::WvCdmMetrics::SessionMetrics *session_metrics); + drm_metrics::WvCdmMetrics::SessionMetrics *session_metrics) const; CdmSessionId session_id_; bool completed_; CryptoMetrics crypto_metrics_; @@ -294,13 +306,14 @@ class OemCryptoDynamicAdapterMetrics { // Serialize the session metrics to the provided |metric_group|. // |metric_group| is owned by the caller and must not be null. - void Serialize(drm_metrics::WvCdmMetrics::EngineMetrics *engine_metrics); + void Serialize(drm_metrics::WvCdmMetrics::EngineMetrics *engine_metrics) + const; // Clears the existing metric values. void Clear(); private: - Lock adapter_lock_; + mutable Lock adapter_lock_; ValueMetric oemcrypto_initialization_mode_; ValueMetric oemcrypto_l1_api_version_; ValueMetric oemcrypto_l1_min_api_version_; @@ -346,7 +359,7 @@ class EngineMetrics { // void Serialize(drm_metrics::MetricsGroup* metric_group, bool // completed_only, // bool clear_serialized_sessions); - void Serialize(drm_metrics::WvCdmMetrics *engine_metrics); + void Serialize(drm_metrics::WvCdmMetrics *engine_metrics) const; void SetAppPackageName(const std::string &app_package_name); @@ -371,7 +384,6 @@ class EngineMetrics { cdm_engine_get_usage_info_; EventMetric cdm_engine_handle_provisioning_response_; - ValueMetric cdm_engine_life_span_; // Milliseconds CounterMetric cdm_engine_open_key_set_session_; CounterMetric @@ -393,13 +405,15 @@ class EngineMetrics { cdm_engine_unprovision_; private: - Lock session_metrics_lock_; + mutable Lock session_metrics_lock_; std::vector 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_; void SerializeEngineMetrics( - drm_metrics::WvCdmMetrics::EngineMetrics *engine_metrics); + drm_metrics::WvCdmMetrics::EngineMetrics *engine_metrics) const; }; } // namespace metrics diff --git a/libwvdrmengine/cdm/metrics/include/timer_metric.h b/libwvdrmengine/cdm/metrics/include/timer_metric.h index ce00c2fd..84a8d20a 100644 --- a/libwvdrmengine/cdm/metrics/include/timer_metric.h +++ b/libwvdrmengine/cdm/metrics/include/timer_metric.h @@ -9,13 +9,23 @@ namespace metrics { class TimerMetric { public: + // Starts the clock running. If the clock was previously set, this resets it. + // IsStarted will return true after this call. void Start(); + // Returns whether or not the timer has started. + bool IsStarted() const { return is_started_; }; + // Stops the clock and clears the current value. IsStarted will return false + // after this call. + void Clear(); + // Returns the current clock value as milliseconds (AsMs) or microseconds + // (AsUs). double AsMs() const; double AsUs() const; private: double sec_; double usec_; + bool is_started_; }; diff --git a/libwvdrmengine/cdm/metrics/include/value_metric.h b/libwvdrmengine/cdm/metrics/include/value_metric.h index 5982439a..360e1dbc 100644 --- a/libwvdrmengine/cdm/metrics/include/value_metric.h +++ b/libwvdrmengine/cdm/metrics/include/value_metric.h @@ -72,7 +72,7 @@ class ValueMetric { // Returns a new ValueMetric proto containing the metric value or the // error code. If neither the error or value are set, it returns nullptr. - drm_metrics::ValueMetric *ToProto() { + drm_metrics::ValueMetric *ToProto() const { if (has_error_) { drm_metrics::ValueMetric *value_proto = new drm_metrics::ValueMetric; value_proto->set_error_code(error_code_); diff --git a/libwvdrmengine/cdm/metrics/src/attribute_handler.cpp b/libwvdrmengine/cdm/metrics/src/attribute_handler.cpp index 6f295881..8de1a641 100644 --- a/libwvdrmengine/cdm/metrics/src/attribute_handler.cpp +++ b/libwvdrmengine/cdm/metrics/src/attribute_handler.cpp @@ -72,6 +72,14 @@ void SetAttributeFieldset_signing_algorithm(signing_algorithm); } +template <> +void SetAttributeField( + const CdmKeyRequestType &key_request_type, + drm_metrics::Attributes *attributes) { + attributes->set_key_request_type(key_request_type); +} + template <> void SetAttributeField<0, util::Unused>(const util::Unused &, drm_metrics::Attributes *) { diff --git a/libwvdrmengine/cdm/metrics/src/metrics.proto b/libwvdrmengine/cdm/metrics/src/metrics.proto index 60c65d1d..680d35e6 100644 --- a/libwvdrmengine/cdm/metrics/src/metrics.proto +++ b/libwvdrmengine/cdm/metrics/src/metrics.proto @@ -44,6 +44,8 @@ message Attributes { optional uint32 key_status_type = 14; // Defined at ::android::hardware::drm::V1_0::EventType; optional uint32 event_type = 15; + // Contains the CdmKeyRequestType defined in wv_cdm_types.h. + optional uint32 key_request_type = 16; } // The Counter message is used to store a count value with an associated @@ -88,7 +90,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 +107,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,11 +150,12 @@ 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 // nested CryptoMetrics that were captured in the context of the session. - // next id: 7 + // next id: 8 message SessionMetrics { optional ValueMetric session_id = 1; optional CryptoMetrics crypto_metrics = 2; @@ -159,6 +163,7 @@ message WvCdmMetrics { repeated DistributionMetric cdm_session_renew_key_time_us = 4; repeated CounterMetric cdm_session_restore_offline_session = 5; repeated CounterMetric cdm_session_restore_usage_session = 6; + repeated DistributionMetric cdm_session_license_request_latency_ms = 7; } // These are metrics recorded at the Engine level. This includes CryptoSession @@ -184,7 +189,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; diff --git a/libwvdrmengine/cdm/metrics/src/metrics_collections.cpp b/libwvdrmengine/cdm/metrics/src/metrics_collections.cpp index 7b3e4d93..b3b28448 100644 --- a/libwvdrmengine/cdm/metrics/src/metrics_collections.cpp +++ b/libwvdrmengine/cdm/metrics/src/metrics_collections.cpp @@ -29,7 +29,8 @@ struct CompareSessionIds { namespace wvcdm { namespace metrics { -void CryptoMetrics::Serialize(WvCdmMetrics::CryptoMetrics *crypto_metrics) { +void CryptoMetrics::Serialize(WvCdmMetrics::CryptoMetrics *crypto_metrics) + const { /* CRYPTO SESSION */ crypto_metrics->set_allocated_crypto_session_security_level( crypto_session_security_level_.ToProto()); @@ -62,6 +63,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,22 +148,20 @@ 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() - : cdm_session_life_span_(), - cdm_session_renew_key_(), - cdm_session_restore_offline_session_(), - cdm_session_restore_usage_session_(), - completed_(false) {} +SessionMetrics::SessionMetrics() {} -void SessionMetrics::Serialize(WvCdmMetrics::SessionMetrics *session_metrics) { +void SessionMetrics::Serialize(WvCdmMetrics::SessionMetrics *session_metrics) + const { SerializeSessionMetrics(session_metrics); crypto_metrics_.Serialize(session_metrics->mutable_crypto_metrics()); } void SessionMetrics::SerializeSessionMetrics( - WvCdmMetrics::SessionMetrics *session_metrics) { + WvCdmMetrics::SessionMetrics *session_metrics) const { // If the session id was set, add it to the metrics. It's possible that // it's not set in some circumstances such as when provisioning is needed. if (!session_id_.empty()) { @@ -174,6 +175,8 @@ void SessionMetrics::SerializeSessionMetrics( session_metrics->mutable_cdm_session_restore_offline_session()); cdm_session_restore_usage_session_.ToProto( session_metrics->mutable_cdm_session_restore_usage_session()); + cdm_session_license_request_latency_ms_.ToProto( + session_metrics->mutable_cdm_session_license_request_latency_ms()); } OemCryptoDynamicAdapterMetrics::OemCryptoDynamicAdapterMetrics() @@ -198,7 +201,7 @@ void OemCryptoDynamicAdapterMetrics::SetL1MinApiVersion(uint32_t version) { } void OemCryptoDynamicAdapterMetrics::Serialize( - WvCdmMetrics::EngineMetrics *engine_metrics) { + WvCdmMetrics::EngineMetrics *engine_metrics) const { AutoLock lock(adapter_lock_); engine_metrics->set_allocated_oemcrypto_initialization_mode( @@ -227,30 +230,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_); @@ -281,7 +263,7 @@ void EngineMetrics::RemoveSession(wvcdm::CdmSessionId session_id) { session_metrics_list_.end()); } -void EngineMetrics::Serialize(WvCdmMetrics *wv_metrics) { +void EngineMetrics::Serialize(WvCdmMetrics *wv_metrics) const { AutoLock lock(session_metrics_lock_); WvCdmMetrics::EngineMetrics *engine_metrics = wv_metrics->mutable_engine_metrics(); @@ -308,7 +290,10 @@ void EngineMetrics::SetAppPackageName(const std::string &app_package_name) { } void EngineMetrics::SerializeEngineMetrics( - WvCdmMetrics::EngineMetrics *engine_metrics) { + WvCdmMetrics::EngineMetrics *engine_metrics) const { + // 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 +317,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( diff --git a/libwvdrmengine/cdm/metrics/src/timer_metric.cpp b/libwvdrmengine/cdm/metrics/src/timer_metric.cpp index f1990949..03575567 100644 --- a/libwvdrmengine/cdm/metrics/src/timer_metric.cpp +++ b/libwvdrmengine/cdm/metrics/src/timer_metric.cpp @@ -11,6 +11,13 @@ void TimerMetric::Start() { gettimeofday(&tv, NULL); sec_ = tv.tv_sec; usec_ = tv.tv_usec; + is_started_ = true; +} + +void TimerMetric::Clear() { + is_started_ = false; + sec_ = 0; + usec_ = 0; } double TimerMetric::AsMs() const { diff --git a/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp b/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp index 6fa9c60a..0d4332c1 100644 --- a/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp +++ b/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp @@ -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,