The interface is defined in hardware/interfaces/drm/aidl(http://go/ag/15329852). Test: build m android.hardware.drm-service.widevine -j128 Test: build_and_run_all_unit_tests.sh for hidl tests Test: atest VtsAidlHalDrmTargetTest Test: atest vts_treble_vintf_vendor_test:vts_treble_vintf_vendor_test.DeviceManifest/SingleManifestTest#ManifestAidlHalsServed/0 -- --abi x86_64 Bug: 200055138 Bug: 170964303 Change-Id: I5654d90d8a4b0bae4b4a78e79b27c1cafec36be7
276 lines
9.2 KiB
C++
276 lines
9.2 KiB
C++
//
|
|
// Copyright 2018 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"
|
|
#include "WVDrmFactory.h"
|
|
|
|
#include <hwbinder/IPCThreadState.h>
|
|
#include <utils/Log.h>
|
|
|
|
#include "HidlTypes.h"
|
|
#include "WVCDMSingleton.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 V1_4 {
|
|
namespace widevine {
|
|
|
|
using wvdrm::hardware::drm::V1_4::widevine::WVDrmPlugin;
|
|
|
|
WVGenericCryptoInterface WVDrmFactory::sOemCryptoInterface;
|
|
|
|
Return<bool> WVDrmFactory::isCryptoSchemeSupported(
|
|
const hidl_array<uint8_t, 16>& uuid) {
|
|
return isWidevineUUID(uuid.data());
|
|
}
|
|
|
|
Return<bool> WVDrmFactory::isCryptoSchemeSupported_1_2(
|
|
const hidl_array<uint8_t, 16>& uuid, const hidl_string& initDataType,
|
|
SecurityLevel level) {
|
|
if (!isWidevineUUID(uuid.data()) || !isContentTypeSupported(initDataType)) {
|
|
return false;
|
|
}
|
|
|
|
if (wvcdm::WvContentDecryptionModule::IsSecurityLevelSupported(
|
|
wvcdm::kSecurityLevelL1)) {
|
|
if (wvcdm::WvContentDecryptionModule::IsAudio(initDataType)) {
|
|
if (level < SecurityLevel::HW_SECURE_ALL) {
|
|
return true;
|
|
}
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
return level <= SecurityLevel::SW_SECURE_DECODE;
|
|
}
|
|
|
|
Return<bool> WVDrmFactory::isContentTypeSupported(
|
|
const hidl_string& initDataType) {
|
|
return wvcdm::WvContentDecryptionModule::IsSupported(initDataType.c_str());
|
|
}
|
|
|
|
Return<void> WVDrmFactory::createPlugin(const hidl_array<uint8_t, 16>& uuid,
|
|
const hidl_string& appPackageName,
|
|
createPlugin_cb _hidl_cb) {
|
|
const auto& self = android::hardware::IPCThreadState::self();
|
|
const char* sid = self->getCallingSid();
|
|
sid = sid ? (strstr(sid, "mediadrmserver") ? sid : "app") : "nullptr";
|
|
ALOGI("[%s][%s] calling %s", sid, appPackageName.c_str(),
|
|
__PRETTY_FUNCTION__);
|
|
|
|
sp<IDrmPlugin> plugin;
|
|
if (!isCryptoSchemeSupported(uuid.data())) {
|
|
ALOGE(
|
|
"Widevine Drm HAL: failed to create drm plugin, "
|
|
"invalid crypto scheme");
|
|
_hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, plugin);
|
|
return Void();
|
|
}
|
|
|
|
if (!isBlankAppPackageNameAllowed() && appPackageName.empty()) {
|
|
ALOGE(
|
|
"Widevine Drm HAL: Failed to create DRM Plugin, blank App Package "
|
|
"Name disallowed.");
|
|
_hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, plugin);
|
|
return Void();
|
|
}
|
|
|
|
plugin = new WVDrmPlugin(getCDM(), appPackageName.c_str(),
|
|
&sOemCryptoInterface, areSpoidsEnabled());
|
|
android::hardware::setRequestingSid(plugin, true);
|
|
_hidl_cb(Status::OK, plugin);
|
|
return Void();
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
Return<void> WVDrmFactory::getSupportedCryptoSchemes(
|
|
getSupportedCryptoSchemes_cb _hidl_cb) {
|
|
std::vector<hidl_array<uint8_t, 16>> schemes;
|
|
for (const auto& scheme : wvdrm::getSupportedCryptoSchemes()) {
|
|
schemes.push_back(scheme);
|
|
}
|
|
_hidl_cb(schemes);
|
|
return Void();
|
|
}
|
|
|
|
std::string WVDrmFactory::stringToHex(const std::string& input) {
|
|
// If input contains punctuations that are not part of
|
|
// a valid server url, we need to convert it to hex.
|
|
const std::string validChars = "/-._~%:";
|
|
bool toHex = false;
|
|
for (const char ch : input) {
|
|
if (ispunct(ch) != 0 && validChars.find(ch) == std::string::npos) {
|
|
toHex = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!toHex) return input;
|
|
|
|
static constexpr char hex[] = "0123456789ABCDEF";
|
|
|
|
std::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(FILE* out) {
|
|
fprintf(out, "\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;
|
|
|
|
sp<wvcdm::WvContentDecryptionModule> cdm(getCDM());
|
|
|
|
std::vector<drm_metrics::WvCdmMetrics> metrics;
|
|
bool full_list_returned = true;
|
|
wvcdm::CdmResponseType result =
|
|
cdm->GetMetrics(&metrics, &full_list_returned);
|
|
if (metrics.empty()) {
|
|
fprintf(out,
|
|
"Metrics not available, please retry while streaming a video\n");
|
|
} else if (!full_list_returned) {
|
|
fprintf(out,
|
|
"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) {
|
|
fprintf(out, "*** Metric size=%zu\n", metric.DebugString().size());
|
|
std::string formatted;
|
|
wv_metrics::FormatWvCdmMetrics(metric, formatted);
|
|
fprintf(out, "%s\n", formatted.c_str());
|
|
}
|
|
} else {
|
|
fprintf(out, "GetMetrics failed, error=%d\n", result);
|
|
}
|
|
}
|
|
|
|
void WVDrmFactory::printCdmProperties(FILE* out) {
|
|
fprintf(out, "\n**** Widevine CDM properties ****\n");
|
|
|
|
sp<wvcdm::WvContentDecryptionModule> cdm(getCDM());
|
|
|
|
const bool isLevel1 =
|
|
cdm->IsSecurityLevelSupported(wvcdm::CdmSecurityLevel::kSecurityLevelL1);
|
|
fprintf(out, "default security level: [%s]\n", isLevel1 ? "L1" : "L3");
|
|
|
|
const std::map<std::string, std::string> cdmProperties = {
|
|
{"version- Widevine CDM:", wvcdm::QUERY_KEY_WVCDM_VERSION},
|
|
{"version- current SRM:", wvcdm::QUERY_KEY_CURRENT_SRM_VERSION},
|
|
{"version(major)- OEM Crypto API:",
|
|
wvcdm::QUERY_KEY_OEMCRYPTO_API_VERSION},
|
|
{"version(minor)- OEM Crypto API:",
|
|
wvcdm::QUERY_KEY_OEMCRYPTO_API_MINOR_VERSION},
|
|
{"id- device:", wvcdm::QUERY_KEY_DEVICE_ID},
|
|
{"id- system:", 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},
|
|
{"OEM Crypto 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},
|
|
};
|
|
|
|
std::string value;
|
|
for (const auto& property : cdmProperties) {
|
|
cdm->QueryStatus(wvcdm::RequestedSecurityLevel::kLevelDefault,
|
|
property.second, &value);
|
|
std::string outString = stringToHex(value);
|
|
fprintf(out, "%s [%s]\n", property.first.c_str(), outString.c_str());
|
|
value.clear();
|
|
}
|
|
}
|
|
|
|
Return<void> WVDrmFactory::debug(const hidl_handle& fd,
|
|
const hidl_vec<hidl_string>& args) {
|
|
if (fd.getNativeHandle() == nullptr || fd->numFds < 1) {
|
|
ALOGE("%s: missing fd for writing", __FUNCTION__);
|
|
return Void();
|
|
}
|
|
|
|
FILE* out = fdopen(dup(fd->data[0]), "w");
|
|
fprintf(out, "\nDefault to print all info if no arguments are used.\n");
|
|
fprintf(out, "Optional arguments are:\n");
|
|
fprintf(out, "\tm:cdm metrics | p:cdm properties\n");
|
|
fprintf(out,
|
|
"Usage: adb shell lshal debug "
|
|
"android.hardware.drm@1.3::IDrmFactory/widevine [m|p]\n");
|
|
|
|
bool dumpCdmProperties, dumpCdmMetrics = false;
|
|
if (args.size() == 0) {
|
|
// default to print all info if no arguments are given
|
|
dumpCdmProperties = dumpCdmMetrics = true;
|
|
} else {
|
|
for (auto& str : args) {
|
|
fprintf(out, "args: %s\n", str.c_str());
|
|
std::string option = str.c_str();
|
|
if (option.find('m') != std::string::npos ||
|
|
option.find('M') != std::string::npos) {
|
|
dumpCdmMetrics = true;
|
|
}
|
|
if (option.find('p') != std::string::npos ||
|
|
option.find('P') != std::string::npos) {
|
|
dumpCdmProperties = true;
|
|
}
|
|
}
|
|
}
|
|
if (dumpCdmMetrics) printCdmMetrics(out);
|
|
if (dumpCdmProperties) printCdmProperties(out);
|
|
fclose(out);
|
|
|
|
return Void();
|
|
}
|
|
|
|
} // namespace widevine
|
|
} // namespace V1_4
|
|
} // namespace drm
|
|
} // namespace hardware
|
|
} // namespace wvdrm
|