Includes minor format change for easier ingestion by other tools. Bug: 231677822 Test: adb shell dumpsys android.hardware.drm.IDrmFactory/widevine Change-Id: Ibd13c84bd8f93ea0fc6cbd38b56ef39541ecc867
307 lines
11 KiB
C++
307 lines
11 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 <cctype>
|
|
|
|
#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 "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;
|
|
|
|
bool WVDrmFactory::isCryptoSchemeSupported(const Uuid& in_uuid) {
|
|
return isWidevineUUID(in_uuid.uuid.data());
|
|
}
|
|
|
|
::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());
|
|
AIBinder_setRequestingSid(plugin->asBinder().get(), true);
|
|
*_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());
|
|
|
|
AIBinder_setRequestingSid(plugin->asBinder().get(), true);
|
|
*_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& s) {
|
|
string input(s.c_str());
|
|
bool toHex = false;
|
|
for (const char ch : input) {
|
|
if (!isprint(ch)) {
|
|
toHex = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!toHex) 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, "\n**** 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());
|
|
|
|
vector<drm_metrics::WvCdmMetrics> metrics;
|
|
bool full_list_returned = true;
|
|
wvcdm::CdmResponseType result =
|
|
cdm->GetMetrics(&metrics, &full_list_returned);
|
|
if (metrics.empty()) {
|
|
dprintf(fd,
|
|
"Metrics not available, please retry while streaming a video\n");
|
|
} else if (!full_list_returned) {
|
|
dprintf(fd,
|
|
"Not all metrics are returned due to some GetMetric error, "
|
|
"please check logcat for possible GetMetric errors.\n");
|
|
}
|
|
if (result == wvcdm::NO_ERROR) {
|
|
for (auto& metric : metrics) {
|
|
dprintf(fd, "*** Metric size=%zu\n", metric.DebugString().size());
|
|
string formatted;
|
|
wv_metrics::FormatWvCdmMetrics(metric, formatted);
|
|
dprintf(fd, "%s\n", formatted.c_str());
|
|
}
|
|
} else {
|
|
dprintf(fd, "GetMetrics failed, error=%d\n", result);
|
|
}
|
|
}
|
|
|
|
void WVDrmFactory::printCdmProperties(int fd) {
|
|
dprintf(fd, "\nwidevine_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},
|
|
};
|
|
|
|
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
|