// // 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 #include #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 WVDrmFactory::isCryptoSchemeSupported( const hidl_array& uuid) { return isWidevineUUID(uuid.data()); } Return WVDrmFactory::isCryptoSchemeSupported_1_2( const hidl_array& 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 WVDrmFactory::isContentTypeSupported( const hidl_string& initDataType) { return wvcdm::WvContentDecryptionModule::IsSupported(initDataType.c_str()); } Return WVDrmFactory::createPlugin(const hidl_array& 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 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("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("ro.build.version.sdk", 0); } return firstApiLevel; } Return WVDrmFactory::getSupportedCryptoSchemes( getSupportedCryptoSchemes_cb _hidl_cb) { std::vector> 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 cdm(getCDM()); std::vector 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 cdm(getCDM()); const bool isLevel1 = cdm->IsSecurityLevelSupported(wvcdm::CdmSecurityLevel::kSecurityLevelL1); fprintf(out, "default security level: [%s]\n", isLevel1 ? "L1" : "L3"); const std::map 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 WVDrmFactory::debug(const hidl_handle& fd, const hidl_vec& 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