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
gugelfrei
Adam Stone 6 years ago
parent 8cc97b178b
commit fb679e38bb

@ -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",
],
}

@ -16,6 +16,8 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "DrmHal"
#include <iomanip>
#include <utils/Log.h>
#include <binder/IPCThreadState.h>
@ -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<sp<IDrmFactory>> DrmHal::makeDrmFactories() {
sp<IDrmPlugin> DrmHal::makeDrmPlugin(const sp<IDrmFactory>& factory,
const uint8_t uuid[16], const String8& appPackageName) {
mMetrics.SetAppPackageName(appPackageName);
sp<IDrmPlugin> plugin;
Return<void> 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<uint8_t> 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<uint8_t> const &sessionId,
String8 &defaultUrl, DrmPlugin::KeyRequestType *keyRequestType) {
Mutex::Autolock autoLock(mLock);
INIT_CHECK();
EventTimer<status_t> keyRequestTimer(&mMetrics.mGetKeyRequestTiming);
EventTimer<status_t> keyRequestTimer(&mMetrics.mGetKeyRequestTimeUs);
DrmSessionManager::Instance()->useSession(sessionId);
@ -729,7 +742,7 @@ status_t DrmHal::getKeyRequest(Vector<uint8_t> const &sessionId,
status_t DrmHal::provideKeyResponse(Vector<uint8_t> const &sessionId,
Vector<uint8_t> const &response, Vector<uint8_t> &keySetId) {
Mutex::Autolock autoLock(mLock);
EventTimer<status_t> keyResponseTimer(&mMetrics.mProvideKeyResponseTiming);
EventTimer<status_t> keyResponseTimer(&mMetrics.mProvideKeyResponseTimeUs);
INIT_CHECK();
@ -1277,8 +1290,41 @@ void DrmHal::writeByteArray(Parcel &obj, hidl_vec<uint8_t> 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<uint8_t> metrics;
String8 vendor;

@ -13,15 +13,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "DrmMetrics"
#include <iomanip>
#include <utility>
#include <android-base/macros.h>
#include <media/DrmMetrics.h>
#include <media/stagefright/foundation/base64.h>
#include <sys/time.h>
#include <utils/Log.h>
#include <utils/Timers.h>
#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<T> &event,
}
void ExportSessionLifespans(
const std::map<android::String16, std::pair<int64_t, int64_t>>
&mSessionLifespans,
const std::map<std::string, std::pair<int64_t, int64_t>> &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<uint8_t> &sessionId) {
android::String8 result;
std::string ToHexString(const android::Vector<uint8_t> &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<uint8_t> &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<uint8_t> &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

@ -23,7 +23,7 @@
#include <media/MediaAnalyticsItem.h>
#include "protos/plugin_metrics.pb.h"
#include "protos/metrics.pb.h"
namespace android {

@ -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<string, SessionLifetime> session_lifetimes = 10;
}

@ -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;
}

@ -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 {

@ -14,21 +14,40 @@
* limitations under the License.
*/
#define LOG_TAG "DrmMetricsTest"
#include "DrmMetrics.h"
#include <binder/PersistableBundle.h>
#include <google/protobuf/text_format.h>
#include <google/protobuf/util/message_differencer.h>
#include <gtest/gtest.h>
#include <utils/Log.h>
#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<status_t> get_key_request_timer(&metrics.mGetKeyRequestTiming);
EventTimer<status_t> get_key_request_timer(&metrics.mGetKeyRequestTimeUs);
EventTimer<status_t> 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<status_t> get_key_request_timer(&metrics.mGetKeyRequestTiming);
EventTimer<status_t> get_key_request_timer(&metrics.mGetKeyRequestTimeUs);
EventTimer<status_t> 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<uint8_t> sessionId1;
sessionId1.push_back(1);
sessionId1.push_back(2);
android::Vector<uint8_t> 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<std::pair<std::string, int64_t>> 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<uint8_t> sessionId1;
sessionId1.push_back(1);
sessionId1.push_back(2);
android::Vector<uint8_t> 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

@ -203,7 +203,8 @@ private:
void writeByteArray(Parcel &obj, const hidl_vec<uint8_t>& 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<uint8_t> &value) const;

@ -34,14 +34,15 @@ namespace android {
class MediaDrmMetrics {
public:
explicit MediaDrmMetrics();
virtual ~MediaDrmMetrics() {};
// Count of openSession calls.
CounterMetric<status_t> mOpenSessionCounter;
// Count of closeSession calls.
CounterMetric<status_t> mCloseSessionCounter;
// Count and timing of getKeyRequest calls.
EventMetric<status_t> mGetKeyRequestTiming;
EventMetric<status_t> mGetKeyRequestTimeUs;
// Count and timing of provideKeyResponse calls.
EventMetric<status_t> mProvideKeyResponseTiming;
EventMetric<status_t> mProvideKeyResponseTimeUs;
// Count of getProvisionRequest calls.
CounterMetric<status_t> mGetProvisionRequestCounter;
// Count of provideProvisionResponse calls.
@ -62,13 +63,31 @@ class MediaDrmMetrics {
// Adds a session end time record.
void SetSessionEnd(const Vector<uint8_t>& 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<String16, std::pair<int64_t, int64_t>> mSessionLifespans;
std::map<std::string, std::pair<int64_t, int64_t>> mSessionLifespans;
String8 mAppPackageName;
};
} // namespace android

Loading…
Cancel
Save