[ Merged from http://go/wvgerrit/151169 ] Test: Netflix and Google TV streaming and downloaded playback Test: unit tests Test: atest VtsAidlHalDrmTargetTest Test: adb shell readelf -d /vendor/bin/hw/android.hardware.drm-service.widevine Test: adb shell readelf -d /vendor/lib64/libwvaidl.so Bug: 230791937 Change-Id: Ia60d9fc838bf228b40d99b076a837ae789fa2d03
2149 lines
75 KiB
C++
2149 lines
75 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 "Utils.h"
|
|
#include "android-base/macros.h"
|
|
#include "log.h"
|
|
#include "mapErrors-inl.h"
|
|
#include "media/stagefright/MediaErrors.h"
|
|
#include "openssl/sha.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::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::CdmUsageInfo;
|
|
using wvcdm::CdmUsageInfoReleaseMessage;
|
|
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, CryptoSession>::iterator mapIterator;
|
|
for (mapIterator iter = mCryptoSessions.begin();
|
|
iter != mCryptoSessions.end(); ++iter) {
|
|
CdmResponseType res = mCDM->CloseSession(iter->first);
|
|
if (!isCdmResponseTypeSuccess(res)) {
|
|
ALOGE("Failed to close session while destroying WVDrmPlugin");
|
|
}
|
|
}
|
|
mCryptoSessions.clear();
|
|
if (mCdmIdentifierBuilder.is_sealed()) {
|
|
CdmIdentifier identifier;
|
|
Status status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
|
|
if (status != Status::OK) {
|
|
ALOGE("Failed to get cdm identifier %d", status);
|
|
} else {
|
|
status = mapCdmResponseType(mCDM->CloseCdm(identifier));
|
|
if (status != Status::OK) {
|
|
ALOGE("Failed to close cdm. status %d", status);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Status WVDrmPlugin::openSessionCommon(vector<uint8_t>& sessionId) {
|
|
Status 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)) {
|
|
status = mapAndNotifyOfCdmResponseType(sessionId, res);
|
|
return status;
|
|
}
|
|
|
|
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[cdmSessionId] = CryptoSession(oecSessionId);
|
|
success = true;
|
|
} else {
|
|
ALOGE("Unable to query key control info.");
|
|
}
|
|
|
|
if (success) {
|
|
// Marshal Session ID
|
|
sessionId = StrToVector(cdmSessionId);
|
|
return Status::OK;
|
|
} else {
|
|
mCDM->CloseSession(cdmSessionId);
|
|
|
|
if (!isCdmResponseTypeSuccess(res)) {
|
|
// We got an error code we can return.
|
|
status = 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");
|
|
status = Status::ERROR_DRM_UNKNOWN;
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
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;
|
|
Status status =
|
|
queryProperty(wvcdm::kLevelDefault, wvcdm::QUERY_KEY_SECURITY_LEVEL,
|
|
native_security_level);
|
|
if (Status::OK != status) {
|
|
*_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::OK == status) {
|
|
SecurityLevel currentSecurityLevel = SecurityLevel::UNKNOWN;
|
|
const auto ret = getSecurityLevel(sessionId, ¤tSecurityLevel);
|
|
if (!ret.isOk() || in_securityLevel != currentSecurityLevel) {
|
|
ALOGE("Failed to open session with the requested security level=%d",
|
|
in_securityLevel);
|
|
closeSession(sessionId);
|
|
sessionId.clear();
|
|
status = 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;
|
|
Status status = Status::OK;
|
|
std::string defaultUrl;
|
|
vector<uint8_t> request;
|
|
|
|
CdmIdentifier identifier;
|
|
status =
|
|
static_cast<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<Status>(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);
|
|
}
|
|
|
|
Status 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 = 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) {
|
|
Status 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;
|
|
Status 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);
|
|
}
|
|
|
|
::ndk::ScopedAStatus WVDrmPlugin::getSecureStop(
|
|
const ::aidl::android::hardware::drm::SecureStopId& in_secureStopId,
|
|
::aidl::android::hardware::drm::SecureStop* _aidl_return) {
|
|
if (!in_secureStopId.secureStopId.size()) {
|
|
*_aidl_return = SecureStop();
|
|
return toNdkScopedAStatus(Status::BAD_VALUE);
|
|
}
|
|
|
|
vector<uint8_t> cdmStopVec;
|
|
SecureStop secureStop;
|
|
|
|
CdmIdentifier identifier;
|
|
Status status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
|
|
if (status != Status::OK) {
|
|
*_aidl_return = SecureStop();
|
|
return toNdkScopedAStatus(status);
|
|
}
|
|
|
|
CdmUsageInfo cdmUsageInfo;
|
|
CdmSecureStopId cdmSsId(in_secureStopId.secureStopId.begin(),
|
|
in_secureStopId.secureStopId.end());
|
|
CdmResponseType res = mCDM->GetUsageInfo(mPropertySet.app_id(), cdmSsId,
|
|
identifier, &cdmUsageInfo);
|
|
|
|
if (isCdmResponseTypeSuccess(res)) {
|
|
for (CdmUsageInfo::const_iterator iter = cdmUsageInfo.begin();
|
|
iter != cdmUsageInfo.end(); ++iter) {
|
|
const std::string& cdmStop = *iter;
|
|
cdmStopVec = StrToVector(cdmStop);
|
|
}
|
|
secureStop.opaqueData = cdmStopVec;
|
|
}
|
|
|
|
*_aidl_return = secureStop;
|
|
return toNdkScopedAStatus(mapCdmResponseType(res));
|
|
}
|
|
|
|
::ndk::ScopedAStatus WVDrmPlugin::getSecureStops(
|
|
vector<::aidl::android::hardware::drm::SecureStop>* _aidl_return) {
|
|
std::list<vector<uint8_t>> secureStops;
|
|
vector<SecureStop> secureStopsVec;
|
|
|
|
CdmIdentifier identifier;
|
|
Status status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
|
|
if (status != Status::OK) {
|
|
*_aidl_return = secureStopsVec;
|
|
return toNdkScopedAStatus(status);
|
|
}
|
|
|
|
CdmUsageInfo cdmUsageInfo;
|
|
CdmResponseType res =
|
|
mCDM->GetUsageInfo(mPropertySet.app_id(), identifier, &cdmUsageInfo);
|
|
|
|
if (isCdmResponseTypeSuccess(res)) {
|
|
secureStops.clear();
|
|
for (CdmUsageInfo::const_iterator iter = cdmUsageInfo.begin();
|
|
iter != cdmUsageInfo.end(); ++iter) {
|
|
const std::string& cdmStop = *iter;
|
|
secureStops.push_back(StrToVector(cdmStop));
|
|
}
|
|
}
|
|
|
|
std::list<vector<uint8_t>>::iterator iter = secureStops.begin();
|
|
while (iter != secureStops.end()) {
|
|
SecureStop secureStop;
|
|
secureStop.opaqueData = *iter++;
|
|
secureStopsVec.push_back(secureStop);
|
|
}
|
|
|
|
*_aidl_return = secureStopsVec;
|
|
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;
|
|
Status status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
|
|
if (status != Status::OK) {
|
|
return toNdkScopedAStatus(status);
|
|
}
|
|
|
|
CdmUsageInfoReleaseMessage 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) {
|
|
CdmIdentifier identifier;
|
|
Status status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
|
|
if (status != Status::OK) {
|
|
*_aidl_return = vector<DrmMetricGroup>();
|
|
return toNdkScopedAStatus(status);
|
|
}
|
|
|
|
drm_metrics::WvCdmMetrics proto_metrics;
|
|
CdmResponseType result = mCDM->GetMetrics(identifier, &proto_metrics);
|
|
if (result != wvcdm::NO_ERROR) {
|
|
*_aidl_return = vector<DrmMetricGroup>();
|
|
return toNdkScopedAStatus(mapCdmResponseType(result));
|
|
}
|
|
|
|
vector<DrmMetricGroup> wvMetrics;
|
|
::wvcdm::WvMetricsAdapter adapter;
|
|
::wvcdm::WvMetricsAdapter::ToWvMetrics(proto_metrics, &wvMetrics);
|
|
*_aidl_return = wvMetrics;
|
|
return toNdkScopedAStatus(Status::OK);
|
|
}
|
|
|
|
::ndk::ScopedAStatus WVDrmPlugin::getSecureStopIds(
|
|
vector<::aidl::android::hardware::drm::SecureStopId>* _aidl_return) {
|
|
vector<SecureStopId> secureStopIds;
|
|
|
|
CdmIdentifier identifier;
|
|
Status 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;
|
|
Status status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
|
|
if (status != Status::OK) {
|
|
return toNdkScopedAStatus(status);
|
|
}
|
|
|
|
const vector<uint8_t> data = in_ssRelease.opaqueData;
|
|
CdmUsageInfoReleaseMessage cdmMessage(data.begin(), data.end());
|
|
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;
|
|
Status status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
|
|
if (status != Status::OK) {
|
|
return toNdkScopedAStatus(status);
|
|
}
|
|
|
|
CdmSecureStopId id(in_secureStopId.secureStopId.begin(),
|
|
in_secureStopId.secureStopId.end());
|
|
CdmResponseType res =
|
|
mCDM->RemoveUsageInfo(mPropertySet.app_id(), identifier, id);
|
|
return toNdkScopedAStatus(mapCdmResponseType(res));
|
|
}
|
|
|
|
::ndk::ScopedAStatus WVDrmPlugin::removeAllSecureStops() {
|
|
CdmIdentifier identifier;
|
|
Status status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
|
|
if (status != Status::OK) {
|
|
return toNdkScopedAStatus(status);
|
|
}
|
|
|
|
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;
|
|
Status 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;
|
|
Status 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", 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;
|
|
Status 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;
|
|
Status status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
|
|
if (status != Status::OK) {
|
|
*_aidl_return = licenseState;
|
|
return toNdkScopedAStatus(status);
|
|
}
|
|
|
|
CdmResponseType res = 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;
|
|
Status 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);
|
|
status = mapCdmResponseType(res);
|
|
}
|
|
|
|
return toNdkScopedAStatus(status);
|
|
}
|
|
|
|
::ndk::ScopedAStatus WVDrmPlugin::getPropertyString(
|
|
const std::string& in_propertyName, std::string* _aidl_return) {
|
|
Status status = Status::OK;
|
|
std::string name(in_propertyName.c_str());
|
|
std::string value;
|
|
|
|
if (name == "vendor") {
|
|
value = "Google";
|
|
} 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 {
|
|
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) {
|
|
Status 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);
|
|
} else {
|
|
status = mapCdmResponseType(mCDM->GetMetrics(identifier, &metrics));
|
|
}
|
|
}
|
|
if (status == Status::OK) {
|
|
std::string serialized_metrics;
|
|
if (!metrics.SerializeToString(&serialized_metrics)) {
|
|
status = Status::ERROR_DRM_UNKNOWN;
|
|
} else {
|
|
value = StrToVector(serialized_metrics);
|
|
}
|
|
}
|
|
} else {
|
|
ALOGE("App requested unknown byte array property %s", name.c_str());
|
|
status = 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.size() == 0) {
|
|
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;
|
|
Status 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.size() == 0) {
|
|
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.size() == 0) {
|
|
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.size() != 0) {
|
|
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 {
|
|
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());
|
|
if (!mCryptoSessions.count(cdmSessionId)) {
|
|
return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED);
|
|
}
|
|
|
|
CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId];
|
|
|
|
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());
|
|
if (!mCryptoSessions.count(cdmSessionId)) {
|
|
return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED);
|
|
}
|
|
|
|
CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId];
|
|
|
|
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());
|
|
if (!mCryptoSessions.count(cdmSessionId)) {
|
|
return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED);
|
|
}
|
|
|
|
const CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId];
|
|
|
|
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());
|
|
if (!mCryptoSessions.count(cdmSessionId)) {
|
|
return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED);
|
|
}
|
|
|
|
const CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId];
|
|
|
|
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());
|
|
if (!mCryptoSessions.count(cdmSessionId)) {
|
|
return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED);
|
|
}
|
|
|
|
const CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId];
|
|
|
|
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());
|
|
if (!mCryptoSessions.count(cdmSessionId)) {
|
|
return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED);
|
|
}
|
|
|
|
const CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId];
|
|
|
|
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 std::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);
|
|
}
|
|
|
|
Status WVDrmPlugin::queryProperty(const std::string& property,
|
|
std::string& stringValue) const {
|
|
return queryProperty(getRequestedSecurityLevel(), property, stringValue);
|
|
}
|
|
|
|
Status 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: %u", res);
|
|
}
|
|
return mapCdmResponseType(res);
|
|
}
|
|
|
|
Status WVDrmPlugin::queryProperty(const std::string& property,
|
|
vector<uint8_t>& vector_value) const {
|
|
std::string string_value;
|
|
Status status = queryProperty(property, string_value);
|
|
if (status != Status::OK) return status;
|
|
vector_value = StrToVector(string_value);
|
|
return 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);
|
|
}
|
|
|
|
Status 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;
|
|
}
|
|
|
|
Status 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();
|
|
}
|
|
|
|
Status WVDrmPlugin::CdmIdentifierBuilder::getCdmIdentifier(
|
|
CdmIdentifier* identifier) {
|
|
if (!mIsIdentifierSealed) {
|
|
Status res = calculateSpoid();
|
|
if (res != Status::OK) return res;
|
|
mIsIdentifierSealed = true;
|
|
}
|
|
*identifier = mCdmIdentifier;
|
|
return Status::OK;
|
|
}
|
|
|
|
Status WVDrmPlugin::CdmIdentifierBuilder::getDeviceUniqueId(std::string* id) {
|
|
if (mUseSpoid) {
|
|
CdmIdentifier identifier;
|
|
Status res = getCdmIdentifier(&identifier);
|
|
if (res != Status::OK) return res;
|
|
|
|
*id = identifier.spoid;
|
|
return Status::OK;
|
|
} else {
|
|
return getOemcryptoDeviceId(id);
|
|
}
|
|
}
|
|
|
|
Status 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.
|
|
Status res = getDeviceUniqueId(id);
|
|
if (res != Status::OK) return res;
|
|
|
|
for (char& c : *id) {
|
|
c = ~c;
|
|
}
|
|
|
|
return 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;
|
|
}
|
|
|
|
Status WVDrmPlugin::CdmIdentifierBuilder::calculateSpoid() {
|
|
if (!mUseSpoid) return Status::OK;
|
|
|
|
// Calculate SPOID for default security level if appropriate
|
|
std::string deviceId;
|
|
if (mParent.getRequestedSecurityLevel() == wvcdm::kLevelDefault) {
|
|
Status res = getOemcryptoDeviceId(&deviceId);
|
|
if (res != Status::OK) return res;
|
|
|
|
return 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.
|
|
Status res = getOemcryptoDeviceId(wvcdm::kLevel3, &deviceId);
|
|
if (res != Status::OK) return res;
|
|
|
|
std::string spoidL3;
|
|
res = calculateSpoid(deviceId, &spoidL3);
|
|
if (res != Status::OK) return res;
|
|
|
|
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 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 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 Status::OK;
|
|
}
|
|
|
|
std::string spoidLevelDefault;
|
|
res = calculateSpoid(deviceIdLevelDefault, &spoidLevelDefault);
|
|
if (res != Status::OK) {
|
|
mCdmIdentifier.spoid = spoidL3;
|
|
return Status::OK;
|
|
}
|
|
|
|
if (mParent.isProvisioned(wvcdm::kSecurityLevelL1, origin(),
|
|
spoidLevelDefault, atsc_mode_enabled)) {
|
|
mCdmIdentifier.spoid = spoidLevelDefault;
|
|
return Status::OK;
|
|
}
|
|
|
|
// Not provisioned with CdmIdentifier containing SPOID with L1 or L3
|
|
// device ID. Return L3 SPOID.
|
|
mCdmIdentifier.spoid = spoidL3;
|
|
return 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;
|
|
}
|
|
|
|
Status WVDrmPlugin::CdmIdentifierBuilder::getOemcryptoDeviceId(
|
|
std::string* id) {
|
|
return mParent.queryProperty(wvcdm::QUERY_KEY_DEVICE_ID, *id);
|
|
}
|
|
|
|
Status 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
|