Files
android/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp
Alex Dale c42627a23e Added metrics history for WV CDM for Android.
[ 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
2023-04-27 14:54:17 -07:00

2229 lines
78 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"
#include "WVDrmPlugin.h"
#include <aidl/android/hardware/drm/DrmMetric.h>
#include <android/binder_ibinder_platform.h>
#include <binder/IPCThreadState.h>
#include <stdlib.h>
#include <utils/Log.h>
#include <cstring>
#include <list>
#include <sstream>
#include <string>
#include <utility>
#include "Utils.h"
#include "android-base/macros.h"
#include "log.h"
#include "mapErrors-inl.h"
#include "media/stagefright/MediaErrors.h"
#include "openssl/sha.h"
#include "string_conversions.h"
#include "widevine_apex_info.h"
#include "wv_cdm_constants.h"
#include "wv_metrics.pb.h"
#include "wv_metrics_adapter.h"
namespace {
static const char* const kResetSecurityLevel = "";
static const char* const kEnable = "enable";
static const char* const kDisable = "disable";
static const std::string kPsshTag = "pssh";
static const char* const kSpecialUnprovisionResponse = "unprovision";
static const std::string kKeyAppPackageName = "application_name";
static const std::string kKeyOrigin = "origin";
} // namespace
namespace wvdrm {
namespace hardware {
namespace drm {
namespace widevine {
using std::shared_ptr;
using std::string;
using std::vector;
using ::aidl::android::hardware::drm::DrmMetric;
using ::aidl::android::hardware::drm::DrmMetricGroup;
using ::aidl::android::hardware::drm::DrmMetricNamedValue;
using ::aidl::android::hardware::drm::DrmMetricValue;
using ::aidl::android::hardware::drm::EventType;
using ::aidl::android::hardware::drm::HdcpLevel;
using ::aidl::android::hardware::drm::KeyRequestType;
using ::aidl::android::hardware::drm::KeySetId;
using ::aidl::android::hardware::drm::KeyStatus;
using ::aidl::android::hardware::drm::KeyStatusType;
using ::aidl::android::hardware::drm::KeyType;
using ::aidl::android::hardware::drm::KeyValue;
using ::aidl::android::hardware::drm::OfflineLicenseState;
using ::aidl::android::hardware::drm::SecureStop;
using ::aidl::android::hardware::drm::SecureStopId;
using ::aidl::android::hardware::drm::SecurityLevel;
using ::aidl::android::hardware::drm::Status;
using wvcdm::CdmAppParameterMap;
using wvcdm::CdmCertificateType;
using wvcdm::CdmInitData;
using wvcdm::CdmKeyRequest;
using wvcdm::CdmKeyRequestType;
using wvcdm::CdmKeyResponse;
using wvcdm::CdmKeySetId;
using wvcdm::CdmKeyStatus;
using wvcdm::CdmLicenseType;
using wvcdm::CdmProvisioningRequest;
using wvcdm::CdmProvisioningResponse;
using wvcdm::CdmQueryMap;
using wvcdm::CdmSecureStopId;
using wvcdm::CdmSecurityLevel;
using wvcdm::CdmUsageReport;
using wvcdm::CdmUsageReportList;
using wvcdm::kDefaultCdmIdentifier;
using wvcdm::KeyId;
using wvcdm::RequestedSecurityLevel;
namespace {
vector<uint8_t> StrToVector(const std::string& str) {
vector<uint8_t> vec(str.begin(), str.end());
return vec;
}
KeyRequestType ConvertFromCdmKeyRequestType(CdmKeyRequestType keyRequestType) {
switch (keyRequestType) {
case wvcdm::kKeyRequestTypeInitial:
return KeyRequestType::INITIAL;
case wvcdm::kKeyRequestTypeRenewal:
return KeyRequestType::RENEWAL;
case wvcdm::kKeyRequestTypeRelease:
return KeyRequestType::RELEASE;
case wvcdm::kKeyRequestTypeNone:
return KeyRequestType::NONE;
case wvcdm::kKeyRequestTypeUpdate:
return KeyRequestType::UPDATE;
default:
return KeyRequestType::UNKNOWN;
}
}
KeyStatusType ConvertFromCdmKeyStatus(CdmKeyStatus keyStatus) {
switch (keyStatus) {
case wvcdm::kKeyStatusUsable:
return KeyStatusType::USABLE;
case wvcdm::kKeyStatusExpired:
return KeyStatusType::EXPIRED;
case wvcdm::kKeyStatusOutputNotAllowed:
return KeyStatusType::OUTPUT_NOT_ALLOWED;
case wvcdm::kKeyStatusPending:
case wvcdm::kKeyStatusUsableInFuture:
return KeyStatusType::USABLE_IN_FUTURE;
case wvcdm::kKeyStatusInternalError:
default:
return KeyStatusType::INTERNAL_ERROR;
}
}
HdcpLevel mapHdcpLevel(const std::string& level) {
if (level == wvcdm::QUERY_VALUE_HDCP_V1)
return HdcpLevel::HDCP_V1;
else if (level == wvcdm::QUERY_VALUE_HDCP_V2_0)
return HdcpLevel::HDCP_V2;
else if (level == wvcdm::QUERY_VALUE_HDCP_V2_1)
return HdcpLevel::HDCP_V2_1;
else if (level == wvcdm::QUERY_VALUE_HDCP_V2_2)
return HdcpLevel::HDCP_V2_2;
else if (level == wvcdm::QUERY_VALUE_HDCP_V2_3)
return HdcpLevel::HDCP_V2_3;
else if (level == wvcdm::QUERY_VALUE_HDCP_NONE)
return HdcpLevel::HDCP_NONE;
else if (level == wvcdm::QUERY_VALUE_HDCP_NO_DIGITAL_OUTPUT)
return HdcpLevel::HDCP_NO_OUTPUT;
else {
ALOGE("Invalid HDCP level=%s", level.c_str());
return HdcpLevel::HDCP_NONE;
}
}
} // namespace
WVDrmPlugin::WVDrmPlugin(const android::sp<WvContentDecryptionModule>& cdm,
const std::string& appPackageName,
WVGenericCryptoInterface* crypto, bool useSpoid)
: mCdmIdentifierBuilder(useSpoid, *this, appPackageName),
mCDM(cdm),
mCrypto(crypto),
mCryptoSessions(),
mAppPackageName(appPackageName) {
Terminator::Register(this);
}
WVDrmPlugin::~WVDrmPlugin() {
wvutil::SetLoggingUid(mCdmIdentifierBuilder.user_id());
Terminator::Forget(this);
Close();
}
void WVDrmPlugin::Close() {
typedef std::map<CdmSessionId, std::shared_ptr<CryptoSession>>::iterator mapIterator;
auto cryptoSessions = mCryptoSessions.clear();
for (mapIterator iter = cryptoSessions.begin();
iter != cryptoSessions.end(); ++iter) {
CdmResponseType res = mCDM->CloseSession(iter->first);
if (!isCdmResponseTypeSuccess(res)) {
ALOGE("Failed to close session while destroying WVDrmPlugin");
}
}
// clear local copy of cryptoSessions map
cryptoSessions.clear();
if (mCdmIdentifierBuilder.is_sealed()) {
CdmIdentifier identifier;
auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
if (status != Status::OK) {
ALOGE("Failed to get cdm identifier %d", status.get());
} else {
status = mapCdmResponseType(mCDM->CloseCdm(identifier));
if (status != Status::OK) {
ALOGE("Failed to close cdm. status %d", status.get());
}
}
}
}
WvStatus WVDrmPlugin::openSessionCommon(vector<uint8_t>& sessionId) {
WvStatus status(Status::OK);
CdmIdentifier identifier;
status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
if (status != Status::OK) {
return status;
}
CdmSessionId cdmSessionId;
CdmResponseType res = mCDM->OpenSession("com.widevine", &mPropertySet,
identifier, this, &cdmSessionId);
if (!isCdmResponseTypeSuccess(res)) {
return mapAndNotifyOfCdmResponseType(sessionId, res);
}
bool success = false;
// Construct a CryptoSession
CdmQueryMap info;
res = mCDM->QueryOemCryptoSessionId(cdmSessionId, &info);
if (isCdmResponseTypeSuccess(res) &&
info.count(wvcdm::QUERY_KEY_OEMCRYPTO_SESSION_ID)) {
OEMCrypto_SESSION oecSessionId =
std::stoul(info[wvcdm::QUERY_KEY_OEMCRYPTO_SESSION_ID]);
mCryptoSessions.insert(cdmSessionId, oecSessionId);
success = true;
} else {
ALOGE("Unable to query key control info.");
}
if (success) {
// Marshal Session ID
sessionId = StrToVector(cdmSessionId);
return WvStatus(Status::OK);
} else {
mCDM->CloseSession(cdmSessionId);
if (!isCdmResponseTypeSuccess(res)) {
// We got an error code we can return.
return mapAndNotifyOfCdmResponseType(sessionId, res);
} else {
// We got a failure that did not give us an error code, such as a failure
// of AttachEventListener() or the key being missing from the map.
ALOGW("Returns UNKNOWN error for legacy status kErrorCDMGeneric");
return WvStatus(Status::ERROR_DRM_UNKNOWN);
}
}
}
SecurityLevel WVDrmPlugin::mapSecurityLevel(const std::string& level) {
SecurityLevel securityLevel = SecurityLevel::UNKNOWN;
if (wvcdm::QUERY_VALUE_SECURITY_LEVEL_L1 == level) {
securityLevel = SecurityLevel::HW_SECURE_ALL;
} else if (wvcdm::QUERY_VALUE_SECURITY_LEVEL_L2 == level) {
securityLevel = SecurityLevel::HW_SECURE_CRYPTO;
} else if (wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3 == level) {
securityLevel = SecurityLevel::SW_SECURE_CRYPTO;
} // else QUERY_VALUE_SECURITY_LEVEL_UNKNOWN returns Security::UNKNOWN
return securityLevel;
}
::ndk::ScopedAStatus WVDrmPlugin::openSession(SecurityLevel in_securityLevel,
vector<uint8_t>* _aidl_return) {
vector<uint8_t> sessionId;
if (SecurityLevel::DEFAULT == in_securityLevel) {
auto err = openSessionCommon(sessionId);
*_aidl_return = sessionId;
return toNdkScopedAStatus(err);
}
if (SecurityLevel::UNKNOWN == in_securityLevel) {
*_aidl_return = sessionId;
return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
}
std::string native_security_level;
auto status =
queryProperty(wvcdm::kLevelDefault, wvcdm::QUERY_KEY_SECURITY_LEVEL,
native_security_level);
if (status != Status::OK) {
*_aidl_return = sessionId;
return toNdkScopedAStatus(status);
}
if (wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3 == native_security_level &&
in_securityLevel >= SecurityLevel::SW_SECURE_DECODE) {
*_aidl_return = sessionId;
return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
}
std::string wvcdm_security_level =
(SecurityLevel::SW_SECURE_CRYPTO == in_securityLevel)
? wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3
: wvcdm::QUERY_VALUE_SECURITY_LEVEL_DEFAULT;
setPropertyString("securityLevel", std::string(wvcdm_security_level));
status = openSessionCommon(sessionId);
if (status == Status::OK) {
SecurityLevel currentSecurityLevel = SecurityLevel::UNKNOWN;
const auto ret = getSecurityLevel(sessionId, &currentSecurityLevel);
if (!ret.isOk() || in_securityLevel != currentSecurityLevel) {
ALOGE("Failed to open session with the requested security level=%d",
in_securityLevel);
closeSession(sessionId);
sessionId.clear();
status = WvStatus(Status::ERROR_DRM_INVALID_STATE);
}
}
*_aidl_return = sessionId;
return toNdkScopedAStatus(status);
}
::ndk::ScopedAStatus WVDrmPlugin::closeSession(
const vector<uint8_t>& in_sessionId) {
if (!in_sessionId.size()) {
return toNdkScopedAStatus(Status::BAD_VALUE);
}
CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end());
CdmResponseType res = mCDM->CloseSession(cdmSessionId);
mCryptoSessions.erase(cdmSessionId);
if (!isCdmResponseTypeSuccess(res)) {
return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED);
}
return toNdkScopedAStatus(Status::OK);
}
::ndk::ScopedAStatus WVDrmPlugin::getKeyRequest(
const vector<uint8_t>& in_scope, const vector<uint8_t>& in_initData,
const std::string& in_mimeType,
::aidl::android::hardware::drm::KeyType in_keyType,
const vector<::aidl::android::hardware::drm::KeyValue>&
in_optionalParameters,
::aidl::android::hardware::drm::KeyRequest* _aidl_return) {
if (!in_scope.size()) {
_aidl_return->request = vector<uint8_t>();
_aidl_return->requestType = KeyRequestType::UNKNOWN;
_aidl_return->defaultUrl = "";
return toNdkScopedAStatus(Status::BAD_VALUE);
}
KeyRequestType requestType = KeyRequestType::UNKNOWN;
WvStatus status(Status::OK);
std::string defaultUrl;
vector<uint8_t> request;
CdmIdentifier identifier;
status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
if (status != Status::OK) {
_aidl_return->request = request;
_aidl_return->requestType = KeyRequestType::UNKNOWN;
_aidl_return->defaultUrl = "";
return toNdkScopedAStatus(status);
}
CdmLicenseType cdmLicenseType;
CdmSessionId cdmSessionId;
CdmKeySetId cdmKeySetId;
if (in_keyType == KeyType::OFFLINE) {
cdmLicenseType = wvcdm::kLicenseTypeOffline;
cdmSessionId.assign(in_scope.begin(), in_scope.end());
} else if (in_keyType == KeyType::STREAMING) {
cdmLicenseType = wvcdm::kLicenseTypeStreaming;
cdmSessionId.assign(in_scope.begin(), in_scope.end());
} else if (in_keyType == KeyType::RELEASE) {
cdmLicenseType = wvcdm::kLicenseTypeRelease;
cdmKeySetId.assign(in_scope.begin(), in_scope.end());
} else {
_aidl_return->request = request;
_aidl_return->requestType = KeyRequestType::UNKNOWN;
_aidl_return->defaultUrl = "";
return toNdkScopedAStatus(Status::BAD_VALUE);
}
std::string cdmInitDataType = in_mimeType;
// Provide backwards-compatibility for apps that pass non-EME-compatible
// MIME types.
if (!WvContentDecryptionModule::IsSupported(cdmInitDataType)) {
cdmInitDataType = wvcdm::ISO_BMFF_VIDEO_MIME_TYPE;
}
CdmInitData processedInitData;
if (in_initData.size() > 0 &&
WvContentDecryptionModule::IsCenc(cdmInitDataType) &&
!initDataResemblesPSSH(in_initData)) {
// This data was passed in the old format, pre-unwrapped. We need to wrap
// the init data in a new PSSH header.
static const uint8_t psshPrefix[] = {
0, 0, 0, 0, // Total size
'p', 's', 's', 'h', // "PSSH"
0, 0, 0, 0, // Flags - must be zero
0xED, 0xEF, 0x8B, 0xA9, 0x79, 0xD6, 0x4A, 0xCE, // Widevine UUID
0xA3, 0xC8, 0x27, 0xDC, 0xD5, 0x1D, 0x21, 0xED,
0, 0, 0, 0 // Size of initData
};
processedInitData.assign(reinterpret_cast<const char*>(psshPrefix),
sizeof(psshPrefix) / sizeof(uint8_t));
processedInitData.append(reinterpret_cast<const char*>(in_initData.data()),
in_initData.size());
const size_t kPsshBoxSizeLocation = 0;
const size_t kInitDataSizeLocation = sizeof(psshPrefix) - sizeof(uint32_t);
uint32_t psshBoxSize = htonl(processedInitData.size());
uint32_t initDataSize = htonl(in_initData.size());
memcpy(&processedInitData[kPsshBoxSizeLocation], &psshBoxSize,
sizeof(uint32_t));
memcpy(&processedInitData[kInitDataSizeLocation], &initDataSize,
sizeof(uint32_t));
} else {
// For other formats, we can pass the init data through unmodified.
processedInitData.assign(reinterpret_cast<const char*>(in_initData.data()),
in_initData.size());
}
CdmAppParameterMap cdmParameters;
for (size_t i = 0; i < in_optionalParameters.size(); ++i) {
const std::string& key(in_optionalParameters[i].key);
const std::string& value(in_optionalParameters[i].value);
std::string cdmKey(key.c_str(), key.size());
std::string cdmValue(value.c_str(), value.size());
cdmParameters[cdmKey] = cdmValue;
}
// Inserting additional client ID parameters here, this will appear
// in the license request.
// Note: This will overwrite user parameters of the same key.
cdmParameters[kKeyAppPackageName] = mAppPackageName;
cdmParameters[kKeyOrigin] = mCdmIdentifierBuilder.origin();
CdmKeyRequest keyRequest;
CdmResponseType res = mCDM->GenerateKeyRequest(
cdmSessionId, cdmKeySetId, cdmInitDataType, processedInitData,
cdmLicenseType, cdmParameters, &mPropertySet, identifier, &keyRequest);
requestType = ConvertFromCdmKeyRequestType(keyRequest.type);
if (isCdmResponseTypeSuccess(res)) {
defaultUrl.clear();
defaultUrl.assign(keyRequest.url.data(), keyRequest.url.size());
request = StrToVector(keyRequest.message);
}
if (in_keyType == KeyType::RELEASE) {
// When releasing keys, we do not have a session ID.
status = mapCdmResponseType(res);
} else {
// For all other requests, we have a session ID.
status = mapAndNotifyOfCdmResponseType(in_scope, res);
}
_aidl_return->request = request;
_aidl_return->requestType = requestType;
_aidl_return->defaultUrl = defaultUrl;
return toNdkScopedAStatus(status);
}
::ndk::ScopedAStatus WVDrmPlugin::provideKeyResponse(
const vector<uint8_t>& in_scope, const vector<uint8_t>& in_response,
::aidl::android::hardware::drm::KeySetId* _aidl_return) {
if (in_scope.size() == 0 || in_response.size() == 0) {
*_aidl_return = {};
return toNdkScopedAStatus(Status::BAD_VALUE);
}
CdmKeySetId cdmKeySetId;
CdmSessionId cdmSessionId;
CdmKeyResponse cdmResponse(in_response.begin(), in_response.end());
bool isRequest = (memcmp(in_scope.data(), wvcdm::SESSION_ID_PREFIX,
sizeof(wvcdm::SESSION_ID_PREFIX) - 1) == 0);
bool isRelease = (memcmp(in_scope.data(), wvcdm::KEY_SET_ID_PREFIX,
sizeof(wvcdm::KEY_SET_ID_PREFIX) - 1) == 0);
vector<uint8_t> keySetId;
if (isRequest) {
cdmSessionId.assign(in_scope.begin(), in_scope.end());
} else if (isRelease) {
cdmKeySetId.assign(in_scope.begin(), in_scope.end());
} else {
_aidl_return->keySetId = keySetId;
return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
}
CdmResponseType res = mCDM->AddKey(cdmSessionId, cdmResponse, &cdmKeySetId);
if (isRequest && isCdmResponseTypeSuccess(res)) {
keySetId = StrToVector(cdmKeySetId);
}
WvStatus status(Status::OK);
if (isRelease) {
// When releasing keys, we do not have a session ID.
status = mapCdmResponseType(res);
} else {
// For all other requests, we have a session ID.
status = mapAndNotifyOfCdmResponseType(in_scope, res);
// For "NEED_KEY," we still want to send the notification, but then we
// don't return the error. This is because "NEED_KEY" from AddKey() is an
// expected behavior when sending a privacy certificate.
if (res == wvcdm::NEED_KEY && mPropertySet.use_privacy_mode()) {
status = WvStatus(Status::OK);
}
}
_aidl_return->keySetId = keySetId;
return toNdkScopedAStatus(status);
}
::ndk::ScopedAStatus WVDrmPlugin::removeKeys(
const vector<uint8_t>& in_sessionId) {
if (!in_sessionId.size()) {
return toNdkScopedAStatus(Status::BAD_VALUE);
}
CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end());
CdmResponseType res = mCDM->RemoveKeys(cdmSessionId);
return toNdkScopedAStatus(mapAndNotifyOfCdmResponseType(in_sessionId, res));
}
::ndk::ScopedAStatus WVDrmPlugin::restoreKeys(
const vector<uint8_t>& in_sessionId,
const ::aidl::android::hardware::drm::KeySetId& in_keySetId) {
if (!in_sessionId.size() || !in_keySetId.keySetId.size()) {
return toNdkScopedAStatus(Status::BAD_VALUE);
}
CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end());
CdmKeySetId cdmKeySetId(in_keySetId.keySetId.begin(),
in_keySetId.keySetId.end());
CdmResponseType res = mCDM->RestoreKey(cdmSessionId, cdmKeySetId);
return toNdkScopedAStatus(mapAndNotifyOfCdmResponseType(in_sessionId, res));
}
::ndk::ScopedAStatus WVDrmPlugin::queryKeyStatus(
const vector<uint8_t>& in_sessionId,
vector<::aidl::android::hardware::drm::KeyValue>* _aidl_return) {
if (in_sessionId.size() == 0) {
*_aidl_return = vector<KeyValue>();
return toNdkScopedAStatus(Status::BAD_VALUE);
}
CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end());
CdmQueryMap cdmLicenseInfo;
CdmResponseType res = mCDM->QueryKeyStatus(cdmSessionId, &cdmLicenseInfo);
vector<KeyValue> infoMapVec;
if (isCdmResponseTypeSuccess(res)) {
infoMapVec.clear();
KeyValue keyValuePair;
for (CdmQueryMap::const_iterator iter = cdmLicenseInfo.begin();
iter != cdmLicenseInfo.end(); ++iter) {
const std::string& cdmKey = iter->first;
const std::string& cdmValue = iter->second;
keyValuePair.key = std::string(cdmKey.data(), cdmKey.size());
keyValuePair.value = std::string(cdmValue.data(), cdmValue.size());
infoMapVec.push_back(keyValuePair);
}
}
*_aidl_return = infoMapVec;
return toNdkScopedAStatus(mapCdmResponseType(res));
}
::ndk::ScopedAStatus WVDrmPlugin::getProvisionRequest(
const std::string& in_certificateType,
const std::string& in_certificateAuthority,
::aidl::android::hardware::drm::ProvisionRequest* _aidl_return) {
WvStatus status(Status::OK);
std::string defaultUrl;
vector<uint8_t> request;
if (mPropertySet.use_atsc_mode()) {
_aidl_return->defaultUrl = defaultUrl;
_aidl_return->request = request;
return toNdkScopedAStatus(
mapCdmResponseType(wvcdm::PROVISIONING_NOT_ALLOWED_FOR_ATSC));
}
CdmIdentifier identifier;
status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
if (status != Status::OK) {
_aidl_return->defaultUrl = defaultUrl;
_aidl_return->request = request;
return toNdkScopedAStatus(status);
}
CdmProvisioningRequest cdmProvisionRequest;
std::string cdmDefaultUrl;
CdmCertificateType cdmCertType = wvcdm::kCertificateWidevine;
if (in_certificateType == "X.509") {
cdmCertType = wvcdm::kCertificateX509;
}
std::string cdmCertAuthority = in_certificateAuthority;
CdmResponseType res = mCDM->GetProvisioningRequest(
cdmCertType, cdmCertAuthority, identifier,
mProvisioningServiceCertificate, getRequestedSecurityLevel(),
&cdmProvisionRequest, &cdmDefaultUrl);
if (isCdmResponseTypeSuccess(res)) {
request = StrToVector(cdmProvisionRequest);
defaultUrl.clear();
defaultUrl.assign(cdmDefaultUrl.data(), cdmDefaultUrl.size());
}
_aidl_return->defaultUrl = defaultUrl;
_aidl_return->request = request;
return toNdkScopedAStatus(mapCdmResponseType(res));
}
::ndk::ScopedAStatus WVDrmPlugin::provideProvisionResponse(
const vector<uint8_t>& in_response,
::aidl::android::hardware::drm::ProvideProvisionResponseResult*
_aidl_return) {
if (!in_response.size()) {
_aidl_return->certificate = vector<uint8_t>();
_aidl_return->wrappedKey = vector<uint8_t>();
return toNdkScopedAStatus(Status::BAD_VALUE);
}
vector<uint8_t> certificate;
vector<uint8_t> wrappedKey;
CdmIdentifier identifier;
auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
if (status != Status::OK) {
_aidl_return->certificate = certificate;
_aidl_return->wrappedKey = wrappedKey;
return toNdkScopedAStatus(status);
}
CdmProvisioningResponse cdmResponse(in_response.begin(), in_response.end());
if (cdmResponse == kSpecialUnprovisionResponse) {
if (identifier.IsEquivalentToDefault()) {
ALOGW("Returns UNKNOWN error for legacy status kErrorNoOriginSpecified");
_aidl_return->certificate = certificate;
_aidl_return->wrappedKey = wrappedKey;
return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN);
}
_aidl_return->certificate = certificate;
_aidl_return->wrappedKey = wrappedKey;
return toNdkScopedAStatus(unprovision(identifier));
} else {
std::string cdmCertificate;
std::string cdmWrappedKey;
CdmResponseType res = mCDM->HandleProvisioningResponse(
identifier, cdmResponse, getRequestedSecurityLevel(), &cdmCertificate,
&cdmWrappedKey);
if (isCdmResponseTypeSuccess(res)) {
certificate = StrToVector(cdmCertificate);
wrappedKey = StrToVector(cdmWrappedKey);
}
_aidl_return->certificate = certificate;
_aidl_return->wrappedKey = wrappedKey;
return toNdkScopedAStatus(mapCdmResponseType(res));
}
}
Status WVDrmPlugin::unprovisionDevice() {
return unprovision(kDefaultCdmIdentifier).get();
}
::ndk::ScopedAStatus WVDrmPlugin::getSecureStop(
const ::aidl::android::hardware::drm::SecureStopId& in_secureStopId,
::aidl::android::hardware::drm::SecureStop* _aidl_return) {
*_aidl_return = SecureStop();
if (in_secureStopId.secureStopId.empty()) {
return toNdkScopedAStatus(Status::BAD_VALUE);
}
CdmIdentifier identifier;
auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
if (status != Status::OK) {
return toNdkScopedAStatus(status);
}
const CdmSecureStopId cdmSsId(in_secureStopId.secureStopId.begin(),
in_secureStopId.secureStopId.end());
CdmUsageReport cdmUsageReport;
CdmResponseType res = mCDM->GetUsageInfo(mPropertySet.app_id(), cdmSsId,
identifier, &cdmUsageReport);
SecureStop secureStop;
if (isCdmResponseTypeSuccess(res)) {
secureStop.opaqueData = StrToVector(cdmUsageReport);
*_aidl_return = std::move(secureStop);
}
return toNdkScopedAStatus(mapCdmResponseType(res));
}
::ndk::ScopedAStatus WVDrmPlugin::getSecureStops(
vector<::aidl::android::hardware::drm::SecureStop>* _aidl_return) {
_aidl_return->clear();
CdmIdentifier identifier;
auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
if (status != Status::OK) {
return toNdkScopedAStatus(status);
}
CdmUsageReportList cdmUsageReports;
const CdmResponseType res =
mCDM->GetUsageInfo(mPropertySet.app_id(), identifier, &cdmUsageReports);
vector<SecureStop> secureStops;
if (isCdmResponseTypeSuccess(res)) {
for (const CdmUsageReport& cdmUsageReport : cdmUsageReports) {
SecureStop secureStop;
secureStop.opaqueData = StrToVector(cdmUsageReport);
secureStops.push_back(std::move(secureStop));
}
}
*_aidl_return = std::move(secureStops);
return toNdkScopedAStatus(mapCdmResponseType(res));
}
::ndk::ScopedAStatus WVDrmPlugin::releaseAllSecureStops() {
const auto res = removeAllSecureStops();
Status status = Status::OK;
if (!res.isOk() && res.getExceptionCode() == EX_SERVICE_SPECIFIC) {
status = static_cast<Status>(res.getServiceSpecificError());
}
return toNdkScopedAStatus(status);
}
::ndk::ScopedAStatus WVDrmPlugin::releaseSecureStop(
const ::aidl::android::hardware::drm::SecureStopId& in_secureStopId) {
if (!in_secureStopId.secureStopId.size()) {
return toNdkScopedAStatus(Status::BAD_VALUE);
}
CdmIdentifier identifier;
auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
if (status != Status::OK) {
return toNdkScopedAStatus(status);
}
const CdmKeyResponse cdmMessage(in_secureStopId.secureStopId.begin(),
in_secureStopId.secureStopId.end());
CdmResponseType res = mCDM->ReleaseUsageInfo(cdmMessage, identifier);
return toNdkScopedAStatus(mapCdmResponseType(res));
}
::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) {
return toNdkScopedAStatus(status);
}
drm_metrics::WvCdmMetrics proto_metrics;
const CdmResponseType result = mCDM->GetCurrentMetrics(identifier, &proto_metrics);
if (result != wvcdm::NO_ERROR) {
return toNdkScopedAStatus(mapCdmResponseType(result));
}
vector<DrmMetricGroup> wvMetrics;
::wvcdm::WvMetricsAdapter adapter;
::wvcdm::WvMetricsAdapter::ToWvMetrics(proto_metrics, &wvMetrics);
*_aidl_return = std::move(wvMetrics);
return toNdkScopedAStatus(Status::OK);
}
::ndk::ScopedAStatus WVDrmPlugin::getSecureStopIds(
vector<::aidl::android::hardware::drm::SecureStopId>* _aidl_return) {
vector<SecureStopId> secureStopIds;
CdmIdentifier identifier;
auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
if (status != Status::OK) {
*_aidl_return = secureStopIds;
return toNdkScopedAStatus(status);
}
vector<CdmSecureStopId> ssids;
CdmResponseType res =
mCDM->GetSecureStopIds(mPropertySet.app_id(), identifier, &ssids);
if (isCdmResponseTypeSuccess(res)) {
for (auto itr = ssids.begin(); itr != ssids.end(); ++itr) {
const CdmSecureStopId& cdmSsid = *itr;
SecureStopId ssid;
ssid.secureStopId = StrToVector(cdmSsid);
secureStopIds.push_back(ssid);
}
}
*_aidl_return = secureStopIds;
return toNdkScopedAStatus(mapCdmResponseType(res));
}
::ndk::ScopedAStatus WVDrmPlugin::releaseSecureStops(
const ::aidl::android::hardware::drm::OpaqueData& in_ssRelease) {
if (in_ssRelease.opaqueData.size() == 0) {
return toNdkScopedAStatus(Status::BAD_VALUE);
}
CdmIdentifier identifier;
auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
if (status != Status::OK) {
return toNdkScopedAStatus(status);
}
const vector<uint8_t>& data = in_ssRelease.opaqueData;
const CdmKeyResponse cdmMessage(data.begin(), data.end());
// Only releases a single secure stop.
const CdmResponseType res = mCDM->ReleaseUsageInfo(cdmMessage, identifier);
return toNdkScopedAStatus(mapCdmResponseType(res));
}
::ndk::ScopedAStatus WVDrmPlugin::removeSecureStop(
const ::aidl::android::hardware::drm::SecureStopId& in_secureStopId) {
if (!in_secureStopId.secureStopId.size()) {
return toNdkScopedAStatus(Status::BAD_VALUE);
}
CdmIdentifier identifier;
auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
if (status != Status::OK) {
return toNdkScopedAStatus(status);
}
const CdmSecureStopId ssid(in_secureStopId.secureStopId.begin(),
in_secureStopId.secureStopId.end());
const CdmResponseType res =
mCDM->RemoveUsageInfo(mPropertySet.app_id(), identifier, ssid);
return toNdkScopedAStatus(mapCdmResponseType(res));
}
::ndk::ScopedAStatus WVDrmPlugin::removeAllSecureStops() {
CdmIdentifier identifier;
auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
if (status != Status::OK) {
return toNdkScopedAStatus(status);
}
const CdmResponseType res =
mCDM->RemoveAllUsageInfo(mPropertySet.app_id(), identifier);
return toNdkScopedAStatus(mapCdmResponseType(res));
}
::ndk::ScopedAStatus WVDrmPlugin::getHdcpLevels(
::aidl::android::hardware::drm::HdcpLevels* _aidl_return) {
HdcpLevel connectedLevel = HdcpLevel::HDCP_NONE;
HdcpLevel maxLevel = HdcpLevel::HDCP_NO_OUTPUT;
std::string level;
auto status = queryProperty(wvcdm::QUERY_KEY_CURRENT_HDCP_LEVEL, level);
if (status == Status::OK) {
connectedLevel = mapHdcpLevel(level);
} else {
ALOGE("Failed to query current hdcp level.");
_aidl_return->connectedLevel = connectedLevel;
_aidl_return->maxLevel = maxLevel;
return toNdkScopedAStatus(Status::ERROR_DRM_INVALID_STATE);
}
status = queryProperty(wvcdm::QUERY_KEY_MAX_HDCP_LEVEL, level);
if (status == Status::OK) {
maxLevel = mapHdcpLevel(level);
} else {
ALOGE("Failed to query maximum hdcp level.");
_aidl_return->connectedLevel = connectedLevel;
_aidl_return->maxLevel = maxLevel;
return toNdkScopedAStatus(Status::ERROR_DRM_INVALID_STATE);
}
_aidl_return->connectedLevel = connectedLevel;
_aidl_return->maxLevel = maxLevel;
return toNdkScopedAStatus(Status::OK);
}
::ndk::ScopedAStatus WVDrmPlugin::getNumberOfSessions(
::aidl::android::hardware::drm::NumberOfSessions* _aidl_return) {
uint32_t currentSessions = 0;
uint32_t maxSessions = 1;
std::string value;
auto status = queryProperty(wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS, value);
if (status == Status::OK) {
currentSessions = std::strtoul(value.c_str(), nullptr, 10);
} else {
ALOGE("Failed to query currently opened sessions.");
_aidl_return->currentSessions = currentSessions;
_aidl_return->maxSessions = maxSessions;
return toNdkScopedAStatus(Status::ERROR_DRM_INVALID_STATE);
}
status = queryProperty(wvcdm::QUERY_KEY_MAX_NUMBER_OF_SESSIONS, value);
if (status == Status::OK) {
maxSessions = std::strtoul(value.c_str(), nullptr, 10);
} else {
ALOGE(
"Failed to query maximum number of sessions that the device can "
"support.");
_aidl_return->currentSessions = currentSessions;
_aidl_return->maxSessions = maxSessions;
return toNdkScopedAStatus(Status::ERROR_DRM_INVALID_STATE);
}
_aidl_return->currentSessions = currentSessions;
_aidl_return->maxSessions = maxSessions;
return toNdkScopedAStatus(Status::OK);
}
::ndk::ScopedAStatus WVDrmPlugin::getSecurityLevel(
const vector<uint8_t>& in_sessionId,
::aidl::android::hardware::drm::SecurityLevel* _aidl_return) {
if (in_sessionId.size() == 0) {
*_aidl_return = SecurityLevel::UNKNOWN;
return toNdkScopedAStatus(Status::BAD_VALUE);
}
CdmQueryMap info;
SecurityLevel securityLevel = SecurityLevel::UNKNOWN;
CdmResponseType status = mCDM->QuerySessionStatus(
std::string(in_sessionId.begin(), in_sessionId.end()), &info);
if (wvcdm::NO_ERROR == status) {
std::string level = info[wvcdm::QUERY_KEY_SECURITY_LEVEL];
securityLevel = mapSecurityLevel(level);
} else {
ALOGE("Failed to query security level, status=%d",
static_cast<int>(status));
}
*_aidl_return = securityLevel;
return toNdkScopedAStatus(mapCdmResponseType(status));
}
::ndk::ScopedAStatus WVDrmPlugin::getOfflineLicenseKeySetIds(
vector<::aidl::android::hardware::drm::KeySetId>* _aidl_return) {
vector<vector<uint8_t>> keySetIds;
vector<KeySetId> keySetIdsVec;
CdmIdentifier identifier;
auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
if (status != Status::OK) {
*_aidl_return = keySetIdsVec;
return toNdkScopedAStatus(status);
}
vector<CdmSecurityLevel> levels = {wvcdm::kSecurityLevelL1,
wvcdm::kSecurityLevelL3};
CdmResponseType res(wvcdm::UNKNOWN_ERROR);
for (auto level : levels) {
vector<CdmKeySetId> cdmKeySetIds;
res = mCDM->ListStoredLicenses(level, identifier, &cdmKeySetIds);
if (isCdmResponseTypeSuccess(res)) {
keySetIds.clear();
for (auto id : cdmKeySetIds) {
keySetIds.push_back(StrToVector(id));
}
KeySetId kid;
for (auto id : keySetIds) {
kid.keySetId = id;
keySetIdsVec.push_back(kid);
}
}
}
*_aidl_return = keySetIdsVec;
return toNdkScopedAStatus(mapCdmResponseType(res));
}
::ndk::ScopedAStatus WVDrmPlugin::getOfflineLicenseState(
const ::aidl::android::hardware::drm::KeySetId& in_keySetId,
::aidl::android::hardware::drm::OfflineLicenseState* _aidl_return) {
OfflineLicenseState licenseState = OfflineLicenseState::UNKNOWN;
if (!in_keySetId.keySetId.size()) {
*_aidl_return = licenseState;
return toNdkScopedAStatus(Status::BAD_VALUE);
}
CdmIdentifier identifier;
auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
if (status != Status::OK) {
*_aidl_return = licenseState;
return toNdkScopedAStatus(status);
}
CdmResponseType res = wvcdm::CdmResponseType(wvcdm::UNKNOWN_ERROR);
CdmKeySetId keySetIdStr(in_keySetId.keySetId.begin(),
in_keySetId.keySetId.end());
wvcdm::CdmOfflineLicenseState state = wvcdm::kLicenseStateUnknown;
res = mCDM->GetOfflineLicenseState(keySetIdStr, wvcdm::kSecurityLevelL1,
identifier, &state);
if (!isCdmResponseTypeSuccess(res)) {
// try L3
res = mCDM->GetOfflineLicenseState(keySetIdStr, wvcdm::kSecurityLevelL3,
identifier, &state);
if (!isCdmResponseTypeSuccess(res)) {
*_aidl_return = licenseState;
return toNdkScopedAStatus(Status::BAD_VALUE);
}
}
switch (state) {
case wvcdm::kLicenseStateActive:
licenseState = OfflineLicenseState::USABLE;
break;
case wvcdm::kLicenseStateReleasing:
licenseState = OfflineLicenseState::INACTIVE;
break;
default:
licenseState = OfflineLicenseState::UNKNOWN;
ALOGE("Return unknown offline license state for %s", keySetIdStr.c_str());
break;
}
*_aidl_return = licenseState;
return toNdkScopedAStatus(mapCdmResponseType(res));
}
::ndk::ScopedAStatus WVDrmPlugin::removeOfflineLicense(
const ::aidl::android::hardware::drm::KeySetId& in_keySetId) {
if (!in_keySetId.keySetId.size()) {
return toNdkScopedAStatus(Status::BAD_VALUE);
}
CdmIdentifier identifier;
auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
if (status != Status::OK) {
return toNdkScopedAStatus(status);
}
CdmResponseType res(wvcdm::UNKNOWN_ERROR);
res = mCDM->RemoveOfflineLicense(
std::string(in_keySetId.keySetId.begin(), in_keySetId.keySetId.end()),
wvcdm::kSecurityLevelL1, identifier);
if (!isCdmResponseTypeSuccess(res)) {
CdmResponseType res = mCDM->RemoveOfflineLicense(
std::string(in_keySetId.keySetId.begin(), in_keySetId.keySetId.end()),
wvcdm::kSecurityLevelL3, identifier);
return toNdkScopedAStatus(mapCdmResponseType(res));
}
return toNdkScopedAStatus(Status::OK);
}
::ndk::ScopedAStatus WVDrmPlugin::getPropertyString(
const std::string& in_propertyName, std::string* _aidl_return) {
WvStatus status(Status::OK);
std::string name(in_propertyName.c_str());
std::string value;
if (name == "vendor") {
#ifdef __ANDROID_APEX__
auto info = ::widevine::apex::GetApexInfo();
if (info.ok()) {
value = info->name;
} else {
auto err = info.error();
ALOGW("apex info error %d: %s", err.code().value(), err.message().c_str());
value = "Google";
}
#else
value = "Google";
#endif
} else if (name == "version") {
status = queryProperty(wvcdm::QUERY_KEY_WVCDM_VERSION, value);
} else if (name == "description") {
value = "Widevine CDM";
} else if (name == "algorithms") {
value = "AES/CBC/NoPadding,HmacSHA256";
} else if (name == "securityLevel") {
std::string requestedLevel = mPropertySet.security_level();
if (requestedLevel.length() > 0) {
value = requestedLevel.c_str();
} else {
status = queryProperty(wvcdm::QUERY_KEY_SECURITY_LEVEL, value);
}
} else if (name == "systemId") {
status = queryProperty(wvcdm::QUERY_KEY_SYSTEM_ID, value);
} else if (name == "privacyMode") {
if (mPropertySet.use_privacy_mode()) {
value = kEnable;
} else {
value = kDisable;
}
} else if (name == "sessionSharing") {
if (mPropertySet.is_session_sharing_enabled()) {
value = kEnable;
} else {
value = kDisable;
}
} else if (name == "hdcpLevel") {
status = queryProperty(wvcdm::QUERY_KEY_CURRENT_HDCP_LEVEL, value);
} else if (name == "maxHdcpLevel") {
status = queryProperty(wvcdm::QUERY_KEY_MAX_HDCP_LEVEL, value);
} else if (name == "usageReportingSupport") {
status = queryProperty(wvcdm::QUERY_KEY_USAGE_SUPPORT, value);
} else if (name == "numberOfOpenSessions") {
status = queryProperty(wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS, value);
} else if (name == "maxNumberOfSessions") {
status = queryProperty(wvcdm::QUERY_KEY_MAX_NUMBER_OF_SESSIONS, value);
} else if (name == "oemCryptoApiVersion") {
status = queryProperty(wvcdm::QUERY_KEY_OEMCRYPTO_API_VERSION, value);
} else if (name == "appId") {
value = mPropertySet.app_id().c_str();
} else if (name == "origin") {
value = mCdmIdentifierBuilder.origin().c_str();
} else if (name == "CurrentSRMVersion") {
status = queryProperty(wvcdm::QUERY_KEY_CURRENT_SRM_VERSION, value);
} else if (name == "SRMUpdateSupport") {
status = queryProperty(wvcdm::QUERY_KEY_SRM_UPDATE_SUPPORT, value);
} else if (name == "resourceRatingTier") {
status = queryProperty(wvcdm::QUERY_KEY_RESOURCE_RATING_TIER, value);
} else if (name == "oemCryptoBuildInformation") {
status = queryProperty(wvcdm::QUERY_KEY_OEMCRYPTO_BUILD_INFORMATION, value);
} else if (name == "decryptHashSupport") {
status = queryProperty(wvcdm::QUERY_KEY_DECRYPT_HASH_SUPPORT, value);
} else if (name == "decryptHashError") {
status = mapCdmResponseType(
mCDM->GetDecryptHashError(mDecryptHashSessionId, &value));
} else if (name == "maxUsageEntriesSupported") {
status = queryProperty(wvcdm::QUERY_KEY_MAX_USAGE_TABLE_ENTRIES, value);
} else if (name == "oemCryptoApiMinorVersion") {
status = queryProperty(wvcdm::QUERY_KEY_OEMCRYPTO_API_MINOR_VERSION, value);
} else if (name == "analogOutputCapabilities") {
status = queryProperty(wvcdm::QUERY_KEY_ANALOG_OUTPUT_CAPABILITIES, value);
} else if (name == "canDisableAnalogOutput") {
status = queryProperty(wvcdm::QUERY_KEY_CAN_DISABLE_ANALOG_OUTPUT, value);
} else if (name == "atscMode") {
if (mPropertySet.use_atsc_mode()) {
value = kEnable;
} else {
value = kDisable;
}
} else if (name == "watermarkingSupport") {
status = queryProperty(wvcdm::QUERY_KEY_WATERMARKING_SUPPORT, value);
} else if (name == "productionReady") {
status = queryProperty(wvcdm::QUERY_KEY_PRODUCTION_READY, value);
} else if (name == "provisioningModel") {
status = queryProperty(wvcdm::QUERY_KEY_PROVISIONING_MODEL, value);
} else {
ALOGE("App requested unknown string property %s", name.c_str());
*_aidl_return = value;
return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
}
*_aidl_return = value;
return toNdkScopedAStatus(status);
}
::ndk::ScopedAStatus WVDrmPlugin::getPropertyByteArray(
const std::string& in_propertyName, vector<uint8_t>* _aidl_return) {
WvStatus status(Status::OK);
std::string name(in_propertyName.c_str());
vector<uint8_t> value;
if (name == "deviceUniqueId") {
std::string id;
status = mCdmIdentifierBuilder.getDeviceUniqueId(&id);
if (status == Status::OK) {
value = StrToVector(id);
}
} else if (name == "provisioningUniqueId") {
std::string id;
status = mCdmIdentifierBuilder.getProvisioningUniqueId(&id);
if (status == Status::OK) {
value = StrToVector(id);
}
} else if (name == "serviceCertificate") {
value = StrToVector(mPropertySet.service_certificate());
} else if (name == "provisioningServiceCertificate") {
value = StrToVector(mProvisioningServiceCertificate);
} else if (name == "metrics") {
drm_metrics::WvCdmMetrics metrics;
// If the cdm identifier is not yet sealed, then there are no metrics
// for that cdm engine. Avoid calling getCdmIdentifier and sealing
// the identifier builder.
if (mCdmIdentifierBuilder.is_sealed()) {
CdmIdentifier identifier;
status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
if (status != Status::OK) {
ALOGE("Unexpected error retrieving cdm identifier: %d", status.get());
} else {
status =
mapCdmResponseType(mCDM->GetCurrentMetrics(identifier, &metrics));
}
}
if (status == Status::OK) {
std::string serialized_metrics;
if (!metrics.SerializeToString(&serialized_metrics)) {
status = WvStatus(Status::ERROR_DRM_UNKNOWN);
} else {
value = StrToVector(serialized_metrics);
}
}
} else {
ALOGE("App requested unknown byte array property %s", name.c_str());
status = WvStatus(Status::ERROR_DRM_CANNOT_HANDLE);
}
*_aidl_return = value;
return toNdkScopedAStatus(status);
}
::ndk::ScopedAStatus WVDrmPlugin::setPropertyString(
const std::string& in_propertyName, const std::string& in_value) {
std::string name(in_propertyName.c_str());
std::string _value(in_value.c_str());
if (name == "securityLevel") {
if (mCryptoSessions.empty()) {
if (_value == wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3.c_str()) {
mPropertySet.set_security_level(wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3);
} else if (_value == wvcdm::QUERY_VALUE_SECURITY_LEVEL_L1.c_str()) {
// We must be sure we CAN set the security level to L1.
std::string current_security_level;
auto status =
queryProperty(wvcdm::kLevelDefault, wvcdm::QUERY_KEY_SECURITY_LEVEL,
current_security_level);
if (status != Status::OK) {
return toNdkScopedAStatus(status);
}
if (current_security_level != wvcdm::QUERY_VALUE_SECURITY_LEVEL_L1) {
ALOGE("App requested L1 security on a non-L1 device.");
return toNdkScopedAStatus(Status::BAD_VALUE);
} else {
mPropertySet.set_security_level(kResetSecurityLevel);
}
} else if (_value == kResetSecurityLevel ||
_value == wvcdm::QUERY_VALUE_SECURITY_LEVEL_DEFAULT) {
mPropertySet.set_security_level(kResetSecurityLevel);
} else {
ALOGE("App requested invalid security level %s", _value.c_str());
return toNdkScopedAStatus(Status::BAD_VALUE);
}
} else {
ALOGE("App tried to change security level while sessions are open.");
ALOGW("Returns UNKNOWN error for legacy status kErrorSessionIsOpen");
return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN);
}
} else if (name == "privacyMode") {
if (_value == kEnable) {
mPropertySet.set_use_privacy_mode(true);
} else if (_value == kDisable) {
mPropertySet.set_use_privacy_mode(false);
} else {
ALOGE("App requested unknown privacy mode %s", _value.c_str());
return toNdkScopedAStatus(Status::BAD_VALUE);
}
} else if (name == "sessionSharing") {
if (mCryptoSessions.empty()) {
if (_value == kEnable) {
mPropertySet.set_is_session_sharing_enabled(true);
} else if (_value == kDisable) {
mPropertySet.set_is_session_sharing_enabled(false);
} else {
ALOGE("App requested unknown sharing type %s", _value.c_str());
return toNdkScopedAStatus(Status::BAD_VALUE);
}
} else {
ALOGE("App tried to change key sharing while sessions are open.");
ALOGW("Returns UNKNOWN error for legacy status kErrorSessionIsOpen");
return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN);
}
} else if (name == "appId") {
if (mCryptoSessions.empty()) {
mPropertySet.set_app_id(_value.c_str());
} else {
ALOGE("App tried to set the application id while sessions are opened.");
ALOGW("Returns UNKNOWN error for legacy status kErrorSessionIsOpen");
return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN);
}
} else if (name == "origin") {
if (!mCryptoSessions.empty()) {
ALOGE("App tried to set the origin while sessions are opened.");
ALOGW("Returns UNKNOWN error for legacy status kErrorSessionIsOpen");
return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN);
} else {
if (!mCdmIdentifierBuilder.set_origin(_value.c_str())) {
return toNdkScopedAStatus(Status::BAD_VALUE);
}
}
} else if (name == "debugIgnoreKeyboxCount") {
std::istringstream ss(_value);
uint32_t count = 0;
ss >> count;
if (ss.fail()) {
ALOGE("Could not parse an integer from '%s'", _value.c_str());
count = 0;
return toNdkScopedAStatus(Status::BAD_VALUE);
}
CdmResponseType res = mCDM->SetDebugIgnoreKeyboxCount(count);
return toNdkScopedAStatus(mapCdmResponseType(res));
} else if (name == "decryptHash") {
wvcdm::CdmSessionId sessionId;
CdmResponseType res = mCDM->SetDecryptHash(_value.c_str(), &sessionId);
if (wvcdm::NO_ERROR == res) mDecryptHashSessionId = sessionId;
return toNdkScopedAStatus(mapCdmResponseType(res));
} else if (name == "decryptHashSessionId") {
mDecryptHashSessionId = _value.c_str();
} else if (name == "atscMode") {
if (_value == kEnable) {
if (!mCdmIdentifierBuilder.set_use_atsc_mode(true)) {
ALOGE("Cdm identifier builder is sealed. Setting ATSC mode prohibited");
return toNdkScopedAStatus(Status::BAD_VALUE);
}
mPropertySet.set_use_atsc_mode(true);
} else if (_value == kDisable) {
if (!mCdmIdentifierBuilder.set_use_atsc_mode(false)) {
ALOGE("Cdm identifier builder is sealed. Setting ATSC mode prohibited");
return toNdkScopedAStatus(Status::BAD_VALUE);
}
mPropertySet.set_use_atsc_mode(false);
} else {
ALOGE("App requested unknown ATSC mode %s", _value.c_str());
return toNdkScopedAStatus(Status::BAD_VALUE);
}
} else if (name == "debugOtaKeyboxFallbackDuration") {
bool success = false;
if (_value == "default") {
success = mCDM->SetDefaultOtaKeyboxFallbackDurationRules();
} else if (_value == "fast") {
success = mCDM->SetFastOtaKeyboxFallbackDurationRules();
} else {
ALOGE("Unknown OTA fallback duration value %s", _value.c_str());
return toNdkScopedAStatus(Status::BAD_VALUE);
}
if (!success) {
return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN);
}
} else if (name == "storeAtscLicense") {
if (!mCdmIdentifierBuilder.is_sealed()) {
ALOGE("Cdm identifier builder is not sealed. Storing ATSC license "
"prohibited");
return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
}
if (!mPropertySet.use_atsc_mode()) {
ALOGE("ATSC mode not enabled. Storing ATSC license prohibited");
return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
}
CdmIdentifier identifier;
auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
if (status != Status::OK) {
ALOGE("Unable to get CDM Identifier = %d", status.get());
return toNdkScopedAStatus(status);
}
std::string key_set_id, license_data;
status = parseAtscLicenseData(_value, &key_set_id, &license_data);
if (status != Status::OK)
return toNdkScopedAStatus(status);
const CdmResponseType res = mCDM->StoreAtscLicense(
identifier, getRequestedSecurityLevel(), key_set_id, license_data);
return toNdkScopedAStatus(mapCdmResponseType(res));
} else {
ALOGE("App set unknown string property %s", name.c_str());
return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
}
return toNdkScopedAStatus(Status::OK);
}
::ndk::ScopedAStatus WVDrmPlugin::setPropertyByteArray(
const std::string& in_propertyName, const vector<uint8_t>& in_value) {
std::string name(in_propertyName.c_str());
vector<uint8_t> _value = in_value;
if (name == "serviceCertificate") {
std::string cert(_value.begin(), _value.end());
if (_value.empty() || mCDM->IsValidServiceCertificate(cert)) {
mPropertySet.set_service_certificate(cert);
} else {
return toNdkScopedAStatus(Status::BAD_VALUE);
}
} else if (name == "provisioningServiceCertificate") {
std::string cert(_value.begin(), _value.end());
if (_value.empty() || mCDM->IsValidServiceCertificate(cert)) {
mProvisioningServiceCertificate = cert;
} else {
return toNdkScopedAStatus(Status::BAD_VALUE);
}
} else {
ALOGE("App set unknown byte array property %s", name.c_str());
return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
}
return toNdkScopedAStatus(Status::OK);
}
::ndk::ScopedAStatus WVDrmPlugin::setCipherAlgorithm(
const vector<uint8_t>& in_sessionId, const std::string& in_algorithm) {
if (in_sessionId.size() == 0 || in_algorithm.size() == 0) {
return toNdkScopedAStatus(Status::BAD_VALUE);
}
std::string algo(in_algorithm.c_str());
CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end());
shared_ptr<CryptoSession> cryptoSession = mCryptoSessions.get(cdmSessionId);
if (cryptoSession == nullptr) {
return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED);
}
if (algo == "AES/CBC/NoPadding") {
cryptoSession->setCipherAlgorithm(OEMCrypto_AES_CBC_128_NO_PADDING);
} else {
return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
}
return toNdkScopedAStatus(Status::OK);
}
::ndk::ScopedAStatus WVDrmPlugin::setMacAlgorithm(
const vector<uint8_t>& in_sessionId, const std::string& in_algorithm) {
if (in_sessionId.size() == 0 || in_algorithm.size() == 0) {
return toNdkScopedAStatus(Status::BAD_VALUE);
}
std::string algo(in_algorithm.c_str());
CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end());
shared_ptr<CryptoSession> cryptoSession = mCryptoSessions.get(cdmSessionId);
if (cryptoSession == nullptr) {
return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED);
}
if (algo == "HmacSHA256") {
cryptoSession->setMacAlgorithm(OEMCrypto_HMAC_SHA256);
} else {
return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
}
return toNdkScopedAStatus(Status::OK);
}
::ndk::ScopedAStatus WVDrmPlugin::encrypt(const vector<uint8_t>& in_sessionId,
const vector<uint8_t>& in_keyId,
const vector<uint8_t>& in_input,
const vector<uint8_t>& in_iv,
vector<uint8_t>* _aidl_return) {
vector<uint8_t> output;
*_aidl_return = output;
CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end());
const shared_ptr<CryptoSession> cryptoSession = mCryptoSessions.get(cdmSessionId);
if (cryptoSession == nullptr) {
return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED);
}
if (cryptoSession->cipherAlgorithm() == kInvalidCryptoAlgorithm) {
ALOGW("Returns UNKNOWN error for legacy status NO_INIT");
return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN);
}
OEMCryptoResult res = mCrypto->selectKey(cryptoSession->oecSessionId(),
in_keyId.data(), in_keyId.size());
if (res != OEMCrypto_SUCCESS) {
ALOGE("OEMCrypto_SelectKey failed with %u", res);
return toNdkScopedAStatus(mapAndNotifyOfOEMCryptoResult(in_sessionId, res));
}
output.resize(in_input.size());
Status status = Status::OK;
res = mCrypto->encrypt(cryptoSession->oecSessionId(), in_input.data(),
in_input.size(), in_iv.data(),
cryptoSession->cipherAlgorithm(), output.data());
*_aidl_return = output;
if (res == OEMCrypto_SUCCESS) {
status = Status::OK;
} else {
ALOGE("OEMCrypto_Generic_Encrypt failed with %u", res);
status = mapAndNotifyOfOEMCryptoResult(in_sessionId, res);
}
return toNdkScopedAStatus(status);
}
::ndk::ScopedAStatus WVDrmPlugin::decrypt(const vector<uint8_t>& in_sessionId,
const vector<uint8_t>& in_keyId,
const vector<uint8_t>& in_input,
const vector<uint8_t>& in_iv,
vector<uint8_t>* _aidl_return) {
vector<uint8_t> output;
*_aidl_return = output;
CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end());
const shared_ptr<CryptoSession> cryptoSession = mCryptoSessions.get(cdmSessionId);
if (cryptoSession == nullptr) {
return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED);
}
if (cryptoSession->cipherAlgorithm() == kInvalidCryptoAlgorithm) {
ALOGW("Returns UNKNOWN error for legacy status NO_INIT");
return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN);
}
OEMCryptoResult res = mCrypto->selectKey(cryptoSession->oecSessionId(),
in_keyId.data(), in_keyId.size());
if (res != OEMCrypto_SUCCESS) {
ALOGE("OEMCrypto_SelectKey failed with %u", res);
return toNdkScopedAStatus(mapAndNotifyOfOEMCryptoResult(in_sessionId, res));
}
output.resize(in_input.size());
Status status = Status::OK;
res = mCrypto->decrypt(cryptoSession->oecSessionId(), in_input.data(),
in_input.size(), in_iv.data(),
cryptoSession->cipherAlgorithm(), output.data());
*_aidl_return = output;
if (res == OEMCrypto_SUCCESS) {
status = Status::OK;
} else {
ALOGE("OEMCrypto_Generic_Decrypt failed with %u", res);
status = mapAndNotifyOfOEMCryptoResult(in_sessionId, res);
}
return toNdkScopedAStatus(status);
}
::ndk::ScopedAStatus WVDrmPlugin::sign(const vector<uint8_t>& in_sessionId,
const vector<uint8_t>& in_keyId,
const vector<uint8_t>& in_message,
vector<uint8_t>* _aidl_return) {
vector<uint8_t> signature;
*_aidl_return = signature;
CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end());
const shared_ptr<CryptoSession> cryptoSession = mCryptoSessions.get(cdmSessionId);
if (cryptoSession == nullptr) {
return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED);
}
if (cryptoSession->macAlgorithm() == kInvalidCryptoAlgorithm) {
ALOGW("Returns UNKNOWN error for legacy status NO_INIT");
return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN);
}
OEMCryptoResult res = mCrypto->selectKey(cryptoSession->oecSessionId(),
in_keyId.data(), in_keyId.size());
if (res != OEMCrypto_SUCCESS) {
ALOGE("OEMCrypto_SelectKey failed with %u", res);
return toNdkScopedAStatus(mapAndNotifyOfOEMCryptoResult(in_sessionId, res));
}
size_t signatureSize = 0;
res = mCrypto->sign(cryptoSession->oecSessionId(), in_message.data(),
in_message.size(), cryptoSession->macAlgorithm(), nullptr,
&signatureSize);
Status status = Status::OK;
if (res != OEMCrypto_ERROR_SHORT_BUFFER) {
ALOGE(
"OEMCrypto_Generic_Sign failed with %u when requesting signature "
"size",
res);
if (res != OEMCrypto_SUCCESS) {
status = mapAndNotifyOfOEMCryptoResult(in_sessionId, res);
} else {
status = Status::ERROR_DRM_UNKNOWN;
}
return toNdkScopedAStatus(status);
}
signature.resize(signatureSize);
res = mCrypto->sign(cryptoSession->oecSessionId(), in_message.data(),
in_message.size(), cryptoSession->macAlgorithm(),
signature.data(), &signatureSize);
*_aidl_return = signature;
if (res == OEMCrypto_SUCCESS) {
status = Status::OK;
} else {
ALOGE("OEMCrypto_Generic_Sign failed with %u", res);
status = mapAndNotifyOfOEMCryptoResult(in_sessionId, res);
}
return toNdkScopedAStatus(status);
}
::ndk::ScopedAStatus WVDrmPlugin::verify(const vector<uint8_t>& in_sessionId,
const vector<uint8_t>& in_keyId,
const vector<uint8_t>& in_message,
const vector<uint8_t>& in_signature,
bool* _aidl_return) {
bool match = false;
*_aidl_return = match;
CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end());
const shared_ptr<CryptoSession> cryptoSession = mCryptoSessions.get(cdmSessionId);
if (cryptoSession == nullptr) {
return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED);
}
if (cryptoSession->macAlgorithm() == kInvalidCryptoAlgorithm) {
ALOGW("Returns UNKNOWN error for legacy status NO_INIT");
return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN);
}
OEMCryptoResult res = mCrypto->selectKey(cryptoSession->oecSessionId(),
in_keyId.data(), in_keyId.size());
if (res != OEMCrypto_SUCCESS) {
ALOGE("OEMCrypto_SelectKey failed with %u", res);
return toNdkScopedAStatus(mapAndNotifyOfOEMCryptoResult(in_sessionId, res));
}
res = mCrypto->verify(cryptoSession->oecSessionId(), in_message.data(),
in_message.size(), cryptoSession->macAlgorithm(),
in_signature.data(), in_signature.size());
Status status = Status::OK;
if (res == OEMCrypto_SUCCESS) {
match = true;
status = Status::OK;
} else if (res == OEMCrypto_ERROR_SIGNATURE_FAILURE) {
match = false;
status = Status::OK;
} else {
ALOGE("OEMCrypto_Generic_Verify failed with %u", res);
match = false;
status = mapAndNotifyOfOEMCryptoResult(in_sessionId, res);
}
*_aidl_return = match;
return toNdkScopedAStatus(status);
}
::ndk::ScopedAStatus WVDrmPlugin::signRSA(const vector<uint8_t>& in_sessionId,
const std::string& in_algorithm,
const vector<uint8_t>& in_message,
const vector<uint8_t>& in_wrappedkey,
vector<uint8_t>* _aidl_return) {
*_aidl_return = vector<uint8_t>();
if (in_sessionId.size() == 0 || in_algorithm.size() == 0 ||
in_message.size() == 0 || in_wrappedkey.size() == 0) {
return toNdkScopedAStatus(Status::BAD_VALUE);
}
const char* sid = AIBinder_getCallingSid();
if (!sid ||
(!strstr(sid, ":mediashell_app:") && !strstr(sid, ":mediadrmserver:") &&
!strstr(sid, ":setupwraith_app:"))) {
ALOGE(
"Only mediashell/mediadrmserver/setupwraith_app can call signRSA, "
"but actually: %s",
sid);
return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN);
}
const std::string algo(in_algorithm.c_str());
vector<uint8_t> signature;
*_aidl_return = signature;
RSA_Padding_Scheme padding_scheme;
if (algo == "RSASSA-PSS-SHA1") {
padding_scheme = kSign_RSASSA_PSS;
} else if (algo == "PKCS1-BlockType1") {
padding_scheme = kSign_PKCS1_Block1;
} else {
ALOGE("Unknown RSA Algorithm %s", algo.c_str());
return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
}
Status status = Status::OK;
OEMCryptoResult res = mCrypto->signRSA(
in_wrappedkey.data(), in_wrappedkey.size(), in_message.data(),
in_message.size(), signature, padding_scheme);
*_aidl_return = signature;
if (res != OEMCrypto_SUCCESS) {
ALOGE("OEMCrypto_GenerateRSASignature failed with %u", res);
status = mapOEMCryptoResult(res);
}
return toNdkScopedAStatus(status);
}
::ndk::ScopedAStatus WVDrmPlugin::setListener(
const shared_ptr<::aidl::android::hardware::drm::IDrmPluginListener>&
in_listener) {
mListener = in_listener;
::ndk::ScopedAStatus _aidl_status;
_aidl_status.set(AStatus_fromStatus(STATUS_OK));
return _aidl_status;
}
void WVDrmPlugin::sendEvent(
::aidl::android::hardware::drm::EventType in_eventType,
const vector<uint8_t>& in_sessionId, const vector<uint8_t>& in_data) {
::ndk::ScopedAStatus err = ::ndk::ScopedAStatus::ok();
if (mListener != nullptr) {
err = mListener->onEvent(in_eventType, in_sessionId, in_data);
} else {
ALOGE("Null event listener, event not sent");
}
if (!err.isOk()) {
ALOGW("sendEvent failed %s", err.getDescription().c_str());
}
return;
}
void WVDrmPlugin::sendExpirationUpdate(const vector<uint8_t>& in_sessionId,
int64_t in_expiryTimeInMS) {
::ndk::ScopedAStatus err = ::ndk::ScopedAStatus::ok();
if (mListener != nullptr) {
err = mListener->onExpirationUpdate(in_sessionId, in_expiryTimeInMS);
} else {
ALOGE("Null event listener, event not sent");
}
if (!err.isOk()) {
ALOGW("sendExpirationUpdate failed %s", err.getDescription().c_str());
}
return;
}
void WVDrmPlugin::sendKeysChange(
const vector<uint8_t>& in_sessionId,
const vector<::aidl::android::hardware::drm::KeyStatus>& in_keyStatusList,
bool in_hasNewUsableKey) {
::ndk::ScopedAStatus err = ::ndk::ScopedAStatus::ok();
if (mListener != nullptr) {
err = mListener->onKeysChange(in_sessionId, in_keyStatusList,
in_hasNewUsableKey);
} else {
ALOGE("Null event listener, event not sent");
}
if (!err.isOk()) {
ALOGW("sendKeysChange failed %s", err.getDescription().c_str());
}
return;
}
void WVDrmPlugin::sendSessionLostState(const vector<uint8_t>& in_sessionId) {
::ndk::ScopedAStatus err = ::ndk::ScopedAStatus::ok();
if (mListener != nullptr) {
err = mListener->onSessionLostState(in_sessionId);
} else {
ALOGE("Null event listener, event not sent");
}
if (!err.isOk()) {
ALOGW("sendSessionLostState failed %s", err.getDescription().c_str());
}
return;
}
::ndk::ScopedAStatus WVDrmPlugin::getLogMessages(
vector<::aidl::android::hardware::drm::LogMessage>* _aidl_return) {
const vector<wvutil::LogMessage>& logs(wvutil::g_logbuf.getLogs());
vector<::aidl::android::hardware::drm::LogMessage> msgs;
for (auto log : logs) {
msgs.push_back(
{log.time_ms_, toAidlLogPriority(log.priority_), log.message_});
}
*_aidl_return = msgs;
return toNdkScopedAStatus(Status::OK);
}
::ndk::ScopedAStatus WVDrmPlugin::requiresSecureDecoder(
const std::string& in_mime, SecurityLevel in_level, bool* _aidl_return) {
if (!strncasecmp(in_mime.c_str(), "video/", 6)) {
if (in_level == SecurityLevel::DEFAULT) {
std::string level(mPropertySet.security_level());
if (level == kResetSecurityLevel) {
mCDM->QueryStatus(wvcdm::kLevelDefault, wvcdm::QUERY_KEY_SECURITY_LEVEL,
&level);
}
*_aidl_return = level == wvcdm::QUERY_VALUE_SECURITY_LEVEL_L1;
} else {
// Type is video, so check level to see if we require a secure decoder.
*_aidl_return = in_level == SecurityLevel::HW_SECURE_ALL ||
in_level == SecurityLevel::HW_SECURE_DECODE;
}
} else {
// Type is not video, so never require a secure decoder.
*_aidl_return = false;
}
return ::ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus WVDrmPlugin::setPlaybackId(
const vector<uint8_t>& in_sessionId, const std::string& in_playbackId) {
CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end());
CdmResponseType res = mCDM->SetPlaybackId(cdmSessionId, in_playbackId);
return toNdkScopedAStatus(mapCdmResponseType(res));
}
void WVDrmPlugin::OnSessionRenewalNeeded(const CdmSessionId& cdmSessionId) {
const vector<uint8_t> sessionId = StrToVector(cdmSessionId);
const vector<uint8_t> data; // data is ignored
sendEvent(EventType::KEY_NEEDED, sessionId, data);
}
void WVDrmPlugin::OnSessionKeysChange(const CdmSessionId& cdmSessionId,
const CdmKeyStatusMap& cdmKeysStatus,
bool hasNewUsableKey) {
bool expired = false;
vector<KeyStatus> keyStatusList;
for (CdmKeyStatusMap::const_iterator it = cdmKeysStatus.begin();
it != cdmKeysStatus.end(); ++it) {
const KeyId& keyId = it->first;
const CdmKeyStatus cdmKeyStatus = it->second;
if (cdmKeyStatus == wvcdm::kKeyStatusExpired) expired = true;
KeyStatus keyStatus;
keyStatus.keyId = StrToVector(keyId);
keyStatus.type = ConvertFromCdmKeyStatus(cdmKeyStatus);
keyStatusList.push_back(keyStatus);
}
const vector<uint8_t> sessionId = StrToVector(cdmSessionId);
const vector<uint8_t> data; // data is ignored
sendKeysChange(sessionId, keyStatusList, hasNewUsableKey);
// For backward compatibility.
if (expired) {
sendEvent(EventType::KEY_EXPIRED, sessionId, data);
}
}
void WVDrmPlugin::OnExpirationUpdate(const CdmSessionId& cdmSessionId,
int64_t newExpiryTimeSeconds) {
const vector<uint8_t> sessionId = StrToVector(cdmSessionId);
int64_t newExpiryTimeMilliseconds =
newExpiryTimeSeconds == wvcdm::NEVER_EXPIRES
? newExpiryTimeSeconds
: newExpiryTimeSeconds * 1000;
sendExpirationUpdate(sessionId, newExpiryTimeMilliseconds);
}
void WVDrmPlugin::OnSessionLostState(const CdmSessionId& cdmSessionId) {
const vector<uint8_t> sessionId = StrToVector(cdmSessionId);
sendSessionLostState(sessionId);
}
WvStatus WVDrmPlugin::queryProperty(const std::string& property,
std::string& stringValue) const {
return queryProperty(getRequestedSecurityLevel(), property, stringValue);
}
WvStatus WVDrmPlugin::queryProperty(RequestedSecurityLevel securityLevel,
const std::string& property,
std::string& stringValue) const {
CdmResponseType res =
mCDM->QueryStatus(securityLevel, property, &stringValue);
if (res != wvcdm::NO_ERROR) {
ALOGE("Error querying CDM status: %d", static_cast<int>(res));
}
return mapCdmResponseType(res);
}
::ndk::SpAIBinder WVDrmPlugin::createBinder() {
auto binder = BnDrmPlugin::createBinder();
AIBinder_setRequestingSid(binder.get(), true);
return binder;
}
WvStatus WVDrmPlugin::queryProperty(const std::string& property,
vector<uint8_t>& vector_value) const {
std::string string_value;
auto status = queryProperty(property, string_value);
if (status != Status::OK) return status;
vector_value = StrToVector(string_value);
return WvStatus(Status::OK);
}
bool WVDrmPlugin::isProvisioned(wvcdm::CdmSecurityLevel securityLevel,
const std::string& origin,
const std::string& spoid,
bool atsc_mode_enabled) const {
return mCDM->IsProvisioned(securityLevel, origin, spoid, atsc_mode_enabled);
}
WvStatus WVDrmPlugin::parseAtscLicenseData(
const std::string& in_value,
std::string* key_set_id,
std::string* serialized_license_data) {
if (key_set_id == nullptr) {
ALOGE("key_set_id null");
return WvStatus(Status::ERROR_DRM_CANNOT_HANDLE);
}
if (serialized_license_data == nullptr) {
ALOGE("serialized_license_data null");
return WvStatus(Status::ERROR_DRM_CANNOT_HANDLE);
}
if (in_value.compare(0,
strlen(wvcdm::ATSC_KEY_SET_ID_PREFIX),
&wvcdm::ATSC_KEY_SET_ID_PREFIX[0]) != 0) {
ALOGE("ATSC license input does not conform to expectations. Key set does "
"not have a valid ATSC Key set prefix %s", in_value.c_str());
return WvStatus(Status::BAD_VALUE);
}
const char kColon = ':';
const size_t pos = in_value.find(kColon);
if (pos == std::string::npos) {
ALOGE("ATSC license input does not conform to expectations. Missing colon "
"= %s", in_value.c_str());
return WvStatus(Status::BAD_VALUE);
}
if (pos == in_value.length()) {
ALOGE("ATSC license input does not conform to expectations. No data after "
"colon");
return WvStatus(Status::BAD_VALUE);
}
*key_set_id = in_value.substr(0, pos);
const std::vector<uint8_t> license_data_binary =
wvutil::Base64Decode(in_value.substr(pos+1));
if (license_data_binary.empty()) {
ALOGE("ATSC license input does not conform to expectations. License data "
"failed to decode from Base64");
return WvStatus(Status::BAD_VALUE);
}
serialized_license_data->assign(license_data_binary.begin(),
license_data_binary.end());
return WvStatus(Status::OK);
}
WvStatus WVDrmPlugin::mapAndNotifyOfCdmResponseType(
const vector<uint8_t>& sessionId, CdmResponseType res) {
notifyOfCdmResponseType(sessionId, res);
return mapCdmResponseType(res);
}
void WVDrmPlugin::notifyOfCdmResponseType(const vector<uint8_t>& sessionId,
CdmResponseType res) {
const vector<uint8_t> data; // data is ignored
if (res == wvcdm::NEED_PROVISIONING) {
sendEvent(EventType::PROVISION_REQUIRED, sessionId, data);
} else if (res == wvcdm::NEED_KEY) {
sendEvent(EventType::KEY_NEEDED, sessionId, data);
}
}
Status WVDrmPlugin::mapAndNotifyOfOEMCryptoResult(
const vector<uint8_t>& sessionId, OEMCryptoResult res) {
const vector<uint8_t> data; // data is ignored
if (res == OEMCrypto_ERROR_NO_DEVICE_KEY) {
sendEvent(EventType::PROVISION_REQUIRED, sessionId, data);
}
return mapOEMCryptoResult(res);
}
Status WVDrmPlugin::mapOEMCryptoResult(OEMCryptoResult res) {
switch (res) {
case OEMCrypto_SUCCESS:
return Status::OK;
case OEMCrypto_ERROR_SIGNATURE_FAILURE:
return Status::ERROR_DRM_INVALID_STATE;
case OEMCrypto_ERROR_NO_DEVICE_KEY:
return Status::ERROR_DRM_NOT_PROVISIONED;
case OEMCrypto_ERROR_INVALID_SESSION:
return Status::ERROR_DRM_SESSION_NOT_OPENED;
case OEMCrypto_ERROR_TOO_MANY_SESSIONS:
case OEMCrypto_ERROR_INSUFFICIENT_RESOURCES:
return Status::ERROR_DRM_RESOURCE_BUSY;
case OEMCrypto_ERROR_NOT_IMPLEMENTED:
return Status::ERROR_DRM_CANNOT_HANDLE;
case OEMCrypto_ERROR_INVALID_RSA_KEY:
case OEMCrypto_ERROR_SHORT_BUFFER:
case OEMCrypto_ERROR_UNKNOWN_FAILURE:
case OEMCrypto_ERROR_OPEN_SESSION_FAILED:
FALLTHROUGH_INTENDED; /* FALLTHROUGH */
default:
ALOGW("Returns UNKNOWN error for legacy status: %d", res);
return static_cast<Status>(Status::GENERAL_OEM_ERROR);
}
}
RequestedSecurityLevel WVDrmPlugin::getRequestedSecurityLevel() const {
return mPropertySet.security_level().compare(
wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3) == 0
? wvcdm::kLevel3
: wvcdm::kLevelDefault;
}
bool WVDrmPlugin::initDataResemblesPSSH(const vector<uint8_t>& initData) {
const uint8_t* const initDataArray = initData.data();
if (sizeof(uint32_t) + kPsshTag.size() > initData.size()) {
// The init data is so small that it couldn't contain a size and PSSH tag.
return false;
}
// Extract the size field
const uint8_t* const sizeField = &initDataArray[0];
uint32_t nboSize;
memcpy(&nboSize, sizeField, sizeof(nboSize));
uint32_t size = ntohl(nboSize);
if (size > initData.size()) {
return false;
}
// Extract the ID field
const char* const idField =
reinterpret_cast<const char* const>(&initDataArray[sizeof(nboSize)]);
std::string id(idField, kPsshTag.size());
return id == kPsshTag;
}
WvStatus WVDrmPlugin::unprovision(const CdmIdentifier& identifier) {
if (mPropertySet.use_atsc_mode())
return mapCdmResponseType(wvcdm::PROVISIONING_NOT_ALLOWED_FOR_ATSC);
CdmResponseType res1 = mCDM->Unprovision(wvcdm::kSecurityLevelL1, identifier);
CdmResponseType res3 = mCDM->Unprovision(wvcdm::kSecurityLevelL3, identifier);
if (!isCdmResponseTypeSuccess(res1)) {
return mapCdmResponseType(res1);
} else {
return mapCdmResponseType(res3);
}
}
// Implementation for the CdmIdentifierBuilder inner class
WVDrmPlugin::CdmIdentifierBuilder::CdmIdentifierBuilder(
bool useSpoid, const WVDrmPlugin& parent, const std::string& appPackageName)
: mCdmIdentifier(),
mIsIdentifierSealed(false),
mUseSpoid(useSpoid),
mAppPackageName(appPackageName),
mParent(parent) {
mCdmIdentifier.app_package_name = mAppPackageName;
mCdmIdentifier.unique_id = getNextUniqueId();
mCdmIdentifier.user_id = wvutil::GetIpcCallingUid();
}
WvStatus WVDrmPlugin::CdmIdentifierBuilder::getCdmIdentifier(
CdmIdentifier* identifier) {
if (!mIsIdentifierSealed) {
auto res = calculateSpoid();
if (res != Status::OK) return res;
mIsIdentifierSealed = true;
}
*identifier = mCdmIdentifier;
return WvStatus(Status::OK);
}
WvStatus WVDrmPlugin::CdmIdentifierBuilder::getDeviceUniqueId(std::string* id) {
if (mUseSpoid) {
CdmIdentifier identifier;
auto res = getCdmIdentifier(&identifier);
if (res != Status::OK) return res;
*id = identifier.spoid;
return WvStatus(Status::OK);
} else {
return getOemcryptoDeviceId(id);
}
}
WvStatus WVDrmPlugin::CdmIdentifierBuilder::getProvisioningUniqueId(
std::string* id) {
if (mUseSpoid) {
// To fake a provisioning-unique ID on SPOID devices where we can't expose
// the real provisioning-unique ID, we just use the SPOID and invert all
// the bits.
auto res = getDeviceUniqueId(id);
if (res != Status::OK) return res;
for (char& c : *id) {
c = ~c;
}
return WvStatus(Status::OK);
} else {
return mParent.queryProperty(wvcdm::QUERY_KEY_PROVISIONING_ID, *id);
}
}
bool WVDrmPlugin::CdmIdentifierBuilder::set_origin(const std::string& id) {
if (mIsIdentifierSealed) return false;
mCdmIdentifier.origin = id;
return true;
}
bool WVDrmPlugin::CdmIdentifierBuilder::set_use_atsc_mode(bool enable) {
if (is_sealed()) return false;
mCdmIdentifier.app_package_name =
enable ? wvcdm::ATSC_APP_PACKAGE_NAME : mAppPackageName;
return true;
}
WvStatus WVDrmPlugin::CdmIdentifierBuilder::calculateSpoid() {
if (!mUseSpoid) return WvStatus(Status::OK);
// Calculate SPOID for default security level if appropriate
std::string deviceId;
if (mParent.getRequestedSecurityLevel() == wvcdm::kLevelDefault) {
auto res = getOemcryptoDeviceId(&deviceId);
if (res != Status::OK) return res;
return WvStatus(calculateSpoid(deviceId, &mCdmIdentifier.spoid));
}
// If requested security level is L3, possibilities are
// (a) L3 has not been provisioned
// (b) L3 was provisioned with L3 device ID in the CdmIdentifier
// (c) L3 was provisioned (incorrectly) with L1 device ID in the
// CdmIdentifier Check (b) first. Get L3 device ID, calculate SPOID and if
// provisioned with this SPOID, return this SPOID. Check (c) next. Get L1
// device ID, calculate SPOID and if provisioned with this SPOID, return
// this SPOID. On any errors in (c) or not provisioned return L3 SPOID.
auto res = getOemcryptoDeviceId(wvcdm::kLevel3, &deviceId);
if (res != Status::OK) return res;
std::string spoidL3;
auto status = calculateSpoid(deviceId, &spoidL3);
if (status != Status::OK) return WvStatus(status);
bool atsc_mode_enabled =
mCdmIdentifier.app_package_name == wvcdm::ATSC_APP_PACKAGE_NAME;
if (mParent.isProvisioned(wvcdm::kSecurityLevelL3, origin(), spoidL3,
atsc_mode_enabled)) {
mCdmIdentifier.spoid = spoidL3;
return WvStatus(Status::OK);
}
// Not provisioned with CdmIdentifier containing SPOID with L3 device ID.
// Try SPOID with L1 device ID.
std::string deviceIdLevelDefault;
res = getOemcryptoDeviceId(wvcdm::kLevelDefault, &deviceIdLevelDefault);
if (res != Status::OK) {
mCdmIdentifier.spoid = spoidL3;
return WvStatus(Status::OK);
}
// If the L3 and default security level IDs are identical then the
// device does not support L1.
if (deviceId == deviceIdLevelDefault) {
mCdmIdentifier.spoid = spoidL3;
return WvStatus(Status::OK);
}
std::string spoidLevelDefault;
status = calculateSpoid(deviceIdLevelDefault, &spoidLevelDefault);
if (status != Status::OK) {
mCdmIdentifier.spoid = spoidL3;
return WvStatus(Status::OK);
}
if (mParent.isProvisioned(wvcdm::kSecurityLevelL1, origin(),
spoidLevelDefault, atsc_mode_enabled)) {
mCdmIdentifier.spoid = spoidLevelDefault;
return WvStatus(Status::OK);
}
// Not provisioned with CdmIdentifier containing SPOID with L1 or L3
// device ID. Return L3 SPOID.
mCdmIdentifier.spoid = spoidL3;
return WvStatus(Status::OK);
}
Status WVDrmPlugin::CdmIdentifierBuilder::calculateSpoid(
const std::string& deviceId, std::string* spoid) {
if (spoid == nullptr) return Status::ERROR_DRM_CANNOT_HANDLE;
if (!mUseSpoid) {
spoid->clear();
return Status::OK;
}
uint8_t hash[SHA256_DIGEST_LENGTH];
SHA256_CTX ctx;
SHA256_Init(&ctx);
SHA256_Update(&ctx, deviceId.data(), deviceId.length());
SHA256_Update(&ctx, mAppPackageName.data(), mAppPackageName.length());
SHA256_Update(&ctx, origin().data(), origin().length());
SHA256_Final(hash, &ctx);
*spoid = std::string(reinterpret_cast<char*>(hash), SHA256_DIGEST_LENGTH);
return Status::OK;
}
WvStatus WVDrmPlugin::CdmIdentifierBuilder::getOemcryptoDeviceId(
std::string* id) {
return mParent.queryProperty(wvcdm::QUERY_KEY_DEVICE_ID, *id);
}
WvStatus WVDrmPlugin::CdmIdentifierBuilder::getOemcryptoDeviceId(
wvcdm::RequestedSecurityLevel securityLevel, std::string* id) {
return mParent.queryProperty(securityLevel, wvcdm::QUERY_KEY_DEVICE_ID, *id);
}
uint32_t WVDrmPlugin::CdmIdentifierBuilder::getNextUniqueId() {
// Start with 1. 0 is reserved for the default cdm identifier.
static uint32_t unique_id = 1;
return ++unique_id;
}
} // namespace widevine
} // namespace drm
} // namespace hardware
} // namespace wvdrm