[ Merge of http://go/wvgerrit/171271 ] There is a need to maintain a short history of metrics from CDMs which have been deleted. This CL adds this ability to the Android version of the WV CDM. The history cannot yet be maintained for long, as the WV CDM instance is destroyed if unused. Further changes are required to the plugin to maintain the history beyond the life-cycle of the CDM instance, and to properly format its output. Bug: 239462891 Bug: 270166158 Test: adb shell dumpsys android.hardware.drm.IDrmFactory/widevine -m Test: atest GtsMediaTestCases Change-Id: I81c0996602722a9795fc3951030d20bb39b5816b
376 lines
13 KiB
C++
376 lines
13 KiB
C++
//
|
|
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
|
// source code may only be used and distributed under the Widevine License
|
|
// Agreement.
|
|
//
|
|
|
|
//#define LOG_NDEBUG 0
|
|
#define LOG_TAG "WVCdm-DrmFactory"
|
|
|
|
#include "WVDrmFactory.h"
|
|
|
|
#include <algorithm>
|
|
#include <cctype>
|
|
#include <cinttypes>
|
|
#include <cstdio>
|
|
|
|
#include <android/binder_ibinder_platform.h>
|
|
#include <binder/IPCThreadState.h>
|
|
#include <utils/Log.h>
|
|
|
|
#include "Utils.h"
|
|
#include "WVCDMSingleton.h"
|
|
#include "WVCryptoPlugin.h"
|
|
#include "WVDrmPlugin.h"
|
|
#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"
|
|
|
|
namespace wvdrm {
|
|
namespace hardware {
|
|
namespace drm {
|
|
namespace widevine {
|
|
|
|
using std::string;
|
|
using std::vector;
|
|
|
|
using ::aidl::android::hardware::drm::CryptoSchemes;
|
|
using ::aidl::android::hardware::drm::SecurityLevel;
|
|
using ::aidl::android::hardware::drm::Status;
|
|
using ::aidl::android::hardware::drm::SupportedContentType;
|
|
using ::aidl::android::hardware::drm::Uuid;
|
|
|
|
WVGenericCryptoInterface WVDrmFactory::sOemCryptoInterface;
|
|
|
|
namespace {
|
|
void PrintStream(int fd, std::stringstream& ss) {
|
|
const std::string content = ss.str();
|
|
dprintf(fd, "%s", content.c_str());
|
|
}
|
|
|
|
void FormatIndent(int fd, size_t indent) {
|
|
const size_t kMaxIndent = 60;
|
|
const size_t real_indent = std::min(indent, kMaxIndent);
|
|
char buffer[kMaxIndent + 1];
|
|
memset(buffer, ' ', real_indent);
|
|
buffer[real_indent] = 0;
|
|
dprintf(fd, "%s", buffer);
|
|
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,
|
|
const wvcdm::WvMetricsSnapshot& snapshot,
|
|
size_t item_index) {
|
|
const size_t indent = parent_indent + 2;
|
|
// CDM identifier.
|
|
FormatIndent(fd, parent_indent); // First parameter uses indent + list tick.
|
|
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(snapshot.metrics(), indent, ss);
|
|
PrintStream(fd, ss);
|
|
}
|
|
} // namespace
|
|
|
|
bool WVDrmFactory::isCryptoSchemeSupported(const Uuid& in_uuid) {
|
|
return isWidevineUUID(in_uuid.uuid.data());
|
|
}
|
|
|
|
|
|
::ndk::SpAIBinder WVDrmFactory::createBinder() {
|
|
auto binder = BnDrmFactory::createBinder();
|
|
AIBinder_setRequestingSid(binder.get(), true);
|
|
return binder;
|
|
}
|
|
|
|
::ndk::ScopedAStatus WVDrmFactory::createCryptoPlugin(
|
|
const ::aidl::android::hardware::drm::Uuid& in_uuid,
|
|
const std::vector<uint8_t>& in_initData,
|
|
std::shared_ptr<::aidl::android::hardware::drm::ICryptoPlugin>*
|
|
_aidl_return) {
|
|
const char* sid = AIBinder_getCallingSid();
|
|
sid = sid ? (std::strstr(sid, "mediadrmserver") ? sid : "app") : "nullptr";
|
|
ALOGI("[%s] calling %s", sid, __PRETTY_FUNCTION__);
|
|
|
|
if (!isCryptoSchemeSupported(in_uuid)) {
|
|
ALOGE(
|
|
"Widevine Drm HAL: failed to create crypto plugin, "
|
|
"invalid crypto scheme");
|
|
*_aidl_return = nullptr;
|
|
return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
|
|
}
|
|
|
|
std::shared_ptr<WVCryptoPlugin> plugin =
|
|
::ndk::SharedRefBase::make<WVCryptoPlugin>(in_initData.data(),
|
|
in_initData.size(), getCDM());
|
|
*_aidl_return = std::move(plugin);
|
|
return toNdkScopedAStatus(Status::OK);
|
|
}
|
|
|
|
::ndk::ScopedAStatus WVDrmFactory::createDrmPlugin(
|
|
const Uuid& in_uuid, const string& in_appPackageName,
|
|
std::shared_ptr<::aidl::android::hardware::drm::IDrmPlugin>* _aidl_return) {
|
|
const char* sid = AIBinder_getCallingSid();
|
|
sid = sid ? (std::strstr(sid, "mediadrmserver") ? sid : "app") : "nullptr";
|
|
ALOGI("[%s][%s] calling %s", sid, in_appPackageName.c_str(),
|
|
__PRETTY_FUNCTION__);
|
|
|
|
if (!isCryptoSchemeSupported(in_uuid)) {
|
|
ALOGE(
|
|
"Widevine Drm HAL: failed to create drm plugin, "
|
|
"invalid crypto scheme");
|
|
*_aidl_return = nullptr;
|
|
return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
|
|
}
|
|
|
|
if (!isBlankAppPackageNameAllowed() && in_appPackageName.empty()) {
|
|
ALOGE(
|
|
"Widevine Drm HAL: Failed to create DRM Plugin, blank App Package "
|
|
"Name disallowed.");
|
|
*_aidl_return = nullptr;
|
|
return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
|
|
}
|
|
|
|
std::shared_ptr<WVDrmPlugin> plugin = ::ndk::SharedRefBase::make<WVDrmPlugin>(
|
|
getCDM(), in_appPackageName.c_str(), &sOemCryptoInterface,
|
|
areSpoidsEnabled());
|
|
|
|
*_aidl_return = plugin;
|
|
return toNdkScopedAStatus(Status::OK);
|
|
}
|
|
|
|
bool WVDrmFactory::areSpoidsEnabled() {
|
|
return firstApiLevel() >= 26; // Android O
|
|
}
|
|
|
|
bool WVDrmFactory::isBlankAppPackageNameAllowed() {
|
|
return firstApiLevel() < 29; // Android Q
|
|
}
|
|
|
|
int32_t WVDrmFactory::firstApiLevel() {
|
|
// Check what this device's first API level was.
|
|
int32_t firstApiLevel =
|
|
android::base::GetIntProperty<int32_t>("ro.product.first_api_level", 0);
|
|
if (firstApiLevel == 0) {
|
|
// First API Level is 0 on factory ROMs, but we can assume the current SDK
|
|
// version is the first if it's a factory ROM.
|
|
firstApiLevel =
|
|
android::base::GetIntProperty<int32_t>("ro.build.version.sdk", 0);
|
|
}
|
|
return firstApiLevel;
|
|
}
|
|
|
|
::ndk::ScopedAStatus WVDrmFactory::getSupportedCryptoSchemes(
|
|
CryptoSchemes* _aidl_return) {
|
|
CryptoSchemes schemes{};
|
|
for (const auto& uuid : wvdrm::getSupportedCryptoSchemes()) {
|
|
schemes.uuids.push_back({uuid});
|
|
}
|
|
|
|
bool isL1 = wvcdm::WvContentDecryptionModule::IsSecurityLevelSupported(wvcdm::kSecurityLevelL1);
|
|
for (auto mime : {wvcdm::ISO_BMFF_VIDEO_MIME_TYPE, wvcdm::ISO_BMFF_AUDIO_MIME_TYPE,
|
|
wvcdm::WEBM_VIDEO_MIME_TYPE, wvcdm::WEBM_AUDIO_MIME_TYPE,
|
|
wvcdm::CENC_INIT_DATA_FORMAT, wvcdm::HLS_INIT_DATA_FORMAT,
|
|
wvcdm::WEBM_INIT_DATA_FORMAT}) {
|
|
bool isAudio = wvcdm::WvContentDecryptionModule::IsAudio(mime);
|
|
SupportedContentType ct{mime, SecurityLevel::SW_SECURE_CRYPTO, SecurityLevel::SW_SECURE_DECODE};
|
|
if (isL1) {
|
|
ct.maxLevel = isAudio ? SecurityLevel::HW_SECURE_DECODE : SecurityLevel::HW_SECURE_ALL;
|
|
}
|
|
schemes.mimeTypes.push_back(ct);
|
|
}
|
|
*_aidl_return = schemes;
|
|
return ::ndk::ScopedAStatus::ok();
|
|
}
|
|
|
|
string WVDrmFactory::stringToHex(const string& input) {
|
|
if (std::all_of(input.begin(), input.end(), ::isprint)) {
|
|
return input;
|
|
}
|
|
|
|
static constexpr char hex[] = "0123456789ABCDEF";
|
|
string output;
|
|
output.reserve(input.length() * 2);
|
|
for (const unsigned char ch : input) {
|
|
output.push_back(hex[ch >> 4]);
|
|
output.push_back(hex[ch & 15]);
|
|
}
|
|
return output;
|
|
}
|
|
|
|
void WVDrmFactory::printCdmMetrics(int fd) {
|
|
dprintf(fd, "widevine_cdm_metrics:\n");
|
|
|
|
// Verify that the version of the library that we linked against is
|
|
// compatible with the version of the headers we compiled against.
|
|
GOOGLE_PROTOBUF_VERIFY_VERSION;
|
|
|
|
android::sp<wvcdm::WvContentDecryptionModule> cdm(getCDM());
|
|
|
|
std::vector<wvcdm::WvMetricsSnapshot> snapshots;
|
|
bool full_list_returned = false;
|
|
wvcdm::CdmResponseType result =
|
|
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 (snapshots.empty()) {
|
|
// YAML does not support empty property values.
|
|
const char kNoMetricsMessage[] =
|
|
"Metrics not available, please retry while streaming a video.";
|
|
dprintf(fd, " live_metrics: [] # %s\n", kNoMetricsMessage);
|
|
} else {
|
|
dprintf(fd, " live_metrics: ");
|
|
if (full_list_returned) {
|
|
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", snapshots.size(), kPartialListMessage);
|
|
}
|
|
for (size_t i = 0; i < snapshots.size(); i++) {
|
|
FormatWvMetricsSnapshotItem(fd, 2, snapshots[i], i);
|
|
}
|
|
}
|
|
// TODO(b/270166158): Print metrics history.
|
|
}
|
|
|
|
void WVDrmFactory::printCdmProperties(int fd) {
|
|
dprintf(fd, "widevine_cdm_properties:\n");
|
|
|
|
android::sp<wvcdm::WvContentDecryptionModule> cdm(getCDM());
|
|
|
|
const bool isLevel1 =
|
|
cdm->IsSecurityLevelSupported(wvcdm::CdmSecurityLevel::kSecurityLevelL1);
|
|
dprintf(fd, " default_security_level: %s\n", isLevel1 ? "L1" : "L3");
|
|
|
|
const std::map<string, string> cdmProperties = {
|
|
{"version_widevine_cdm", wvcdm::QUERY_KEY_WVCDM_VERSION},
|
|
{"version_current_srm", wvcdm::QUERY_KEY_CURRENT_SRM_VERSION},
|
|
{"version_major_oemcrypto_api",
|
|
wvcdm::QUERY_KEY_OEMCRYPTO_API_VERSION},
|
|
{"version_minor_oemcrypto_api",
|
|
wvcdm::QUERY_KEY_OEMCRYPTO_API_MINOR_VERSION},
|
|
{"device_id", wvcdm::QUERY_KEY_DEVICE_ID},
|
|
{"system_id", wvcdm::QUERY_KEY_SYSTEM_ID},
|
|
{"renewal_server_url", wvcdm::QUERY_KEY_RENEWAL_SERVER_URL},
|
|
{"hdcp_level_max", wvcdm::QUERY_KEY_MAX_HDCP_LEVEL},
|
|
{"hdcp_level_current", wvcdm::QUERY_KEY_CURRENT_HDCP_LEVEL},
|
|
{"num_sessions_max_supported", wvcdm::QUERY_KEY_MAX_NUMBER_OF_SESSIONS},
|
|
{"num_sessions_opened", wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS},
|
|
{"resource_rating_tier", wvcdm::QUERY_KEY_RESOURCE_RATING_TIER},
|
|
{"support_decrypt_hash", wvcdm::QUERY_KEY_DECRYPT_HASH_SUPPORT},
|
|
{"support_SRM_update", wvcdm::QUERY_KEY_SRM_UPDATE_SUPPORT},
|
|
{"support_usage_table", wvcdm::QUERY_KEY_USAGE_SUPPORT},
|
|
{"max_usage_table_entries", wvcdm::QUERY_KEY_MAX_USAGE_TABLE_ENTRIES},
|
|
{"oemcrypto_build_info", wvcdm::QUERY_KEY_OEMCRYPTO_BUILD_INFORMATION},
|
|
{"provisioning_id", wvcdm::QUERY_KEY_PROVISIONING_ID},
|
|
{"provisioning_model", wvcdm::QUERY_KEY_PROVISIONING_MODEL},
|
|
{"analog_capabilities", wvcdm::QUERY_KEY_ANALOG_OUTPUT_CAPABILITIES},
|
|
{"can_disable_analog_output",
|
|
wvcdm::QUERY_KEY_CAN_DISABLE_ANALOG_OUTPUT},
|
|
{"watermarking_support", wvcdm::QUERY_KEY_WATERMARKING_SUPPORT},
|
|
{"production_ready", wvcdm::QUERY_KEY_PRODUCTION_READY},
|
|
// Debug properties. Not exposed to app.
|
|
{"boot_certificate_chain",
|
|
wvcdm::QUERY_KEY_DEBUG_BOOT_CERTIFICATE_CHAIN},
|
|
};
|
|
|
|
string value;
|
|
for (const auto& property : cdmProperties) {
|
|
cdm->QueryStatus(wvcdm::RequestedSecurityLevel::kLevelDefault,
|
|
property.second, &value);
|
|
string outString = stringToHex(value);
|
|
dprintf(fd, " %s: %s\n", property.first.c_str(), outString.c_str());
|
|
value.clear();
|
|
}
|
|
}
|
|
|
|
void WVDrmFactory::printUsage(int fd) {
|
|
dprintf(fd, "\nDefault to print all info if no arguments are used.\n");
|
|
dprintf(fd, "Optional arguments are:\n");
|
|
dprintf(fd, "\ta:print all info | m:cdm metrics | p:cdm properties\n");
|
|
dprintf(fd,
|
|
"Usage: adb shell dumpsys "
|
|
"android.hardware.drm.IDrmFactory/widevine [-a|-h|-m|-p]\n");
|
|
}
|
|
|
|
binder_status_t WVDrmFactory::dump(int fd, const char** args,
|
|
uint32_t numArgs) {
|
|
if (fd < 0) {
|
|
ALOGE("%s: missing fd for writing", __FUNCTION__);
|
|
return STATUS_BAD_VALUE;
|
|
}
|
|
|
|
bool dumpCdmProperties, dumpCdmMetrics = false;
|
|
if (numArgs == 0) {
|
|
// default to print all info if no arguments are given
|
|
dumpCdmProperties = dumpCdmMetrics = true;
|
|
} else {
|
|
for (auto&& str : std::vector<std::string_view>{args, args + numArgs}) {
|
|
string option = str.data();
|
|
if (option.find("-a") != string::npos ||
|
|
option.find("-A") != string::npos) {
|
|
dumpCdmProperties = dumpCdmMetrics = true;
|
|
} else if (option.find("-m") != string::npos ||
|
|
option.find("-M") != string::npos) {
|
|
dumpCdmMetrics = true;
|
|
} else if (option.find("-p") != string::npos ||
|
|
option.find("-P") != string::npos) {
|
|
dumpCdmProperties = true;
|
|
} else if (option.find("-h") != string::npos ||
|
|
option.find("-H") != string::npos) {
|
|
printUsage(fd);
|
|
} else {
|
|
dprintf(fd, "Invalid arg: %s\n", option.c_str());
|
|
printUsage(fd);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dumpCdmMetrics) printCdmMetrics(fd);
|
|
if (dumpCdmProperties) printCdmProperties(fd);
|
|
fsync(fd);
|
|
|
|
return STATUS_OK;
|
|
}
|
|
|
|
} // namespace widevine
|
|
} // namespace drm
|
|
} // namespace hardware
|
|
} // namespace wvdrm
|