diff --git a/libwvdrmengine/Android.mk b/libwvdrmengine/Android.mk index f689ec44..5c85356a 100644 --- a/libwvdrmengine/Android.mk +++ b/libwvdrmengine/Android.mk @@ -79,7 +79,9 @@ include $(CLEAR_VARS) LOCAL_MODULE := libcdm_protos LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_SRC_FILES := $(call all-proto-files-under, cdm/core/src) +CORE_PROTO_SRC_FILES := $(call all-proto-files-under, cdm/core/src) +METRICS_PROTO_SRC_FILES := $(call all-proto-files-under, cdm/metrics/src) +LOCAL_SRC_FILES := $(CORE_PROTO_SRC_FILES) $(METRICS_PROTO_SRC_FILES) generated_sources_dir := $(call local-generated-sources-dir) @@ -91,7 +93,8 @@ generated_sources_dir := $(call local-generated-sources-dir) # with this path. LOCAL_EXPORT_C_INCLUDE_DIRS := \ $(generated_sources_dir)/proto \ - $(generated_sources_dir)/proto/$(LOCAL_PATH)/cdm/core/src + $(generated_sources_dir)/proto/$(LOCAL_PATH)/cdm/core/src \ + $(generated_sources_dir)/proto/$(LOCAL_PATH)/cdm/metrics/src include $(BUILD_STATIC_LIBRARY) diff --git a/libwvdrmengine/cdm/metrics/include/metrics_collections.h b/libwvdrmengine/cdm/metrics/include/metrics_collections.h index 8b2ea675..2b5e4fdd 100644 --- a/libwvdrmengine/cdm/metrics/include/metrics_collections.h +++ b/libwvdrmengine/cdm/metrics/include/metrics_collections.h @@ -6,10 +6,12 @@ #ifndef WVCDM_METRICS_METRICS_GROUP_H_ #define WVCDM_METRICS_METRICS_GROUP_H_ +#include #include #include #include "event_metric.h" +#include "metrics.pb.h" #include "OEMCryptoCENC.h" #include "wv_cdm_types.h" @@ -80,6 +82,8 @@ class CryptoMetrics { public: CryptoMetrics(); + void Serialize(drm_metrics::MetricsGroup* metrics); + /* CRYPTO SESSION */ EventMetric crypto_session_delete_all_usage_reports_; EventMetric crypto_session_delete_multiple_usage_information_; @@ -173,6 +177,10 @@ class SessionMetrics { // This instance retains ownership of the object. CryptoMetrics* GetCryptoMetrics() { return &crypto_metrics_; } + // 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::MetricsGroup* metric_group); + // Metrics collected at the session level. EventMetric<> cdm_session_life_span_; EventMetric cdm_session_renew_key_; @@ -180,6 +188,7 @@ class SessionMetrics { EventMetric cdm_session_restore_usage_session_; private: + void SerializeSessionMetrics(drm_metrics::MetricsGroup* metric_group); CdmSessionId session_id_; bool completed_; CryptoMetrics crypto_metrics_; @@ -207,11 +216,16 @@ class EngineMetrics { // until this object is deleted. CryptoMetrics* GetCryptoMetrics() { return &crypto_metrics_; } - // Serialize all engine and completed session metrics to json format and - // send them to the output string. |out| must NOT be null. |completed_only| - // indicates that this call should only publish SessionMetrics instances - // that are marked as completed. - void JsonSerialize(std::string* out, bool completed_only); + // Serialize engine and session metrics into a serialized MetricsGroup + // instance and output that instance to the provided |metric_group|. + // |metric_group| is owned by the caller and must NOT be null. + // |completed_only| indicates that this call should only publish + // SessionMetrics instances that are marked as completed. + // |clear_sessions| indicates that this call should clear sessions metrics + // for those sessions that were serialized. This allows atomic + // serialization and closing of session-level metrics. + void Serialize(drm_metrics::MetricsGroup* metric_group, bool completed_only, + bool clear_serialized_sessions); // Metrics recorded at the engine level. EventMetric cdm_engine_add_key_; @@ -236,6 +250,8 @@ class EngineMetrics { Lock session_metrics_lock_; std::vector session_metrics_list_; CryptoMetrics crypto_metrics_; + + void SerializeEngineMetrics(drm_metrics::MetricsGroup* out); }; } // namespace metrics diff --git a/libwvdrmengine/cdm/metrics/src/event_metric.cpp b/libwvdrmengine/cdm/metrics/src/event_metric.cpp index 0671418d..06e2ac51 100644 --- a/libwvdrmengine/cdm/metrics/src/event_metric.cpp +++ b/libwvdrmengine/cdm/metrics/src/event_metric.cpp @@ -43,15 +43,18 @@ void BaseEventMetric::Serialize(MetricSerializer* serializer) { serializer->SetDouble( metric_name_ + "/mean" + it->first, it->second->Mean()); - serializer->SetDouble( - metric_name_ + "/variance" + it->first, - it->second->Variance()); - serializer->SetDouble( - metric_name_ + "/min" + it->first, - it->second->Min()); - serializer->SetDouble( - metric_name_ + "/max" + it->first, - it->second->Max()); + // Only publish additional information if there was more than one sample. + if (it->second->Count() > 1) { + serializer->SetDouble( + metric_name_ + "/variance" + it->first, + it->second->Variance()); + serializer->SetDouble( + metric_name_ + "/min" + it->first, + it->second->Min()); + serializer->SetDouble( + metric_name_ + "/max" + it->first, + it->second->Max()); + } } } diff --git a/libwvdrmengine/cdm/metrics/src/metrics.proto b/libwvdrmengine/cdm/metrics/src/metrics.proto new file mode 100644 index 00000000..4df1fe53 --- /dev/null +++ b/libwvdrmengine/cdm/metrics/src/metrics.proto @@ -0,0 +1,36 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// This file contains a proto definition for serialization of metrics data. +// +syntax = "proto2"; + +package drm_metrics; + +// need this if we are using libprotobuf-cpp-2.3.0-lite +option optimize_for = LITE_RUNTIME; + +// The MetricsGroup is a collection of metric name/value pair instances +// that can be serialized and provided to a caller. +message MetricsGroup { + message Metric { + message MetricValue { + // Only one of the following values must be set. Note that the oneof + // keyword is not supported in the protobuf version checked into the CDM. + optional int64 int_value = 1; + optional double double_value = 2; + optional string string_value = 3; + } + + // The name of the metric. Must be valid UTF-8. Required. + optional string name = 1; + + // The value of the metric. Required. + optional MetricValue value = 2; + } + + // The list of name/value pairs of metrics. + repeated Metric metric = 1; + + // Allow multiple sub groups of metrics. + repeated MetricsGroup metric_sub_group = 2; +} diff --git a/libwvdrmengine/cdm/metrics/src/metrics_collections.cpp b/libwvdrmengine/cdm/metrics/src/metrics_collections.cpp index f0d06582..936cae8b 100644 --- a/libwvdrmengine/cdm/metrics/src/metrics_collections.cpp +++ b/libwvdrmengine/cdm/metrics/src/metrics_collections.cpp @@ -4,6 +4,11 @@ #include +#include "metrics.pb.h" + +using drm_metrics::MetricsGroup; +using wvcdm::metrics::MetricSerializer; + namespace { // Helper struct for comparing session ids. struct CompareSessionIds { @@ -16,6 +21,41 @@ struct CompareSessionIds { } }; +// Local class used to serialize to the MetricsGroup proto message. +class ProtoMetricSerializer : public wvcdm::metrics::MetricSerializer { + public: + ProtoMetricSerializer(MetricsGroup* metric_group) + : metric_group_(metric_group) {} + + virtual void SetString(const std::string& metric_id, + const std::string& value) { + MetricsGroup::Metric* metric = metric_group_->add_metric(); + metric->set_name(metric_id); + metric->mutable_value()->set_string_value(value); + } + + virtual void SetInt32(const std::string& metric_id, int32_t value) { + MetricsGroup::Metric* metric = metric_group_->add_metric(); + metric->set_name(metric_id); + metric->mutable_value()->set_int_value(value); + } + + virtual void SetInt64(const std::string& metric_id, int64_t value) { + MetricsGroup::Metric* metric = metric_group_->add_metric(); + metric->set_name(metric_id); + metric->mutable_value()->set_int_value(value); + } + + virtual void SetDouble(const std::string& metric_id, double value) { + MetricsGroup::Metric* metric = metric_group_->add_metric(); + metric->set_name(metric_id); + metric->mutable_value()->set_double_value(value); + } + + private: + MetricsGroup* metric_group_; +}; + } // anonymous namespace namespace wvcdm { @@ -241,6 +281,76 @@ CryptoMetrics::CryptoMetrics() : "version", "min_version") {} +void CryptoMetrics::Serialize(MetricsGroup* metrics) { + ProtoMetricSerializer serializer(metrics); + /* CRYPTO SESSION */ + crypto_session_delete_all_usage_reports_.Serialize(&serializer); + crypto_session_delete_multiple_usage_information_.Serialize(&serializer); + crypto_session_generic_decrypt_.Serialize(&serializer); + crypto_session_generic_encrypt_.Serialize(&serializer); + crypto_session_generic_sign_.Serialize(&serializer); + crypto_session_generic_verify_.Serialize(&serializer); + crypto_session_get_device_unique_id_.Serialize(&serializer); + crypto_session_get_security_level_.Serialize(&serializer); + crypto_session_get_system_id_.Serialize(&serializer); + crypto_session_get_token_.Serialize(&serializer); + crypto_session_life_span_.Serialize(&serializer); + crypto_session_load_certificate_private_key_.Serialize(&serializer); + crypto_session_open_.Serialize(&serializer); + crypto_session_update_usage_information_.Serialize(&serializer); + crypto_session_usage_information_support_.Serialize(&serializer); + + /* OEMCRYPTO */ + oemcrypto_api_version_.Serialize(&serializer); + oemcrypto_close_session_.Serialize(&serializer); + oemcrypto_copy_buffer_.Serialize(&serializer); + oemcrypto_deactivate_usage_entry_.Serialize(&serializer); + oemcrypto_decrypt_cenc_.Serialize(&serializer); + oemcrypto_delete_usage_entry_.Serialize(&serializer); + oemcrypto_delete_usage_table_.Serialize(&serializer); + oemcrypto_derive_keys_from_session_key_.Serialize(&serializer); + oemcrypto_force_delete_usage_entry_.Serialize(&serializer); + oemcrypto_generate_derived_keys_.Serialize(&serializer); + oemcrypto_generate_nonce_.Serialize(&serializer); + oemcrypto_generate_rsa_signature_.Serialize(&serializer); + oemcrypto_generate_signature_.Serialize(&serializer); + oemcrypto_generic_decrypt_.Serialize(&serializer); + oemcrypto_generic_encrypt_.Serialize(&serializer); + oemcrypto_generic_sign_.Serialize(&serializer); + oemcrypto_generic_verify_.Serialize(&serializer); + oemcrypto_get_device_id_.Serialize(&serializer); + oemcrypto_get_hdcp_capability_.Serialize(&serializer); + oemcrypto_get_key_data_.Serialize(&serializer); + oemcrypto_get_max_number_of_sessions_.Serialize(&serializer); + oemcrypto_get_number_of_open_sessions_.Serialize(&serializer); + oemcrypto_get_oem_public_certificate_.Serialize(&serializer); + oemcrypto_get_provisioning_method_.Serialize(&serializer); + oemcrypto_get_random_.Serialize(&serializer); + oemcrypto_initialize_.Serialize(&serializer); + oemcrypto_install_keybox_.Serialize(&serializer); + oemcrypto_is_anti_rollback_hw_present_.Serialize(&serializer); + oemcrypto_is_keybox_valid_.Serialize(&serializer); + oemcrypto_load_device_rsa_key_.Serialize(&serializer); + oemcrypto_load_keys_.Serialize(&serializer); + oemcrypto_load_test_keybox_.Serialize(&serializer); + oemcrypto_load_test_rsa_key_.Serialize(&serializer); + oemcrypto_open_session_.Serialize(&serializer); + oemcrypto_refresh_keys_.Serialize(&serializer); + oemcrypto_report_usage_.Serialize(&serializer); + oemcrypto_rewrap_device_rsa_key_.Serialize(&serializer); + oemcrypto_rewrap_device_rsa_key_30_.Serialize(&serializer); + oemcrypto_security_level_.Serialize(&serializer); + oemcrypto_security_patch_level_.Serialize(&serializer); + oemcrypto_select_key_.Serialize(&serializer); + oemcrypto_supports_usage_table_.Serialize(&serializer); + oemcrypto_update_usage_table_.Serialize(&serializer); + oemcrypto_wrap_keybox_.Serialize(&serializer); + + /* Internal OEMCrypto Metrics */ + oemcrypto_initialization_mode_.Serialize(&serializer); + oemcrypto_l1_api_version_.Serialize(&serializer); +} + SessionMetrics::SessionMetrics() : cdm_session_life_span_( "/drm/widevine/cdm_session/life_span/time"), @@ -256,6 +366,21 @@ SessionMetrics::SessionMetrics() : completed_(false) { } +void SessionMetrics::Serialize(MetricsGroup* metric_group) { + SerializeSessionMetrics(metric_group); + crypto_metrics_.Serialize(metric_group); +} + +void SessionMetrics::SerializeSessionMetrics(MetricsGroup* metric_group) { + ProtoMetricSerializer serializer(metric_group); + // Add the session id as a single-valued metric. + serializer.SetString("/drm/widevine/cdm_session/session_id", session_id_); + cdm_session_life_span_.Serialize(&serializer); + cdm_session_renew_key_.Serialize(&serializer); + cdm_session_restore_offline_session_.Serialize(&serializer); + cdm_session_restore_usage_session_.Serialize(&serializer); +} + EngineMetrics::EngineMetrics() : cdm_engine_add_key_( "/drm/widevine/cdm_engine/add_key/time", @@ -336,8 +461,51 @@ void EngineMetrics::RemoveSession(wvcdm::CdmSessionId session_id) { session_metrics_list_.end()); } -void JsonSerialize(std::string* out, bool completed_only) { - // TODO(blueeyes): Implement this. +void EngineMetrics::Serialize(drm_metrics::MetricsGroup* metric_group, + bool completed_only, + bool clear_serialized_sessions) { + AutoLock lock(session_metrics_lock_); + + SerializeEngineMetrics(metric_group); + std::vector::iterator i; + for (i = session_metrics_list_.begin(); i != session_metrics_list_.end(); + /* no increment */) { + bool serialized = false; + if (!completed_only || (*i)->IsCompleted()) { + (*i)->Serialize(metric_group->add_metric_sub_group()); + serialized = true; + } + + // Clear the serialized session metrics if requested. + if (serialized && clear_serialized_sessions) { + session_metrics_list_.erase(i); + } else { + i++; + } + } +} + +void EngineMetrics::SerializeEngineMetrics(MetricsGroup* metric_group) { + ProtoMetricSerializer serializer(metric_group); + cdm_engine_add_key_.Serialize(&serializer); + cdm_engine_close_session_.Serialize(&serializer); + cdm_engine_decrypt_.Serialize(&serializer); + cdm_engine_find_session_for_key_.Serialize(&serializer); + cdm_engine_generate_key_request_.Serialize(&serializer); + cdm_engine_get_provisioning_request_.Serialize(&serializer); + cdm_engine_get_usage_info_.Serialize(&serializer); + cdm_engine_handle_provisioning_response_.Serialize(&serializer); + cdm_engine_life_span_.Serialize(&serializer); + cdm_engine_open_key_set_session_.Serialize(&serializer); + cdm_engine_open_session_.Serialize(&serializer); + cdm_engine_query_key_status_.Serialize(&serializer); + cdm_engine_release_all_usage_info_.Serialize(&serializer); + cdm_engine_release_usage_info_.Serialize(&serializer); + cdm_engine_remove_keys_.Serialize(&serializer); + cdm_engine_restore_key_.Serialize(&serializer); + cdm_engine_unprovision_.Serialize(&serializer); + + crypto_metrics_.Serialize(metric_group); } } // metrics diff --git a/libwvdrmengine/cdm/metrics/test/event_metric_test.cpp b/libwvdrmengine/cdm/metrics/test/event_metric_test.cpp index dc0c410c..ac376308 100644 --- a/libwvdrmengine/cdm/metrics/test/event_metric_test.cpp +++ b/libwvdrmengine/cdm/metrics/test/event_metric_test.cpp @@ -61,7 +61,7 @@ TEST_F(EventMetricTest, NoFieldsSuccessNullCallback) { TEST_F(EventMetricTest, NoFieldsSuccessWithCallback) { wvcdm::metrics::EventMetric<> metric("no/fields/metric"); EXPECT_CALL(*mock_serializer_, - SetInt64("no/fields/metric/count", 1.0)); + SetInt64("no/fields/metric/count", 2)); EXPECT_CALL(*mock_serializer_, SetDouble("no/fields/metric/mean", 10.0)); EXPECT_CALL(*mock_serializer_, @@ -71,6 +71,23 @@ TEST_F(EventMetricTest, NoFieldsSuccessWithCallback) { EXPECT_CALL(*mock_serializer_, SetDouble("no/fields/metric/max", 10.0)); + metric.Record(10); + metric.Record(10); + metric.Serialize(mock_serializer_.get()); + + std::map value_map = GetValueMap(metric); + ASSERT_EQ(1u, GetValueMap(metric).size()); + EXPECT_EQ(2, value_map.begin()->second->Count()); + EXPECT_EQ("", value_map.begin()->first); +} + +TEST_F(EventMetricTest, NoFieldsSuccessSingleRecordWithCallback) { + wvcdm::metrics::EventMetric<> metric("no/fields/metric"); + EXPECT_CALL(*mock_serializer_, + SetInt64("no/fields/metric/count", 1.0)); + EXPECT_CALL(*mock_serializer_, + SetDouble("no/fields/metric/mean", 10.0)); + metric.Record(10); metric.Serialize(mock_serializer_.get()); diff --git a/libwvdrmengine/cdm/metrics/test/metrics_collections_test.cpp b/libwvdrmengine/cdm/metrics/test/metrics_collections_test.cpp new file mode 100644 index 00000000..7a724e19 --- /dev/null +++ b/libwvdrmengine/cdm/metrics/test/metrics_collections_test.cpp @@ -0,0 +1,444 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Unit tests for the metrics collections, +// EngineMetrics, SessionMetrics and CrytpoMetrics. + +#include "metrics_collections.h" + +#include + +#include "gmock/gmock.h" +#include "google/protobuf/text_format.h" +#include "gtest/gtest.h" +#include "log.h" +#include "metrics.pb.h" +#include "wv_cdm_types.h" + +using drm_metrics::MetricsGroup; +using google::protobuf::TextFormat; + +namespace wvcdm { +namespace metrics { + +// TODO(blueeyes): Improve this implementation by supporting full message +// API In CDM. That allows us to use MessageDifferencer. +class EngineMetricsTest : public ::testing::Test { +}; + +TEST_F(EngineMetricsTest, AllEngineMetrics) { + EngineMetrics engine_metrics; + + // Set some values in all of the engine metrics. + engine_metrics.cdm_engine_add_key_.Record(1.0, KEY_ADDED); + engine_metrics.cdm_engine_close_session_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_decrypt_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_find_session_for_key_.Record(1.0, false); + engine_metrics.cdm_engine_generate_key_request_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_get_provisioning_request_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_get_usage_info_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_handle_provisioning_response_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_life_span_.Record(1.0); + engine_metrics.cdm_engine_open_key_set_session_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_open_session_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_query_key_status_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_release_all_usage_info_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_release_usage_info_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_remove_keys_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_restore_key_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_unprovision_.Record(1.0, NO_ERROR, kSecurityLevelL1); + + drm_metrics::MetricsGroup actual_metrics; + engine_metrics.Serialize(&actual_metrics, true, false); + + // For each EventMetric, 2 metrics get serialized since only one sample was + // taken. So, the total number of serialized metrics are 2*17. + ASSERT_EQ(2 * 17, actual_metrics.metric_size()); + EXPECT_EQ(0, actual_metrics.metric_sub_group_size()); + + // Spot check some metrics. + EXPECT_EQ("/drm/widevine/cdm_engine/add_key/time/count{error:2}", + actual_metrics.metric(0).name()); + EXPECT_EQ("/drm/widevine/cdm_engine/close_session/time/mean{error:0}", + actual_metrics.metric(3).name()); + EXPECT_EQ("/drm/widevine/cdm_engine/decrypt/time/mean{error:0}", + actual_metrics.metric(5).name()); + EXPECT_EQ(1.0, actual_metrics.metric(5).value().double_value()); +} + +TEST_F(EngineMetricsTest, EngineAndCryptoMetrics) { + EngineMetrics engine_metrics; + + // Set some values in some of the engine metrics and some crypto metrics. + engine_metrics.cdm_engine_add_key_.Record(1.0, KEY_ADDED); + engine_metrics.cdm_engine_close_session_.Record(1.0, NO_ERROR); + CryptoMetrics* crypto_metrics = engine_metrics.GetCryptoMetrics(); + + crypto_metrics->crypto_session_generic_decrypt_ + .Record(2.0, NO_ERROR, Pow2Bucket(1025), kEncryptionAlgorithmAesCbc128); + crypto_metrics->crypto_session_get_device_unique_id_ + .Record(4.0, false); + + drm_metrics::MetricsGroup actual_metrics; + engine_metrics.Serialize(&actual_metrics, true, false); + + // For each EventMetric, 2 metrics get serialized since only one sample was + // taken. So, the total number of serialized metrics are 2*4 since we + // touched 4 metrics. + ASSERT_EQ(2 * 4, actual_metrics.metric_size()); + EXPECT_EQ(0, actual_metrics.metric_sub_group_size()); + + // Spot check some metrics. + EXPECT_EQ("/drm/widevine/cdm_engine/add_key/time/count{error:2}", + actual_metrics.metric(0).name()); + EXPECT_EQ( + "/drm/widevine/crypto_session/generic_decrypt/time/count" + "{error:0&length:1024&algorithm:1}", + actual_metrics.metric(4).name()); + EXPECT_EQ( + "/drm/widevine/crypto_session/get_device_unique_id/time/mean{success:0}", + actual_metrics.metric(7).name()); + EXPECT_EQ(4.0, actual_metrics.metric(7).value().double_value()); +} + +TEST_F(EngineMetricsTest, EmptyEngineMetrics) { + EngineMetrics engine_metrics; + + drm_metrics::MetricsGroup actual_metrics; + engine_metrics.Serialize(&actual_metrics, true, false); + + EXPECT_EQ(0, actual_metrics.metric_size()); + EXPECT_EQ(0, actual_metrics.metric_sub_group_size()); +} + +TEST_F(EngineMetricsTest, EngineMetricsWithCompletedSessions) { + EngineMetrics engine_metrics; + + // Set a values in an engine metric and in a crypto metric. + engine_metrics.cdm_engine_add_key_.Record(1.0, KEY_ADDED); + engine_metrics.GetCryptoMetrics() + ->crypto_session_load_certificate_private_key_.Record(2.0, true); + + // Create two sessions and record some metrics. + SessionMetrics* session_metrics_1 = engine_metrics.AddSession(); + session_metrics_1->SetSessionId("session_id_1"); + SessionMetrics* session_metrics_2 = engine_metrics.AddSession(); + session_metrics_2->SetSessionId("session_id_2"); + // Record a CryptoMetrics metric in the session. + session_metrics_2->GetCryptoMetrics()->crypto_session_generic_decrypt_ + .Record(2.0, NO_ERROR, Pow2Bucket(1025), kEncryptionAlgorithmAesCbc128); + session_metrics_2->SetSessionId("session_id_2"); + // Mark only session 2 as completed. + session_metrics_2->SetCompleted(); + + drm_metrics::MetricsGroup actual_metrics; + engine_metrics.Serialize(&actual_metrics, true, false); + + // Validate metric counts. + // For each EventMetric, 2 metrics get serialized since only one sample was + // taken. So, the total number of serialized metrics are 2*2 since we + // touched 2 metrics. + ASSERT_EQ(2 * 2, actual_metrics.metric_size()); + ASSERT_EQ(1, actual_metrics.metric_sub_group_size()); + ASSERT_EQ(3, actual_metrics.metric_sub_group(0).metric_size()); + + // Spot check some metrics. + EXPECT_EQ("/drm/widevine/cdm_engine/add_key/time/count{error:2}", + actual_metrics.metric(0).name()); + EXPECT_EQ("/drm/widevine/crypto_session/load_certificate_private_key" + "/time/count{success:1}", + actual_metrics.metric(2).name()); + EXPECT_EQ("/drm/widevine/cdm_session/session_id", + actual_metrics.metric_sub_group(0).metric(0).name()); + EXPECT_EQ( + "session_id_2", + actual_metrics.metric_sub_group(0).metric(0).value().string_value()); + EXPECT_EQ( + "/drm/widevine/crypto_session/generic_decrypt/time/count" + "{error:0&length:1024&algorithm:1}", + actual_metrics.metric_sub_group(0).metric(1).name()); +} + +TEST_F(EngineMetricsTest, EngineMetricsSerializeAllSessions) { + EngineMetrics engine_metrics; + + // Create two sessions and record some metrics. + SessionMetrics* session_metrics_1 = engine_metrics.AddSession(); + session_metrics_1->SetSessionId("session_id_1"); + SessionMetrics* session_metrics_2 = engine_metrics.AddSession(); + session_metrics_2->SetSessionId("session_id_2"); + // Mark only session 2 as completed. + session_metrics_2->SetCompleted(); + + drm_metrics::MetricsGroup actual_metrics; + engine_metrics.Serialize(&actual_metrics, false, false); + + // Validate metric counts. + // No Engine-level metrics were recorded. + ASSERT_EQ(0, actual_metrics.metric_size()); + // Two sub groups, 1 per session. + ASSERT_EQ(2, actual_metrics.metric_sub_group_size()); + ASSERT_EQ(1, actual_metrics.metric_sub_group(0).metric_size()); + + // Spot check some metrics. + EXPECT_EQ("/drm/widevine/cdm_session/session_id", + actual_metrics.metric_sub_group(0).metric(0).name()); + EXPECT_EQ( + "session_id_1", + actual_metrics.metric_sub_group(0).metric(0).value().string_value()); + EXPECT_EQ("/drm/widevine/cdm_session/session_id", + actual_metrics.metric_sub_group(1).metric(0).name()); + EXPECT_EQ( + "session_id_2", + actual_metrics.metric_sub_group(1).metric(0).value().string_value()); +} + +TEST_F(EngineMetricsTest, EngineMetricsRemoveSessions) { + EngineMetrics engine_metrics; + + // Create two sessions and record some metrics. + SessionMetrics* session_metrics_1 = engine_metrics.AddSession(); + session_metrics_1->SetSessionId("session_id_1"); + SessionMetrics* session_metrics_2 = engine_metrics.AddSession(); + session_metrics_2->SetSessionId("session_id_2"); + // Mark only session 2 as completed. + session_metrics_2->SetCompleted(); + + // Serialize all metrics, don't remove any. + drm_metrics::MetricsGroup actual_metrics; + engine_metrics.Serialize(&actual_metrics, false, false); + + // Validate metric counts. + // Two sub groups, 1 per session. + ASSERT_EQ(2, actual_metrics.metric_sub_group_size()); + + // Serialize completed metrics, remove them. + actual_metrics.Clear(); + engine_metrics.Serialize(&actual_metrics, true, true); + // Validate metric counts. + // Only one, completed session should exist. + ASSERT_EQ(1, actual_metrics.metric_sub_group_size()); + ASSERT_EQ(1, actual_metrics.metric_sub_group(0).metric_size()); + EXPECT_EQ( + "session_id_2", + actual_metrics.metric_sub_group(0).metric(0).value().string_value()); + + // Serialize all metrics, remove them. + actual_metrics.Clear(); + engine_metrics.Serialize(&actual_metrics, false, true); + // Validate metric counts. + // Only one, non-complete session should exist. + ASSERT_EQ(1, actual_metrics.metric_sub_group_size()); + ASSERT_EQ(1, actual_metrics.metric_sub_group(0).metric_size()); + EXPECT_EQ( + "session_id_1", + actual_metrics.metric_sub_group(0).metric(0).value().string_value()); + + // Serialize all metrics, don't remove any. + actual_metrics.Clear(); + engine_metrics.Serialize(&actual_metrics, false, false); + // Validate metric counts. + // There should be no more metric subgroups left. + ASSERT_EQ(0, actual_metrics.metric_sub_group_size()); +} + +class SessionMetricsTest : public ::testing::Test { +}; + +TEST_F(SessionMetricsTest, AllSessionMetrics) { + SessionMetrics session_metrics; + session_metrics.SetSessionId("session_id 1"); + + session_metrics.cdm_session_life_span_.Record(1.0); + session_metrics.cdm_session_renew_key_.Record(1.0, NO_ERROR); + session_metrics.cdm_session_restore_offline_session_.Record(1.0, NO_ERROR); + session_metrics.cdm_session_restore_usage_session_.Record(1.0, NO_ERROR); + + // Record a CryptoMetrics metric in the session. + session_metrics.GetCryptoMetrics()->crypto_session_generic_decrypt_ + .Record(2.0, NO_ERROR, Pow2Bucket(1025), kEncryptionAlgorithmAesCbc128); + + MetricsGroup actual_metrics; + session_metrics.Serialize(&actual_metrics); + + ASSERT_EQ(11, actual_metrics.metric_size()); + EXPECT_EQ(0, actual_metrics.metric_sub_group_size()); + + // Spot check some metrics. + EXPECT_EQ("/drm/widevine/cdm_session/session_id", + actual_metrics.metric(0).name()); + EXPECT_EQ("/drm/widevine/cdm_session/life_span/time/count", + actual_metrics.metric(1).name()); + EXPECT_EQ("/drm/widevine/cdm_session/renew_key/time/mean{error:0}", + actual_metrics.metric(4).name()); + EXPECT_EQ(1.0, actual_metrics.metric(4).value().double_value()); + EXPECT_EQ("/drm/widevine/crypto_session/generic_decrypt/time/count" + "{error:0&length:1024&algorithm:1}", + actual_metrics.metric(9).name()); +} + +TEST_F(SessionMetricsTest, EmptySessionMetrics) { + SessionMetrics session_metrics; + + MetricsGroup actual_metrics; + session_metrics.Serialize(&actual_metrics); + + // Session metric always has a session id. + ASSERT_EQ(1, actual_metrics.metric_size()); + EXPECT_EQ("/drm/widevine/cdm_session/session_id", + actual_metrics.metric(0).name()); + EXPECT_EQ("", actual_metrics.metric(0).value().string_value()); + EXPECT_EQ(0, actual_metrics.metric_sub_group_size()); +} + +class CryptoMetricsTest : public ::testing::Test { +}; + +TEST_F(CryptoMetricsTest, AllCryptoMetrics) { + CryptoMetrics crypto_metrics; + + // Crypto session metrics. + crypto_metrics.crypto_session_delete_all_usage_reports_ + .Record(1.0, NO_ERROR); + crypto_metrics.crypto_session_delete_multiple_usage_information_ + .Record(1.0, NO_ERROR); + crypto_metrics.crypto_session_generic_decrypt_ + .Record(2.0, NO_ERROR, Pow2Bucket(1025), kEncryptionAlgorithmAesCbc128); + crypto_metrics.crypto_session_generic_encrypt_ + .Record(2.0, NO_ERROR, Pow2Bucket(1025), kEncryptionAlgorithmAesCbc128); + crypto_metrics.crypto_session_generic_sign_ + .Record(2.0, NO_ERROR, Pow2Bucket(1025), kSigningAlgorithmHmacSha256); + crypto_metrics.crypto_session_generic_verify_ + .Record(2.0, NO_ERROR, Pow2Bucket(1025), kSigningAlgorithmHmacSha256); + crypto_metrics.crypto_session_get_device_unique_id_.Record(1.0, true); + crypto_metrics.crypto_session_get_security_level_ + .Record(1.0, kSecurityLevelL1); + crypto_metrics.crypto_session_get_system_id_.Record(1.0, true, 1234); + crypto_metrics.crypto_session_get_token_.Record(1.0, true); + crypto_metrics.crypto_session_life_span_.Record(1.0); + crypto_metrics.crypto_session_load_certificate_private_key_ + .Record(1.0, true); + crypto_metrics.crypto_session_open_.Record(1.0, NO_ERROR, kLevelDefault); + crypto_metrics.crypto_session_update_usage_information_ + .Record(1.0, NO_ERROR); + crypto_metrics.crypto_session_usage_information_support_.Record(1.0, true); + + // Oem crypto metrics. + crypto_metrics.oemcrypto_api_version_.Record(1.0, 123, kLevelDefault); + crypto_metrics.oemcrypto_close_session_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_copy_buffer_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, + kLevelDefault, Pow2Bucket(1025)); + crypto_metrics.oemcrypto_deactivate_usage_entry_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_decrypt_cenc_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, Pow2Bucket(1025)); + crypto_metrics.oemcrypto_delete_usage_entry_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_delete_usage_table_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_derive_keys_from_session_key_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_force_delete_usage_entry_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_generate_derived_keys_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_generate_nonce_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_generate_rsa_signature_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, Pow2Bucket(1025)); + crypto_metrics.oemcrypto_generate_signature_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, Pow2Bucket(1025)); + crypto_metrics.oemcrypto_generic_decrypt_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, Pow2Bucket(1025)); + crypto_metrics.oemcrypto_generic_encrypt_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, Pow2Bucket(1025)); + crypto_metrics.oemcrypto_generic_sign_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, Pow2Bucket(1025)); + crypto_metrics.oemcrypto_generic_verify_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, Pow2Bucket(1025)); + crypto_metrics.oemcrypto_get_device_id_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, kLevelDefault); + crypto_metrics.oemcrypto_get_hdcp_capability_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, kLevelDefault); + crypto_metrics.oemcrypto_get_key_data_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, + Pow2Bucket(1025), kLevelDefault); + crypto_metrics.oemcrypto_get_max_number_of_sessions_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, kLevelDefault); + crypto_metrics.oemcrypto_get_number_of_open_sessions_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, kLevelDefault); + crypto_metrics.oemcrypto_get_oem_public_certificate_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + + crypto_metrics.oemcrypto_get_provisioning_method_ + .Record(1.0, OEMCrypto_Keybox, kLevelDefault); + + crypto_metrics.oemcrypto_get_random_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, Pow2Bucket(1025)); + crypto_metrics.oemcrypto_initialize_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_install_keybox_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, kLevelDefault); + crypto_metrics.oemcrypto_is_anti_rollback_hw_present_ + .Record(1.0, true, kLevelDefault); + + crypto_metrics.oemcrypto_is_keybox_valid_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, kLevelDefault); + crypto_metrics.oemcrypto_load_device_rsa_key_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_load_keys_.Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_load_test_keybox_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_load_test_rsa_key_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_open_session_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, kLevelDefault); + crypto_metrics.oemcrypto_refresh_keys_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_report_usage_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_rewrap_device_rsa_key_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_rewrap_device_rsa_key_30_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_security_level_.Record(1.0, "test", kLevelDefault); + crypto_metrics.oemcrypto_security_patch_level_ + .Record(1.0, 123, kLevelDefault); + crypto_metrics.oemcrypto_select_key_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_supports_usage_table_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, kLevelDefault); + crypto_metrics.oemcrypto_update_usage_table_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_wrap_keybox_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + + // Internal OEMCrypto Metrics + crypto_metrics.oemcrypto_initialization_mode_ + .Record(1.0, OEMCrypto_INITIALIZED_FORCING_L3); + crypto_metrics.oemcrypto_l1_api_version_.Record(1.0, 12, 123); + + MetricsGroup actual_metrics; + crypto_metrics.Serialize(&actual_metrics); + + // 61 EventMetric instances, 2 values each. + ASSERT_EQ(122, actual_metrics.metric_size()); + + // Spot check some metrics. + EXPECT_EQ( + "/drm/widevine/crypto_session/delete_all_usage_reports/time/count" + "{error:0}", + actual_metrics.metric(0).name()); + EXPECT_EQ(1, actual_metrics.metric(0).value().int_value()); + EXPECT_EQ( + "/drm/widevine/oemcrypto/l1_api_version/mean{version:12&min_version:123}", + actual_metrics.metric(121).name()); + EXPECT_EQ(1.0, actual_metrics.metric(121).value().double_value()); + + // No subgroups should exist. + EXPECT_EQ(0, actual_metrics.metric_sub_group_size()); +} + +} // namespace metrics +} // namespace wvcdm