Merge "Added metrics history for WV CDM for Android." into udc-dev am: b5a7412675 am: 600c37d848

Original change: https://googleplex-android-review.googlesource.com/c/platform/vendor/widevine/+/22794476

Change-Id: I218eb3950be0fc7686ccc34bb64f7244310c9be1
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Alex Dale
2023-04-29 00:49:29 +00:00
committed by Automerger Merge Worker
7 changed files with 259 additions and 80 deletions

View File

@@ -5,12 +5,14 @@
#ifndef CDM_BASE_WV_CONTENT_DECRYPTION_MODULE_H_
#define CDM_BASE_WV_CONTENT_DECRYPTION_MODULE_H_
#include <utils/RefBase.h>
#include <time.h>
#include <map>
#include <memory>
#include <mutex>
#include <utils/RefBase.h>
#include "cdm_identifier.h"
#include "disallow_copy_and_assign.h"
#include "file_store.h"
@@ -24,6 +26,32 @@ class CdmClientPropertySet;
class CdmEngine;
class WvCdmEventListener;
class WvMetricsSnapshot {
public:
WvMetricsSnapshot(const CdmIdentifier& identifier,
drm_metrics::WvCdmMetrics&& metrics, ::time_t ts);
// Acts as a constructor, but uses the current time.
static WvMetricsSnapshot MakeSnapshot(const CdmIdentifier& identifier,
drm_metrics::WvCdmMetrics&& metrics);
WvMetricsSnapshot() = default;
WvMetricsSnapshot(const WvMetricsSnapshot&) = default;
WvMetricsSnapshot(WvMetricsSnapshot&&) = default;
WvMetricsSnapshot& operator=(const WvMetricsSnapshot&) = default;
WvMetricsSnapshot& operator=(WvMetricsSnapshot&&) = default;
const CdmIdentifier& cdm_id() const { return cdm_id_; }
const drm_metrics::WvCdmMetrics& metrics() const { return metrics_; }
::time_t timestamp() const { return timestamp_; }
std::string GetFormattedTimestamp() const;
private:
CdmIdentifier cdm_id_;
drm_metrics::WvCdmMetrics metrics_;
::time_t timestamp_ = 0;
};
class WvContentDecryptionModule : public android::RefBase, public TimerHandler {
public:
WvContentDecryptionModule();
@@ -142,18 +170,30 @@ class WvContentDecryptionModule : public android::RefBase, public TimerHandler {
// Validate a passed-in service certificate
virtual bool IsValidServiceCertificate(const std::string& certificate);
// Fill the metrics parameter with the metrics data for the CdmEngine
// associated with the given CdmIdentifier. If the CdmEngine instance does
// not exist, this will return an error.
virtual CdmResponseType GetMetrics(const CdmIdentifier& identifier,
drm_metrics::WvCdmMetrics* metrics);
// Fill the |metrics| parameter with the engine metrics data for the
// CdmEngine associated with the given CdmIdentifier.
// If the CdmEngine instance does not exist, this will return an error.
virtual CdmResponseType GetCurrentMetrics(const CdmIdentifier& identifier,
drm_metrics::WvCdmMetrics* metrics);
// Fill the metrics parameter with the metrics data for all the CdmEngine
// associated with the given CdmIdentifiers. If there are no CdmEngine
// instances, this will return an error.
virtual CdmResponseType GetMetrics(
std::vector<drm_metrics::WvCdmMetrics>* metrics,
bool* full_list_returned);
// Fill the |snapshots| parameter wrapped engine metrics for all CdmEngine
// instances that currently enabled (actively decrypting or performing
// other engine-level operations).
// Current metrics snapshots contain the engine identifier, metrics data
// and time (roughly current time).
// |full_list_returned| will indicate whether all existing enginers were
// able to report their metrics successfully.
virtual CdmResponseType GetAllCurrentMetricsSnapshots(
std::vector<WvMetricsSnapshot>* snapshots, bool* full_list_returned);
// Fill the |snapshots| parameter wrapped engine metrics for several of
// the most recently closed CdmEngine instances.
// Saved metrics snapshots contain the engine identifier, metrics data
// and time of closing.
// The same engine identifier may appear multiple times in the list (
// depending on how the app utilizes the MediaDrm plugin).
virtual CdmResponseType GetAllSavedMetricsSnapshots(
std::vector<WvMetricsSnapshot>* snapshots);
// Closes the CdmEngine and sessions associated with the given CdmIdentifier.
virtual CdmResponseType CloseCdm(const CdmIdentifier& identifier);
@@ -234,8 +274,10 @@ class WvContentDecryptionModule : public android::RefBase, public TimerHandler {
// not exist, this will return an error.
// This methods assumes that |metrics| is not null and that the |cdms_lock_|
// has already been acquired.
CdmResponseType GetMetricsInternal(const CdmIdentifier& identifier,
drm_metrics::WvCdmMetrics* metrics);
CdmResponseType GetCurrentMetricsInternal(const CdmIdentifier& identifier,
drm_metrics::WvCdmMetrics* metrics);
void SaveMetrics(const CdmIdentifier& identifier,
drm_metrics::WvCdmMetrics&& metrics);
static std::mutex session_sharing_id_generation_lock_;
std::mutex timer_lock_;
@@ -264,6 +306,10 @@ class WvContentDecryptionModule : public android::RefBase, public TimerHandler {
// - Hold lock when erasing, release once erased.
std::mutex cdms_lock_;
// Contains a finite list of histories of different CDM engine instances.
// When a CDM engine is closed, its metrics will be saved.
std::deque<WvMetricsSnapshot> saved_metrics_snapshots_;
CORE_DISALLOW_COPY_AND_ASSIGN(WvContentDecryptionModule);
};
} // namespace wvcdm

View File

@@ -20,14 +20,71 @@
#include "wv_cdm_event_listener.h"
#include "wv_metrics.pb.h"
namespace wvcdm {
using wvutil::FileSystem;
using wvutil::LoggingUidSetter;
namespace {
const int kCdmTimerDurationSeconds = 1;
// In the interest of limiting memory usage, only a limited number of
// the most recent metrics snapshots whould be stored.
constexpr size_t kMaxSavedMetricsSnapshotCount = 50;
// Value returned by time() if the operation fails.
constexpr time_t kTimeError = static_cast<time_t>(-1);
// Removes certain field from the provided CdmIdentifier as to make
// it better usable for tracking the same engine across different
// calls into the DRM plugin service.
CdmIdentifier ScrubIdentifierForMetrics(const CdmIdentifier& identifier) {
return CdmIdentifier{identifier.spoid, identifier.origin,
identifier.app_package_name, identifier.unique_id,
/* user_id */ wvutil::UNKNOWN_UID};
}
} // namespace
namespace wvcdm {
WvMetricsSnapshot::WvMetricsSnapshot(const CdmIdentifier& identifier,
drm_metrics::WvCdmMetrics&& metrics,
time_t ts)
: cdm_id_(ScrubIdentifierForMetrics(identifier)),
metrics_(std::move(metrics)),
timestamp_(ts) {}
WvMetricsSnapshot WvMetricsSnapshot::MakeSnapshot(
const CdmIdentifier& identifier, drm_metrics::WvCdmMetrics&& metrics) {
const time_t now = time(nullptr);
if (now == kTimeError) {
LOGW("Failed to get current time");
return WvMetricsSnapshot(identifier, std::move(metrics), 0);
}
return WvMetricsSnapshot(identifier, std::move(metrics), now);
}
std::string WvMetricsSnapshot::GetFormattedTimestamp() const {
if (timestamp_ == 0) return "<unset>";
struct tm time_info = {};
if (localtime_r(&timestamp_, &time_info) == nullptr) {
LOGE("Failed to convert to local time");
return "<conversion error(" + std::to_string(timestamp_) + ")>";
}
char timestamp_buffer[64]; // asctime_r() requires at least 29 bytes.
memset(timestamp_buffer, 0, sizeof(timestamp_buffer));
if (asctime_r(&time_info, timestamp_buffer) == nullptr) {
LOGE("Failed to format localtime to ASC time");
return "<format error(" + std::to_string(timestamp_) + ")>";
}
std::string result(timestamp_buffer);
// The Linux manual illustrates asctime_r() as returning a value
// with a new line character at the end. However, it does not
// specify this in the description. This is to account for different
// behavior on different systems.
if (!result.empty() && result.back() == '\n') {
result.pop_back();
}
return result;
}
std::mutex WvContentDecryptionModule::session_sharing_id_generation_lock_;
@@ -394,45 +451,61 @@ bool WvContentDecryptionModule::IsValidServiceCertificate(
return cert.has_certificate();
}
CdmResponseType WvContentDecryptionModule::GetMetrics(
std::vector<drm_metrics::WvCdmMetrics>* metrics, bool* full_list_returned) {
if (!metrics || !full_list_returned) {
return CdmResponseType(PARAMETER_NULL);
}
std::unique_lock<std::mutex> auto_lock(cdms_lock_);
for (auto& key_value_pair : cdms_) {
const CdmIdentifier& identifier = key_value_pair.first;
drm_metrics::WvCdmMetrics metric;
const CdmResponseType status = GetMetricsInternal(identifier, &metric);
if (status == NO_ERROR) {
metrics->push_back(metric);
} else {
LOGD("GetMetrics call failed: cdm identifier=%u, error=%d",
identifier.unique_id, status.ToInt());
}
}
// With no streaming activities, |cdms_| size would be zero,
// treat it as a non full list in that case.
*full_list_returned = !cdms_.empty() && metrics->size() == cdms_.size();
// We only return error if no metrics is returned when cdms_ is not empty.
// - metrics && cdms_ sizes can both be zero, returns NO_ERROR
// - metrics size <= cdms_, returns NO_ERROR
// - metrics is empty, but cdms_ is not, returns UNKNOWN_ERROR
return (metrics->empty() && !cdms_.empty()) ? CdmResponseType(UNKNOWN_ERROR)
: CdmResponseType(NO_ERROR);
}
CdmResponseType WvContentDecryptionModule::GetMetrics(
CdmResponseType WvContentDecryptionModule::GetCurrentMetrics(
const CdmIdentifier& identifier, drm_metrics::WvCdmMetrics* metrics) {
if (!metrics) {
return CdmResponseType(PARAMETER_NULL);
}
std::unique_lock<std::mutex> auto_lock(cdms_lock_);
return GetMetricsInternal(identifier, metrics);
return GetCurrentMetricsInternal(identifier, metrics);
}
CdmResponseType WvContentDecryptionModule::GetMetricsInternal(
CdmResponseType WvContentDecryptionModule::GetAllCurrentMetricsSnapshots(
std::vector<WvMetricsSnapshot>* snapshots, bool* full_list_returned) {
if (!snapshots || !full_list_returned) {
return CdmResponseType(PARAMETER_NULL);
}
snapshots->clear();
std::unique_lock<std::mutex> auto_lock(cdms_lock_);
for (const auto& cdm_info : cdms_) {
const CdmIdentifier& identifier = cdm_info.first;
drm_metrics::WvCdmMetrics metric;
const CdmResponseType status =
GetCurrentMetricsInternal(identifier, &metric);
if (status == NO_ERROR) {
snapshots->push_back(
WvMetricsSnapshot::MakeSnapshot(identifier, std::move(metric)));
} else {
LOGD("Failed to get metrics: cdm_identifier = %u, error = %s",
identifier.unique_id, status.ToString().c_str());
}
}
// With no streaming activities, |cdms_| size would be zero,
// treat it as a non full list in that case.
*full_list_returned = !cdms_.empty() && snapshots->size() == cdms_.size();
// We only return error if no metrics is returned when cdms_ is not empty.
// - metrics && cdms_ sizes can both be zero, returns NO_ERROR
// - metrics size <= cdms_, returns NO_ERROR
// - metrics is empty, but cdms_ is not, returns UNKNOWN_ERROR
return (snapshots->empty() && !cdms_.empty()) ? CdmResponseType(UNKNOWN_ERROR)
: CdmResponseType(NO_ERROR);
}
CdmResponseType WvContentDecryptionModule::GetAllSavedMetricsSnapshots(
std::vector<WvMetricsSnapshot>* snapshots) {
if (snapshots == nullptr) {
return CdmResponseType(PARAMETER_NULL);
}
std::unique_lock<std::mutex> auto_lock(cdms_lock_);
// This has the potential to be an expensive operation. However,
// this function is only used during debug reporting.
snapshots->assign(saved_metrics_snapshots_.cbegin(),
saved_metrics_snapshots_.cend());
return CdmResponseType(NO_ERROR);
}
CdmResponseType WvContentDecryptionModule::GetCurrentMetricsInternal(
const CdmIdentifier& identifier, drm_metrics::WvCdmMetrics* metrics) {
// Note: Caller should lock before calling.
auto it = cdms_.find(identifier);
@@ -446,6 +519,16 @@ CdmResponseType WvContentDecryptionModule::GetMetricsInternal(
: CdmResponseType(UNKNOWN_ERROR);
}
void WvContentDecryptionModule::SaveMetrics(
const CdmIdentifier& identifier, drm_metrics::WvCdmMetrics&& metrics) {
// Caller should have acquired |cdms_lock_|.
saved_metrics_snapshots_.push_front(
WvMetricsSnapshot::MakeSnapshot(identifier, std::move(metrics)));
if (saved_metrics_snapshots_.size() > kMaxSavedMetricsSnapshotCount) {
saved_metrics_snapshots_.resize(kMaxSavedMetricsSnapshotCount);
}
}
WvContentDecryptionModule::CdmInfo::CdmInfo()
: cdm_engine(CdmEngineFactory::CreateCdmEngine(&file_system)) {}
@@ -489,6 +572,17 @@ CdmEngine* WvContentDecryptionModule::GetCdmForSessionId(
void WvContentDecryptionModule::CloseAllCdms() {
std::unique_lock<std::mutex> auto_lock(cdms_lock_);
for (const auto& cdm_info : cdms_) {
const CdmIdentifier& identifier = cdm_info.first;
drm_metrics::WvCdmMetrics metrics;
const CdmResponseType status =
GetCurrentMetricsInternal(identifier, &metrics);
if (status == NO_ERROR) {
SaveMetrics(identifier, std::move(metrics));
} else {
LOGD("CloseAllCdms failed to save metrics");
}
}
cdm_by_session_id_.clear();
cdms_.clear();
}
@@ -496,22 +590,32 @@ void WvContentDecryptionModule::CloseAllCdms() {
CdmResponseType WvContentDecryptionModule::CloseCdm(
const CdmIdentifier& cdm_identifier) {
std::unique_lock<std::mutex> auto_lock(cdms_lock_);
auto it = cdms_.find(cdm_identifier);
if (it == cdms_.end()) {
auto cdm_it = cdms_.find(cdm_identifier);
if (cdm_it == cdms_.end()) {
LOGE("Cdm Identifier not found");
// TODO(blueeyes): Create a better error.
return CdmResponseType(UNKNOWN_ERROR);
}
CdmEngine* cdm_engine = cdm_it->second.cdm_engine.get();
// Save metrics snapshot.
drm_metrics::WvCdmMetrics metrics;
const bool success = cdm_engine->GetMetricsSnapshot(&metrics);
if (success) {
SaveMetrics(cdm_identifier, std::move(metrics));
} else {
LOGW("Failed to get metrics snapshot: unique_id = %" PRIu32,
cdm_identifier.unique_id);
}
// Remove any sessions that point to this engine.
for (auto session_it = cdm_by_session_id_.begin();
session_it != cdm_by_session_id_.end();) {
if (session_it->second == it->second.cdm_engine.get()) {
if (session_it->second == cdm_engine) {
session_it = cdm_by_session_id_.erase(session_it);
} else {
++session_it;
}
}
cdms_.erase(it);
cdms_.erase(cdm_it);
return CdmResponseType(NO_ERROR);
}

View File

@@ -1545,7 +1545,8 @@ TEST_P(WvCdmStreamingUsageReportTest, DISABLED_UsageTest) {
// Validate that update usage table entry is exercised.
drm_metrics::WvCdmMetrics metrics;
ASSERT_EQ(NO_ERROR, decryptor_->GetMetrics(kDefaultCdmIdentifier, &metrics));
ASSERT_EQ(NO_ERROR, decryptor_->GetCurrentMetrics(
kDefaultCdmIdentifier, &metrics));
ValidateHasUpdateUsageEntry(metrics);
}

View File

@@ -45,7 +45,7 @@ class WvContentDecryptionModuleMetricsTest : public WvCdmTestBase {
TEST_F(WvContentDecryptionModuleMetricsTest, IdentifierNotFound) {
drm_metrics::WvCdmMetrics metrics;
ASSERT_EQ(wvcdm::UNKNOWN_ERROR,
decryptor_.GetMetrics(kDefaultCdmIdentifier, &metrics));
decryptor_.GetCurrentMetrics(kDefaultCdmIdentifier, &metrics));
}
TEST_F(WvContentDecryptionModuleMetricsTest, EngineOnlyMetrics) {
@@ -62,7 +62,7 @@ TEST_F(WvContentDecryptionModuleMetricsTest, EngineOnlyMetrics) {
drm_metrics::WvCdmMetrics metrics;
ASSERT_EQ(wvcdm::NO_ERROR,
decryptor_.GetMetrics(kDefaultCdmIdentifier, &metrics));
decryptor_.GetCurrentMetrics(kDefaultCdmIdentifier, &metrics));
// 100 is an arbitrary high value that shouldn't ever occur.
EXPECT_THAT(metrics.engine_metrics()
@@ -110,7 +110,7 @@ TEST_F(WvContentDecryptionModuleMetricsTest, EngineAndSessionMetrics) {
drm_metrics::WvCdmMetrics metrics;
ASSERT_EQ(wvcdm::NO_ERROR,
decryptor_.GetMetrics(kDefaultCdmIdentifier, &metrics));
decryptor_.GetCurrentMetrics(kDefaultCdmIdentifier, &metrics));
std::string serialized_metrics;
ASSERT_TRUE(metrics.SerializeToString(&serialized_metrics));
@@ -162,7 +162,8 @@ TEST_F(WvContentDecryptionModuleMetricsTest,
for (int i = 0; i < cdm_engine_count; i++) {
drm_metrics::WvCdmMetrics metrics;
metrics.Clear();
ASSERT_EQ(wvcdm::NO_ERROR, decryptor_.GetMetrics(identifiers[i], &metrics));
ASSERT_EQ(wvcdm::NO_ERROR,
decryptor_.GetCurrentMetrics(identifiers[i], &metrics));
std::string serialized_metrics;
ASSERT_TRUE(metrics.SerializeToString(&serialized_metrics));

View File

@@ -19,6 +19,7 @@
#include <list>
#include <sstream>
#include <string>
#include <utility>
#include "Utils.h"
#include "android-base/macros.h"
@@ -754,24 +755,23 @@ Status WVDrmPlugin::unprovisionDevice() {
::ndk::ScopedAStatus WVDrmPlugin::getMetrics(
vector<::aidl::android::hardware::drm::DrmMetricGroup>* _aidl_return) {
_aidl_return->clear();
CdmIdentifier identifier;
auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
if (status != Status::OK) {
*_aidl_return = vector<DrmMetricGroup>();
return toNdkScopedAStatus(status);
}
drm_metrics::WvCdmMetrics proto_metrics;
CdmResponseType result = mCDM->GetMetrics(identifier, &proto_metrics);
const CdmResponseType result = mCDM->GetCurrentMetrics(identifier, &proto_metrics);
if (result != wvcdm::NO_ERROR) {
*_aidl_return = vector<DrmMetricGroup>();
return toNdkScopedAStatus(mapCdmResponseType(result));
}
vector<DrmMetricGroup> wvMetrics;
::wvcdm::WvMetricsAdapter adapter;
::wvcdm::WvMetricsAdapter::ToWvMetrics(proto_metrics, &wvMetrics);
*_aidl_return = wvMetrics;
*_aidl_return = std::move(wvMetrics);
return toNdkScopedAStatus(Status::OK);
}
@@ -1196,7 +1196,8 @@ Status WVDrmPlugin::unprovisionDevice() {
if (status != Status::OK) {
ALOGE("Unexpected error retrieving cdm identifier: %d", status.get());
} else {
status = mapCdmResponseType(mCDM->GetMetrics(identifier, &metrics));
status =
mapCdmResponseType(mCDM->GetCurrentMetrics(identifier, &metrics));
}
}
if (status == Status::OK) {

View File

@@ -241,7 +241,7 @@ class MockCDM : public WvContentDecryptionModule {
MOCK_METHOD(bool, IsValidServiceCertificate, (const std::string &),
(override));
MOCK_METHOD(CdmResponseType, GetMetrics,
MOCK_METHOD(CdmResponseType, GetCurrentMetrics,
(const CdmIdentifier &, drm_metrics::WvCdmMetrics *), (override));
MOCK_METHOD(CdmResponseType, GetDecryptHashError,
@@ -1175,7 +1175,7 @@ TEST_F(WVDrmPluginHalTest, ReturnsExpectedPropertyValues) {
.WillOnce(DoAll(SetArgPointee<2>(wvcdm::QUERY_VALUE_NOT_SUPPORTED),
testing::Return(CdmResponseType(wvcdm::NO_ERROR))));
EXPECT_CALL(*mCdm, GetMetrics(_, _))
EXPECT_CALL(*mCdm, GetCurrentMetrics(_, _))
.WillOnce(DoAll(SetArgPointee<1>(expected_metrics),
testing::Return(CdmResponseType(wvcdm::NO_ERROR))));

View File

@@ -9,8 +9,10 @@
#include "WVDrmFactory.h"
#include <cctype>
#include <algorithm>
#include <cctype>
#include <cinttypes>
#include <cstdio>
#include <android/binder_ibinder_platform.h>
#include <binder/IPCThreadState.h>
@@ -23,6 +25,7 @@
#include "WVUUID.h"
#include "android-base/properties.h"
#include "cutils/properties.h"
#include "string_conversions.h"
#include "wv_cdm_constants.h"
#include "wv_content_decryption_module.h"
#include "wv_metrics.h"
@@ -59,21 +62,44 @@ void FormatIndent(int fd, size_t indent) {
if (real_indent < indent) FormatIndent(fd, indent - real_indent);
}
void FormatWvCdmIdentifier(int fd, size_t parent_indent,
const wvcdm::CdmIdentifier& identifier) {
const size_t indent = parent_indent + 2;
// Spoid.
FormatIndent(fd, indent);
dprintf(fd, "spoid: \"%s\"\n", wvutil::b2a_hex(identifier.spoid).c_str());
// Origin.
FormatIndent(fd, indent);
dprintf(fd, "origin: \"%s\"\n", wvutil::b2a_hex(identifier.origin).c_str());
// App package name.
FormatIndent(fd, indent);
dprintf(fd, "app_package_name: \"%s\"\n",
identifier.app_package_name.c_str());
// Unique ID.
FormatIndent(fd, indent);
dprintf(fd, "unique_id: %" PRIu32 "\n", identifier.unique_id);
}
void FormatWvMetricsSnapshotItem(int fd, size_t parent_indent,
drm_metrics::WvCdmMetrics& metrics,
const wvcdm::WvMetricsSnapshot& snapshot,
size_t item_index) {
const size_t indent = parent_indent + 2;
// TODO(b/239462891): Provide identifier and timestamp for metrics.
// Serialized proto size.
// CDM identifier.
FormatIndent(fd, parent_indent); // First parameter uses indent + list tick.
dprintf(fd, "- serialized_proto_bytes: %zu # [%zu]\n",
metrics.ByteSizeLong(), item_index);
dprintf(fd, "- identifier: # [%zu]\n", item_index);
FormatWvCdmIdentifier(fd, indent, snapshot.cdm_id());
// Timestamp.
FormatIndent(fd, indent);
dprintf(fd, "timestamp: \"%s\"\n", snapshot.GetFormattedTimestamp().c_str());
// Serialized proto size.
FormatIndent(fd, indent);
dprintf(fd, "serialized_proto_bytes: %zu\n",
snapshot.metrics().ByteSizeLong());
// Engine metrics.
FormatIndent(fd, indent);
dprintf(fd, "cdm_metrics:\n");
std::stringstream ss;
wv_metrics::FormatWvCdmMetrics(metrics, indent, ss);
wv_metrics::FormatWvCdmMetrics(snapshot.metrics(), indent, ss);
PrintStream(fd, ss);
}
} // namespace
@@ -213,15 +239,15 @@ void WVDrmFactory::printCdmMetrics(int fd) {
android::sp<wvcdm::WvContentDecryptionModule> cdm(getCDM());
vector<drm_metrics::WvCdmMetrics> metrics;
bool full_list_returned = true;
std::vector<wvcdm::WvMetricsSnapshot> snapshots;
bool full_list_returned = false;
wvcdm::CdmResponseType result =
cdm->GetMetrics(&metrics, &full_list_returned);
cdm->GetAllCurrentMetricsSnapshots(&snapshots, &full_list_returned);
if (result != wvcdm::NO_ERROR) {
dprintf(fd, " live_metrics:\n");
dprintf(fd, " error_message: \"%s\"\n", result.ToString().c_str());
dprintf(fd, " error_code: %d\n", static_cast<int>(result.code()));
} else if (metrics.empty()) {
} else if (snapshots.empty()) {
// YAML does not support empty property values.
const char kNoMetricsMessage[] =
"Metrics not available, please retry while streaming a video.";
@@ -229,15 +255,15 @@ void WVDrmFactory::printCdmMetrics(int fd) {
} else {
dprintf(fd, " live_metrics: ");
if (full_list_returned) {
dprintf(fd, "# count = %zu\n", metrics.size());
dprintf(fd, "# count = %zu\n", snapshots.size());
} else {
const char kPartialListMessage[] =
"Some metrics are missing due to an internal error, "
"check logs for details.";
dprintf(fd, "# count = %zu, %s\n", metrics.size(), kPartialListMessage);
dprintf(fd, "# count = %zu, %s\n", snapshots.size(), kPartialListMessage);
}
for (size_t i = 0; i < metrics.size(); i++) {
FormatWvMetricsSnapshotItem(fd, 2, metrics[i], i);
for (size_t i = 0; i < snapshots.size(); i++) {
FormatWvMetricsSnapshotItem(fd, 2, snapshots[i], i);
}
}
// TODO(b/270166158): Print metrics history.