From fb679e38bbc91614faa917024adddeb51ff07d0a Mon Sep 17 00:00:00 2001 From: Adam Stone Date: Wed, 7 Feb 2018 10:25:48 -0800 Subject: [PATCH] Add logging support for FW metrics Adds proto serialization and logging support for media drm framework metrics. Bug: 64001676 Test: Ran CTS tests, unit tests and Google Play. Change-Id: Ie350ac93caa6b35610eb63e4acc860c8e8a5cf5b --- drm/libmediadrm/Android.bp | 72 ++++++- drm/libmediadrm/DrmHal.cpp | 56 ++++- drm/libmediadrm/DrmMetrics.cpp | 162 ++++++++++++--- drm/libmediadrm/PluginMetricsReporting.cpp | 2 +- drm/libmediadrm/protos/metrics.proto | 134 ++++++++++++ drm/libmediadrm/protos/plugin_metrics.proto | 50 ----- drm/libmediadrm/tests/Android.bp | 16 +- drm/libmediadrm/tests/DrmMetrics_test.cpp | 219 +++++++++++++++++++- media/libmedia/include/media/DrmHal.h | 3 +- media/libmedia/include/media/DrmMetrics.h | 25 ++- 10 files changed, 636 insertions(+), 103 deletions(-) create mode 100644 drm/libmediadrm/protos/metrics.proto delete mode 100644 drm/libmediadrm/protos/plugin_metrics.proto diff --git a/drm/libmediadrm/Android.bp b/drm/libmediadrm/Android.bp index ea239c594d..5ea4614da1 100644 --- a/drm/libmediadrm/Android.bp +++ b/drm/libmediadrm/Android.bp @@ -15,25 +15,20 @@ cc_library { "IDrm.cpp", "IDrmClient.cpp", "IMediaDrmService.cpp", - "PluginMetricsReporting.cpp", "SharedLibrary.cpp", "DrmHal.cpp", - "DrmMetrics.cpp", "CryptoHal.cpp", - "protos/plugin_metrics.proto", ], - proto: { - type: "lite", - }, - shared_libs: [ "libbinder", "libcutils", "libdl", "liblog", + "libmediadrmmetrics_lite", "libmediametrics", "libmediautils", + "libprotobuf-cpp-lite", "libstagefright_foundation", "libutils", "android.hardware.drm@1.0", @@ -48,3 +43,66 @@ cc_library { "-Wall", ], } + +// This is the version of the drm metrics configured for protobuf lite. +cc_library_shared { + name: "libmediadrmmetrics_lite", + srcs: [ + "DrmMetrics.cpp", + "PluginMetricsReporting.cpp", + "protos/metrics.proto", + ], + + proto: { + export_proto_headers: true, + type: "lite", + }, + shared_libs: [ + "android.hardware.drm@1.0", + "android.hardware.drm@1.1", + "libbase", + "libbinder", + "liblog", + "libmediametrics", + "libprotobuf-cpp-lite", + "libutils", + ], + cflags: [ + // Suppress unused parameter and no error options. These cause problems + // with the when using the map type in a proto definition. + "-Wno-unused-parameter", + "-Wno-error", + ], +} + +// This is the version of the drm metrics library configured for full protobuf. +cc_library_shared { + name: "libmediadrmmetrics_full", + srcs: [ + "DrmMetrics.cpp", + "PluginMetricsReporting.cpp", + "protos/metrics.proto", + ], + + proto: { + export_proto_headers: true, + type: "full", + }, + shared_libs: [ + "android.hardware.drm@1.0", + "android.hardware.drm@1.1", + "libbase", + "libbinder", + "liblog", + "libmediametrics", + "libprotobuf-cpp-full", + "libutils", + ], + cflags: [ + // Suppress unused parameter and no error options. These cause problems + // when using the map type in a proto definition. + "-Wno-unused-parameter", + "-Wno-error", + ], +} + diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp index 6b782f034a..48f4479759 100644 --- a/drm/libmediadrm/DrmHal.cpp +++ b/drm/libmediadrm/DrmHal.cpp @@ -16,6 +16,8 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "DrmHal" +#include + #include #include @@ -98,6 +100,15 @@ static hidl_string toHidlString(const String8& string) { return hidl_string(string.string()); } +std::string ToHexString(const std::string& str) { + std::ostringstream out; + out << std::hex << std::setfill('0'); + for (size_t i = 0; i < str.size(); i++) { + out << std::setw(2) << (int)(str[i]); + } + return out.str(); +} + static DrmPlugin::SecurityLevel toSecurityLevel(SecurityLevel level) { switch(level) { case SecurityLevel::SW_SECURE_CRYPTO: @@ -309,6 +320,7 @@ Vector> DrmHal::makeDrmFactories() { sp DrmHal::makeDrmPlugin(const sp& factory, const uint8_t uuid[16], const String8& appPackageName) { + mMetrics.SetAppPackageName(appPackageName); sp plugin; Return hResult = factory->createPlugin(uuid, appPackageName.string(), @@ -504,7 +516,8 @@ status_t DrmHal::destroyPlugin() { INIT_CHECK(); closeOpenSessions(); - reportMetrics(); + reportPluginMetrics(); + reportFrameworkMetrics(); setListener(NULL); mInitCheck = NO_INIT; @@ -620,7 +633,7 @@ status_t DrmHal::closeSession(Vector const &sessionId) { status_t response = toStatusT(status); mMetrics.SetSessionEnd(sessionId); mMetrics.mCloseSessionCounter.Increment(response); - reportMetrics(); + reportPluginMetrics(); return response; } mMetrics.mCloseSessionCounter.Increment(DEAD_OBJECT); @@ -634,7 +647,7 @@ status_t DrmHal::getKeyRequest(Vector const &sessionId, String8 &defaultUrl, DrmPlugin::KeyRequestType *keyRequestType) { Mutex::Autolock autoLock(mLock); INIT_CHECK(); - EventTimer keyRequestTimer(&mMetrics.mGetKeyRequestTiming); + EventTimer keyRequestTimer(&mMetrics.mGetKeyRequestTimeUs); DrmSessionManager::Instance()->useSession(sessionId); @@ -729,7 +742,7 @@ status_t DrmHal::getKeyRequest(Vector const &sessionId, status_t DrmHal::provideKeyResponse(Vector const &sessionId, Vector const &response, Vector &keySetId) { Mutex::Autolock autoLock(mLock); - EventTimer keyResponseTimer(&mMetrics.mProvideKeyResponseTiming); + EventTimer keyResponseTimer(&mMetrics.mProvideKeyResponseTimeUs); INIT_CHECK(); @@ -1277,8 +1290,41 @@ void DrmHal::writeByteArray(Parcel &obj, hidl_vec const &vec) } } +void DrmHal::reportFrameworkMetrics() const +{ + MediaAnalyticsItem item("mediadrm"); + item.generateSessionID(); + item.setPkgName(mMetrics.GetAppPackageName().c_str()); + String8 vendor; + String8 description; + status_t result = getPropertyStringInternal(String8("vendor"), vendor); + if (result != OK) { + ALOGE("Failed to get vendor from drm plugin. %d", result); + } else { + item.setCString("vendor", vendor.c_str()); + } + result = getPropertyStringInternal(String8("description"), description); + if (result != OK) { + ALOGE("Failed to get description from drm plugin. %d", result); + } else { + item.setCString("description", description.c_str()); + } + + std::string serializedMetrics; + result = mMetrics.GetSerializedMetrics(&serializedMetrics); + if (result != OK) { + ALOGE("Failed to serialize Framework metrics: %d", result); + } + serializedMetrics = ToHexString(serializedMetrics); + if (!serializedMetrics.empty()) { + item.setCString("serialized_metrics", serializedMetrics.c_str()); + } + if (!item.selfrecord()) { + ALOGE("Failed to self record framework metrics."); + } +} -void DrmHal::reportMetrics() const +void DrmHal::reportPluginMetrics() const { Vector metrics; String8 vendor; diff --git a/drm/libmediadrm/DrmMetrics.cpp b/drm/libmediadrm/DrmMetrics.cpp index dd91058ba4..03bd88a792 100644 --- a/drm/libmediadrm/DrmMetrics.cpp +++ b/drm/libmediadrm/DrmMetrics.cpp @@ -13,15 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +#define LOG_TAG "DrmMetrics" +#include #include #include #include #include #include +#include #include +#include "protos/metrics.pb.h" + +using ::android::String16; +using ::android::String8; +using ::android::drm_metrics::DrmFrameworkMetrics; using ::android::hardware::drm::V1_0::EventType; using ::android::hardware::drm::V1_0::KeyStatusType; using ::android::os::PersistableBundle; @@ -130,8 +137,7 @@ void ExportEventMetric(const android::EventMetric &event, } void ExportSessionLifespans( - const std::map> - &mSessionLifespans, + const std::map> &mSessionLifespans, PersistableBundle *metrics) { if (!metrics) { ALOGE("metrics was unexpectedly null."); @@ -146,9 +152,9 @@ void ExportSessionLifespans( PersistableBundle endTimesBundle; for (auto it = mSessionLifespans.begin(); it != mSessionLifespans.end(); it++) { - startTimesBundle.putLong(android::String16(it->first), - it->second.first); - endTimesBundle.putLong(android::String16(it->first), it->second.second); + String16 key(it->first.c_str(), it->first.size()); + startTimesBundle.putLong(key, it->second.first); + endTimesBundle.putLong(key, it->second.second); } metrics->putPersistableBundle( android::String16("drm.mediadrm.session_start_times_ms"), @@ -157,18 +163,13 @@ void ExportSessionLifespans( android::String16("drm.mediadrm.session_end_times_ms"), endTimesBundle); } -android::String8 ToHexString(const android::Vector &sessionId) { - android::String8 result; +std::string ToHexString(const android::Vector &sessionId) { + std::ostringstream out; + out << std::hex << std::setfill('0'); for (size_t i = 0; i < sessionId.size(); i++) { - result.appendFormat("%02x", sessionId[i]); + out << std::setw(2) << (int)(sessionId[i]); } - return result; -} - -int64_t getCurrentTimeMs() { - struct timeval tv; - gettimeofday(&tv, NULL); - return ((int64_t)tv.tv_sec * 1000) + ((int64_t)tv.tv_usec / 1000); + return out.str(); } } // namespace @@ -178,8 +179,8 @@ namespace android { MediaDrmMetrics::MediaDrmMetrics() : mOpenSessionCounter("drm.mediadrm.open_session", "status"), mCloseSessionCounter("drm.mediadrm.close_session", "status"), - mGetKeyRequestTiming("drm.mediadrm.get_key_request", "status"), - mProvideKeyResponseTiming("drm.mediadrm.provide_key_response", "status"), + mGetKeyRequestTimeUs("drm.mediadrm.get_key_request", "status"), + mProvideKeyResponseTimeUs("drm.mediadrm.provide_key_response", "status"), mGetProvisionRequestCounter("drm.mediadrm.get_provision_request", "status"), mProvideProvisionResponseCounter( @@ -192,14 +193,14 @@ MediaDrmMetrics::MediaDrmMetrics() void MediaDrmMetrics::SetSessionStart( const android::Vector &sessionId) { - String16 sessionIdHex = String16(ToHexString(sessionId)); + std::string sessionIdHex = ToHexString(sessionId); mSessionLifespans[sessionIdHex] = - std::make_pair(getCurrentTimeMs(), (int64_t)0); + std::make_pair(GetCurrentTimeMs(), (int64_t)0); } void MediaDrmMetrics::SetSessionEnd(const android::Vector &sessionId) { - String16 sessionIdHex = String16(ToHexString(sessionId)); - int64_t endTimeMs = getCurrentTimeMs(); + std::string sessionIdHex = ToHexString(sessionId); + int64_t endTimeMs = GetCurrentTimeMs(); if (mSessionLifespans.find(sessionIdHex) != mSessionLifespans.end()) { mSessionLifespans[sessionIdHex] = std::make_pair(mSessionLifespans[sessionIdHex].first, endTimeMs); @@ -215,8 +216,8 @@ void MediaDrmMetrics::Export(PersistableBundle *metrics) { } ExportCounterMetric(mOpenSessionCounter, metrics); ExportCounterMetric(mCloseSessionCounter, metrics); - ExportEventMetric(mGetKeyRequestTiming, metrics); - ExportEventMetric(mProvideKeyResponseTiming, metrics); + ExportEventMetric(mGetKeyRequestTimeUs, metrics); + ExportEventMetric(mProvideKeyResponseTimeUs, metrics); ExportCounterMetric(mGetProvisionRequestCounter, metrics); ExportCounterMetric(mProvideProvisionResponseCounter, metrics); ExportCounterMetricWithAttributeNames(mKeyStatusChangeCounter, metrics); @@ -225,4 +226,117 @@ void MediaDrmMetrics::Export(PersistableBundle *metrics) { ExportSessionLifespans(mSessionLifespans, metrics); } +status_t MediaDrmMetrics::GetSerializedMetrics(std::string *serializedMetrics) { + + if (!serializedMetrics) { + ALOGE("serializedMetrics was unexpectedly null."); + return UNEXPECTED_NULL; + } + + DrmFrameworkMetrics metrics; + + mOpenSessionCounter.ExportValues( + [&](const android::status_t status, const int64_t value) { + DrmFrameworkMetrics::Counter *counter = + metrics.add_open_session_counter(); + counter->set_count(value); + counter->mutable_attributes()->set_error_code(status); + }); + + mCloseSessionCounter.ExportValues( + [&](const android::status_t status, const int64_t value) { + DrmFrameworkMetrics::Counter *counter = + metrics.add_close_session_counter(); + counter->set_count(value); + counter->mutable_attributes()->set_error_code(status); + }); + + mGetProvisionRequestCounter.ExportValues( + [&](const android::status_t status, const int64_t value) { + DrmFrameworkMetrics::Counter *counter = + metrics.add_get_provisioning_request_counter(); + counter->set_count(value); + counter->mutable_attributes()->set_error_code(status); + }); + + mProvideProvisionResponseCounter.ExportValues( + [&](const android::status_t status, const int64_t value) { + DrmFrameworkMetrics::Counter *counter = + metrics.add_provide_provisioning_response_counter(); + counter->set_count(value); + counter->mutable_attributes()->set_error_code(status); + }); + + mKeyStatusChangeCounter.ExportValues( + [&](const KeyStatusType key_status_type, const int64_t value) { + DrmFrameworkMetrics::Counter *counter = + metrics.add_key_status_change_counter(); + counter->set_count(value); + counter->mutable_attributes()->set_key_status_type( + (uint32_t)key_status_type); + }); + + mEventCounter.ExportValues( + [&](const EventType event_type, const int64_t value) { + DrmFrameworkMetrics::Counter *counter = + metrics.add_event_callback_counter(); + counter->set_count(value); + counter->mutable_attributes()->set_event_type((uint32_t)event_type); + }); + + mGetDeviceUniqueIdCounter.ExportValues( + [&](const status_t status, const int64_t value) { + DrmFrameworkMetrics::Counter *counter = + metrics.add_get_device_unique_id_counter(); + counter->set_count(value); + counter->mutable_attributes()->set_error_code(status); + }); + + mGetKeyRequestTimeUs.ExportValues( + [&](const status_t status, const EventStatistics &stats) { + DrmFrameworkMetrics::DistributionMetric *metric = + metrics.add_get_key_request_time_us(); + metric->set_min(stats.min); + metric->set_max(stats.max); + metric->set_mean(stats.mean); + metric->set_operation_count(stats.count); + metric->set_variance(stats.sum_squared_deviation / stats.count); + metric->mutable_attributes()->set_error_code(status); + }); + + mProvideKeyResponseTimeUs.ExportValues( + [&](const status_t status, const EventStatistics &stats) { + DrmFrameworkMetrics::DistributionMetric *metric = + metrics.add_provide_key_response_time_us(); + metric->set_min(stats.min); + metric->set_max(stats.max); + metric->set_mean(stats.mean); + metric->set_operation_count(stats.count); + metric->set_variance(stats.sum_squared_deviation / stats.count); + metric->mutable_attributes()->set_error_code(status); + }); + + for (const auto &sessionLifespan : mSessionLifespans) { + auto *map = metrics.mutable_session_lifetimes(); + + (*map)[sessionLifespan.first].set_start_time_ms( + sessionLifespan.second.first); + (*map)[sessionLifespan.first].set_end_time_ms( + sessionLifespan.second.second); + } + + if (!metrics.SerializeToString(serializedMetrics)) { + ALOGE("Failed to serialize metrics."); + return UNKNOWN_ERROR; + } + + return OK; +} + +int64_t MediaDrmMetrics::GetCurrentTimeMs() { + struct timeval tv; + gettimeofday(&tv, NULL); + return ((int64_t)tv.tv_sec * 1000) + ((int64_t)tv.tv_usec / 1000); +} + } // namespace android diff --git a/drm/libmediadrm/PluginMetricsReporting.cpp b/drm/libmediadrm/PluginMetricsReporting.cpp index 26c8427c9d..6c97f2b4f0 100644 --- a/drm/libmediadrm/PluginMetricsReporting.cpp +++ b/drm/libmediadrm/PluginMetricsReporting.cpp @@ -23,7 +23,7 @@ #include -#include "protos/plugin_metrics.pb.h" +#include "protos/metrics.pb.h" namespace android { diff --git a/drm/libmediadrm/protos/metrics.proto b/drm/libmediadrm/protos/metrics.proto new file mode 100644 index 0000000000..aa26f5fb64 --- /dev/null +++ b/drm/libmediadrm/protos/metrics.proto @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +package android.drm_metrics; + +// 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 { + // Exactly one of the following values must be set. + 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; + + // Name of the application package associated with the metrics. + optional string app_package_name = 3; +} + +// This message contains the specific metrics captured by DrmMetrics. It is +// used for serializing and logging metrics. +// next id: 11. +message DrmFrameworkMetrics { + // TODO: Consider using extensions. + + // Attributes are associated with a recorded value. E.g. A counter may + // represent a count of an operation returning a specific error code. The + // error code will be an attribute. + message Attributes { + // Reserved for compatibility with logging proto. + reserved 2 to 13; + + // A general purpose error code where 0 means OK. + optional int32 error_code = 1; + + // Defined at ::android::hardware::drm::V1_0::KeyStatusType; + optional uint32 key_status_type = 14; + + // Defined at ::android::hardware::drm::V1_0::EventType; + optional uint32 event_type = 15; + } + + // The Counter message is used to store a count value with an associated + // Attribute. + message Counter { + optional int64 count = 1; + // Represents the attributes associated with this counter instance. + optional Attributes attributes = 2; + } + + // The DistributionMetric is meant to capture the moments of a normally + // distributed (or approximately normal) value. + message DistributionMetric { + optional double min = 1; + optional double max = 2; + optional double mean = 3; + optional double variance = 4; + optional double operation_count = 5; + + // Represents the attributes assocated with this distribution metric + // instance. + optional Attributes attributes = 6; + } + + message SessionLifetime { + // Start time of the session in milliseconds since epoch. + optional int64 start_time_ms = 1; + // End time of the session in milliseconds since epoch. + optional int64 end_time_ms = 2; + } + + // The count of open session operations. Each instance has a specific error + // code associated with it. + repeated Counter open_session_counter = 1; + + // The count of close session operations. Each instance has a specific error + // code associated with it. + repeated Counter close_session_counter = 2; + + // Count and execution time of getKeyRequest calls. + repeated DistributionMetric get_key_request_time_us = 3; + + // Count and execution time of provideKeyResponse calls. + repeated DistributionMetric provide_key_response_time_us = 4; + + // Count of getProvisionRequest calls. + repeated Counter get_provisioning_request_counter = 5; + + // Count of provideProvisionResponse calls. + repeated Counter provide_provisioning_response_counter = 6; + + // Count of key status events broken out by status type. + repeated Counter key_status_change_counter = 7; + + // Count of events broken out by event type + repeated Counter event_callback_counter = 8; + + // Count getPropertyByteArray calls to retrieve the device unique id. + repeated Counter get_device_unique_id_counter = 9; + + // Session ids to lifetime (start and end time) map. + // Session ids are strings of hex-encoded byte arrays. + map session_lifetimes = 10; +} + diff --git a/drm/libmediadrm/protos/plugin_metrics.proto b/drm/libmediadrm/protos/plugin_metrics.proto deleted file mode 100644 index 7e3bcf5304..0000000000 --- a/drm/libmediadrm/protos/plugin_metrics.proto +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto2"; - -package android.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 { - // Exactly one of the following values must be set. - 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; - - // Name of the application package associated with the metrics. - optional string app_package_name = 3; -} diff --git a/drm/libmediadrm/tests/Android.bp b/drm/libmediadrm/tests/Android.bp index b508c59f31..670d3b9f37 100644 --- a/drm/libmediadrm/tests/Android.bp +++ b/drm/libmediadrm/tests/Android.bp @@ -18,15 +18,21 @@ cc_test { "android.hardware.drm@1.0", "libbinder", "liblog", - "libmediadrm", + "libmediadrmmetrics_full", "libmediametrics", + "libprotobuf-cpp-full", "libutils", ], - include_dirs: ["frameworks/av/include/media"], - cflags: [ - "-Werror", - "-Wall", + static_libs: ["libgmock"], + include_dirs: [ + "frameworks/av/include/media", ], + cflags: [ + // Suppress unused parameter and no error options. These cause problems + // when using the map type in a proto definition. + "-Wno-unused-parameter", + "-Wno-error", + ] } cc_test { diff --git a/drm/libmediadrm/tests/DrmMetrics_test.cpp b/drm/libmediadrm/tests/DrmMetrics_test.cpp index d774eae6ed..fe762c9997 100644 --- a/drm/libmediadrm/tests/DrmMetrics_test.cpp +++ b/drm/libmediadrm/tests/DrmMetrics_test.cpp @@ -14,21 +14,40 @@ * limitations under the License. */ +#define LOG_TAG "DrmMetricsTest" +#include "DrmMetrics.h" + #include +#include +#include #include +#include -#include "DrmMetrics.h" +#include "protos/metrics.pb.h" +using ::android::drm_metrics::DrmFrameworkMetrics; using ::android::hardware::drm::V1_0::EventType; using ::android::hardware::drm::V1_0::KeyStatusType; using ::android::os::PersistableBundle; +using ::google::protobuf::util::MessageDifferencer; +using ::google::protobuf::TextFormat; namespace android { /** * Unit tests for the MediaDrmMetrics class. */ -class MediaDrmMetricsTest : public ::testing::Test { +class MediaDrmMetricsTest : public ::testing::Test {}; + +/** + * This derived class mocks the clock for testing purposes. + */ +class FakeMediaDrmMetrics : public MediaDrmMetrics { + public: + FakeMediaDrmMetrics() : MediaDrmMetrics(), time_(0) {}; + + int64_t GetCurrentTimeMs() { return time_++; } + int64_t time_; }; TEST_F(MediaDrmMetricsTest, EmptySuccess) { @@ -46,9 +65,9 @@ TEST_F(MediaDrmMetricsTest, AllValuesSuccessCounts) { metrics.mCloseSessionCounter.Increment(OK); { - EventTimer get_key_request_timer(&metrics.mGetKeyRequestTiming); + EventTimer get_key_request_timer(&metrics.mGetKeyRequestTimeUs); EventTimer provide_key_response_timer( - &metrics.mProvideKeyResponseTiming); + &metrics.mProvideKeyResponseTimeUs); get_key_request_timer.SetAttribute(OK); provide_key_response_timer.SetAttribute(OK); } @@ -109,9 +128,9 @@ TEST_F(MediaDrmMetricsTest, AllValuesFull) { for (status_t s : {OK, UNEXPECTED_NULL}) { { - EventTimer get_key_request_timer(&metrics.mGetKeyRequestTiming); + EventTimer get_key_request_timer(&metrics.mGetKeyRequestTimeUs); EventTimer provide_key_response_timer( - &metrics.mProvideKeyResponseTiming); + &metrics.mProvideKeyResponseTimeUs); get_key_request_timer.SetAttribute(s); provide_key_response_timer.SetAttribute(s); } @@ -135,9 +154,23 @@ TEST_F(MediaDrmMetricsTest, AllValuesFull) { metrics.mEventCounter.Increment(EventType::VENDOR_DEFINED); metrics.mEventCounter.Increment(EventType::SESSION_RECLAIMED); + android::Vector sessionId1; + sessionId1.push_back(1); + sessionId1.push_back(2); + android::Vector sessionId2; + sessionId2.push_back(3); + sessionId2.push_back(4); + String16 hexSessionId1("0102"); + String16 hexSessionId2("0304"); + + metrics.SetSessionStart(sessionId1); + metrics.SetSessionStart(sessionId2); + metrics.SetSessionEnd(sessionId2); + metrics.SetSessionEnd(sessionId1); + PersistableBundle bundle; metrics.Export(&bundle); - EXPECT_EQ(33U, bundle.size()); + EXPECT_EQ(35U, bundle.size()); // Verify the list of pairs of int64 metrics. std::vector> expected_values = { @@ -194,6 +227,25 @@ TEST_F(MediaDrmMetricsTest, AllValuesFull) { } } + // Verify the lifespans + PersistableBundle start_times; + PersistableBundle end_times; + String16 start_time_key("drm.mediadrm.session_start_times_ms"); + String16 end_time_key("drm.mediadrm.session_end_times_ms"); + ASSERT_TRUE(bundle.getPersistableBundle(start_time_key, &start_times)); + ASSERT_TRUE(bundle.getPersistableBundle(end_time_key, &end_times)); + EXPECT_EQ(2U, start_times.size()); + EXPECT_EQ(2U, end_times.size()); + int64_t start_time, end_time; + for (const auto& sid : { hexSessionId1, hexSessionId2 }) { + start_time = -1; + end_time = -1; + EXPECT_TRUE(start_times.getLong(sid, &start_time)); + EXPECT_TRUE(end_times.getLong(sid, &end_time)); + EXPECT_GT(start_time, 0); + EXPECT_GE(end_time, start_time); + } + // Validate timing values exist. String16 get_key_request_key( "drm.mediadrm.get_key_request.ok.average_time_micros"); @@ -207,4 +259,157 @@ TEST_F(MediaDrmMetricsTest, AllValuesFull) { EXPECT_GE(value, 0); } + +TEST_F(MediaDrmMetricsTest, CounterValuesProtoSerialization) { + MediaDrmMetrics metrics; + + metrics.mOpenSessionCounter.Increment(OK); + metrics.mOpenSessionCounter.Increment(UNEXPECTED_NULL); + metrics.mCloseSessionCounter.Increment(OK); + metrics.mCloseSessionCounter.Increment(UNEXPECTED_NULL); + + metrics.mGetProvisionRequestCounter.Increment(OK); + metrics.mGetProvisionRequestCounter.Increment(UNEXPECTED_NULL); + metrics.mProvideProvisionResponseCounter.Increment(OK); + metrics.mProvideProvisionResponseCounter.Increment(UNEXPECTED_NULL); + metrics.mGetDeviceUniqueIdCounter.Increment(OK); + metrics.mGetDeviceUniqueIdCounter.Increment(UNEXPECTED_NULL); + + metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::USABLE); + metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::EXPIRED); + metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::OUTPUTNOTALLOWED); + metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::STATUSPENDING); + metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::INTERNALERROR); + metrics.mEventCounter.Increment(EventType::PROVISION_REQUIRED); + metrics.mEventCounter.Increment(EventType::KEY_NEEDED); + metrics.mEventCounter.Increment(EventType::KEY_EXPIRED); + metrics.mEventCounter.Increment(EventType::VENDOR_DEFINED); + metrics.mEventCounter.Increment(EventType::SESSION_RECLAIMED); + + std::string serializedMetrics; + ASSERT_EQ(OK, metrics.GetSerializedMetrics(&serializedMetrics)); + + DrmFrameworkMetrics metricsProto; + ASSERT_TRUE(metricsProto.ParseFromString(serializedMetrics)); + + std::string expectedMetrics = + "open_session_counter { count: 1 attributes { error_code: -0x7FFFFFF8 } } " + "open_session_counter { count: 1 attributes { error_code: 0 } } " + "close_session_counter { count: 1 attributes { error_code: -0x7FFFFFF8 } } " + "close_session_counter { count: 1 attributes { error_code: 0 } } " + "get_provisioning_request_counter { count: 1 attributes { error_code: -0x7FFFFFF8 } } " + "get_provisioning_request_counter { count: 1 attributes { error_code: 0 } } " + "provide_provisioning_response_counter { count: 1 attributes { error_code: -0x7ffffff8 } } " + "provide_provisioning_response_counter { count: 1 attributes { error_code: 0 } } " + "get_device_unique_id_counter { count: 1 attributes { error_code: -0x7ffffff8 } } " + "get_device_unique_id_counter { count: 1 attributes { error_code: 0 } } " + "key_status_change_counter { count: 1 attributes { key_status_type: 0 } } " + "key_status_change_counter { count: 1 attributes { key_status_type: 1 } } " + "key_status_change_counter { count: 1 attributes { key_status_type: 2 } } " + "key_status_change_counter { count: 1 attributes { key_status_type: 3 } } " + "key_status_change_counter { count: 1 attributes { key_status_type: 4 } } " + "event_callback_counter { count: 1 attributes { event_type: 0 } } " + "event_callback_counter { count: 1 attributes { event_type: 1 } } " + "event_callback_counter { count: 1 attributes { event_type: 2 } } " + "event_callback_counter { count: 1 attributes { event_type: 3 } } " + "event_callback_counter { count: 1 attributes { event_type: 4 } } "; + + DrmFrameworkMetrics expectedMetricsProto; + ASSERT_TRUE(TextFormat::MergeFromString(expectedMetrics, &expectedMetricsProto)); + + std::string diffString; + MessageDifferencer differ; + differ.ReportDifferencesToString(&diffString); + ASSERT_TRUE(differ.Compare(expectedMetricsProto, metricsProto)) + << diffString; +} + +TEST_F(MediaDrmMetricsTest, TimeMetricsProtoSerialization) { + MediaDrmMetrics metrics; + + for (status_t s : {OK, UNEXPECTED_NULL}) { + double time = 0; + for (int i = 0; i < 5; i++) { + time += 1.0; + metrics.mGetKeyRequestTimeUs.Record(time, s); + metrics.mProvideKeyResponseTimeUs.Record(time, s); + } + } + + std::string serializedMetrics; + ASSERT_EQ(OK, metrics.GetSerializedMetrics(&serializedMetrics)); + + DrmFrameworkMetrics metricsProto; + ASSERT_TRUE(metricsProto.ParseFromString(serializedMetrics)); + + std::string expectedMetrics = + "get_key_request_timing { " + " min: 1 max: 5 mean: 3.5 variance: 1 operation_count: 5 " + " attributes { error_code: -0x7FFFFFF8 } " + "} " + "get_key_request_timing { " + " min: 1 max: 5 mean: 3.5 variance: 1 operation_count: 5 " + " attributes { error_code: 0 } " + "} " + "provide_key_response_timing { " + " min: 1 max: 5 mean: 3.5 variance: 1 operation_count: 5 " + " attributes { error_code: -0x7FFFFFF8 } " + "} " + "provide_key_response_timing { " + " min: 1 max: 5 mean: 3.5 variance: 1 operation_count: 5 " + " attributes { error_code: 0 } " + "} "; + + DrmFrameworkMetrics expectedMetricsProto; + ASSERT_TRUE(TextFormat::MergeFromString(expectedMetrics, &expectedMetricsProto)); + + std::string diffString; + MessageDifferencer differ; + differ.ReportDifferencesToString(&diffString); + ASSERT_TRUE(differ.Compare(expectedMetricsProto, metricsProto)) + << diffString; +} + +TEST_F(MediaDrmMetricsTest, SessionLifetimeProtoSerialization) { + // Use the fake so the clock is predictable; + FakeMediaDrmMetrics metrics; + + android::Vector sessionId1; + sessionId1.push_back(1); + sessionId1.push_back(2); + android::Vector sessionId2; + sessionId2.push_back(3); + sessionId2.push_back(4); + + metrics.SetSessionStart(sessionId1); + metrics.SetSessionStart(sessionId2); + metrics.SetSessionEnd(sessionId2); + metrics.SetSessionEnd(sessionId1); + + std::string serializedMetrics; + ASSERT_EQ(OK, metrics.GetSerializedMetrics(&serializedMetrics)); + + DrmFrameworkMetrics metricsProto; + ASSERT_TRUE(metricsProto.ParseFromString(serializedMetrics)); + + std::string expectedMetrics = + "session_lifetimes: { " + " key: '0102' " + " value { start_time_ms: 0 end_time_ms: 3 } " + "} " + "session_lifetimes: { " + " key: '0304' " + " value { start_time_ms: 1 end_time_ms: 2 } " + "} "; + + DrmFrameworkMetrics expectedMetricsProto; + ASSERT_TRUE(TextFormat::MergeFromString(expectedMetrics, &expectedMetricsProto)); + + std::string diffString; + MessageDifferencer differ; + differ.ReportDifferencesToString(&diffString); + ASSERT_TRUE(differ.Compare(expectedMetricsProto, metricsProto)) + << diffString; +} + } // namespace android diff --git a/media/libmedia/include/media/DrmHal.h b/media/libmedia/include/media/DrmHal.h index 67659b226e..bf91ea94a9 100644 --- a/media/libmedia/include/media/DrmHal.h +++ b/media/libmedia/include/media/DrmHal.h @@ -203,7 +203,8 @@ private: void writeByteArray(Parcel &obj, const hidl_vec& array); - void reportMetrics() const; + void reportPluginMetrics() const; + void reportFrameworkMetrics() const; status_t getPropertyStringInternal(String8 const &name, String8 &value) const; status_t getPropertyByteArrayInternal(String8 const &name, Vector &value) const; diff --git a/media/libmedia/include/media/DrmMetrics.h b/media/libmedia/include/media/DrmMetrics.h index d6d6ae6675..5c2fdf2510 100644 --- a/media/libmedia/include/media/DrmMetrics.h +++ b/media/libmedia/include/media/DrmMetrics.h @@ -34,14 +34,15 @@ namespace android { class MediaDrmMetrics { public: explicit MediaDrmMetrics(); + virtual ~MediaDrmMetrics() {}; // Count of openSession calls. CounterMetric mOpenSessionCounter; // Count of closeSession calls. CounterMetric mCloseSessionCounter; // Count and timing of getKeyRequest calls. - EventMetric mGetKeyRequestTiming; + EventMetric mGetKeyRequestTimeUs; // Count and timing of provideKeyResponse calls. - EventMetric mProvideKeyResponseTiming; + EventMetric mProvideKeyResponseTimeUs; // Count of getProvisionRequest calls. CounterMetric mGetProvisionRequestCounter; // Count of provideProvisionResponse calls. @@ -62,13 +63,31 @@ class MediaDrmMetrics { // Adds a session end time record. void SetSessionEnd(const Vector& sessionId); + // The app package name is the application package name that is using the + // instance. The app package name is held here for convenience. It is not + // serialized or exported with the metrics. + void SetAppPackageName(const String8& appPackageName) { mAppPackageName = appPackageName; } + const String8& GetAppPackageName() { return mAppPackageName; } + // Export the metrics to a PersistableBundle. void Export(os::PersistableBundle* metricsBundle); + // Get the serialized metrics. Metrics are formatted as a serialized + // DrmFrameworkMetrics proto. If there is a failure serializing the metrics, + // this returns an error. The parameter |serlializedMetrics| is owned by the + // caller and must not be null. + status_t GetSerializedMetrics(std::string* serializedMetrics); + + protected: + // This is visible for testing only. + virtual int64_t GetCurrentTimeMs(); + private: // Session lifetimes. A pair of values representing the milliseconds since // epoch, UTC. The first value is the start time, the second is the end time. - std::map> mSessionLifespans; + std::map> mSessionLifespans; + + String8 mAppPackageName; }; } // namespace android