Adds code to the Widevine Glue Layer that drives the generation and use of SPOIDs on platforms that support SPOIDs. (All devices whose first release is Android O or later.) Note that this only applies to the new, Treble-ized API. If the Widevine DRM Plugin is accessed through the old API, it will not use SPOIDs. This is by design because the old API does not provide an application package name, so SPOID generation is no better than the existing, origin-based solution. Bug: 27101531 Test: Unit tests Test: GTS tests Test: Google Play Change-Id: I80f79fca84065105e218e9070a1d5299c8e33500
1323 lines
43 KiB
C++
1323 lines
43 KiB
C++
//
|
|
// Copyright 2017 Google Inc. All Rights Reserved.
|
|
//
|
|
|
|
//#define LOG_NDEBUG 0
|
|
#define LOG_TAG "WVCdm"
|
|
#include <utils/Log.h>
|
|
|
|
#include "WVDrmPlugin.h"
|
|
#include "TypeConvert.h"
|
|
|
|
#include "cutils/properties.h"
|
|
#include "mapErrors-inl.h"
|
|
#include "media/stagefright/MediaErrors.h"
|
|
#include "openssl/sha.h"
|
|
#include "utils/List.h"
|
|
#include "wv_cdm_constants.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";
|
|
|
|
} // namespace
|
|
|
|
namespace wvdrm {
|
|
namespace hardware {
|
|
namespace drm {
|
|
namespace V1_0 {
|
|
namespace widevine {
|
|
|
|
using ::android::hardware::drm::V1_0::EventType;
|
|
using ::android::hardware::drm::V1_0::KeyRequestType;
|
|
using ::android::hardware::drm::V1_0::KeyStatusType;
|
|
using ::android::hardware::drm::V1_0::KeyType;
|
|
using ::android::hardware::drm::V1_0::Status;
|
|
using ::android::hardware::drm::V1_0::widevine::toHidlVec;
|
|
using ::android::hardware::drm::V1_0::widevine::toStatus;
|
|
using ::android::hardware::drm::V1_0::widevine::toVector;
|
|
using ::android::hardware::Void;
|
|
|
|
using android::List;
|
|
|
|
using wvcdm::kDefaultCdmIdentifier;
|
|
using wvcdm::CdmAppParameterMap;
|
|
using wvcdm::CdmCertificateType;
|
|
using wvcdm::CdmInitData;
|
|
using wvcdm::CdmKeyStatus;
|
|
using wvcdm::CdmKeyRequest;
|
|
using wvcdm::CdmKeyRequestType;
|
|
using wvcdm::CdmKeyResponse;
|
|
using wvcdm::CdmKeySetId;
|
|
using wvcdm::CdmLicenseType;
|
|
using wvcdm::CdmProvisioningRequest;
|
|
using wvcdm::CdmProvisioningResponse;
|
|
using wvcdm::CdmQueryMap;
|
|
using wvcdm::CdmSecureStopId;
|
|
using wvcdm::CdmUsageInfo;
|
|
using wvcdm::CdmUsageInfoReleaseMessage;
|
|
using wvcdm::KeyId;
|
|
using wvcdm::SecurityLevel;
|
|
|
|
namespace {
|
|
|
|
Vector<uint8_t> StrToVector(const std::string& str) {
|
|
Vector<uint8_t> vector;
|
|
vector.appendArray(reinterpret_cast<const uint8_t*>(str.data()), str.size());
|
|
return vector;
|
|
}
|
|
|
|
KeyRequestType ConvertFromCdmKeyRequestType(
|
|
CdmKeyRequestType keyRequestType) {
|
|
switch (keyRequestType) {
|
|
case wvcdm::kKeyRequestTypeInitial:
|
|
return KeyRequestType::INITIAL;
|
|
case wvcdm::kKeyRequestTypeRenewal:
|
|
return KeyRequestType::RENEWAL;
|
|
case wvcdm::kKeyRequestTypeRelease:
|
|
return KeyRequestType::RELEASE;
|
|
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::OUTPUTNOTALLOWED;
|
|
case wvcdm::kKeyStatusPending:
|
|
return KeyStatusType::STATUSPENDING;
|
|
case wvcdm::kKeyStatusInternalError:
|
|
default:
|
|
return KeyStatusType::INTERNALERROR;
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
WVDrmPlugin::WVDrmPlugin(const sp<WvContentDecryptionModule>& cdm,
|
|
const std::string& appPackageName,
|
|
WVGenericCryptoInterface* crypto)
|
|
: mCDM(cdm),
|
|
mCrypto(crypto),
|
|
mCryptoSessions() {
|
|
mCdmIdentifierBuilder.set_app_package_name(appPackageName);
|
|
|
|
std::string deviceId;
|
|
queryProperty(wvcdm::QUERY_KEY_DEVICE_ID, deviceId);
|
|
mCdmIdentifierBuilder.set_device_id(deviceId);
|
|
}
|
|
|
|
WVDrmPlugin::~WVDrmPlugin() {
|
|
typedef 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();
|
|
}
|
|
|
|
Return<void> WVDrmPlugin::openSession(openSession_cb _hidl_cb) {
|
|
status_t status = android::OK;
|
|
Vector<uint8_t> sessionId;
|
|
|
|
CdmSessionId cdmSessionId;
|
|
CdmResponseType res =
|
|
mCDM->OpenSession("com.widevine", &mPropertySet,
|
|
mCdmIdentifierBuilder.get_identifier(), this,
|
|
&cdmSessionId);
|
|
|
|
if (!isCdmResponseTypeSuccess(res)) {
|
|
status = mapAndNotifyOfCdmResponseType(sessionId, res);
|
|
_hidl_cb(toStatus(status), toHidlVec(sessionId));
|
|
return Void();
|
|
}
|
|
|
|
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::istringstream(
|
|
info[wvcdm::QUERY_KEY_OEMCRYPTO_SESSION_ID]) >> oecSessionId;
|
|
mCryptoSessions[cdmSessionId] = CryptoSession(oecSessionId);
|
|
success = true;
|
|
} else {
|
|
ALOGE("Unable to query key control info.");
|
|
}
|
|
|
|
if (success) {
|
|
// Marshal Session ID
|
|
sessionId = StrToVector(cdmSessionId);
|
|
_hidl_cb(Status::OK, toHidlVec(sessionId));
|
|
return Void();
|
|
} 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.
|
|
status = kErrorCDMGeneric;
|
|
}
|
|
}
|
|
_hidl_cb(toStatus(status), toHidlVec(sessionId));
|
|
return Void();
|
|
}
|
|
|
|
Return<Status> WVDrmPlugin::closeSession(const hidl_vec<uint8_t>& sessionId) {
|
|
|
|
const Vector<uint8_t> sId = toVector(sessionId);
|
|
CdmSessionId cdmSessionId(sId.begin(), sId.end());
|
|
mCDM->CloseSession(cdmSessionId);
|
|
mCryptoSessions.erase(cdmSessionId);
|
|
return Status::OK;
|
|
}
|
|
|
|
Return<void> WVDrmPlugin::getKeyRequest(
|
|
const hidl_vec<uint8_t>& scope,
|
|
const hidl_vec<uint8_t>& initData,
|
|
const hidl_string& mimeType,
|
|
KeyType keyType,
|
|
const hidl_vec<KeyValue>& optionalParameters,
|
|
getKeyRequest_cb _hidl_cb) {
|
|
|
|
KeyRequestType requestType = KeyRequestType::UNKNOWN;
|
|
status_t status = android::OK;
|
|
String8 defaultUrl;
|
|
Vector<uint8_t> request;
|
|
const Vector<uint8_t> scopeId = toVector(scope);
|
|
|
|
CdmLicenseType cdmLicenseType;
|
|
CdmSessionId cdmSessionId;
|
|
CdmKeySetId cdmKeySetId;
|
|
if (keyType == KeyType::OFFLINE) {
|
|
cdmLicenseType = wvcdm::kLicenseTypeOffline;
|
|
cdmSessionId.assign(scopeId.begin(), scopeId.end());
|
|
} else if (keyType == KeyType::STREAMING) {
|
|
cdmLicenseType = wvcdm::kLicenseTypeStreaming;
|
|
cdmSessionId.assign(scopeId.begin(), scopeId.end());
|
|
} else if (keyType == KeyType::RELEASE) {
|
|
cdmLicenseType = wvcdm::kLicenseTypeRelease;
|
|
cdmKeySetId.assign(scopeId.begin(), scopeId.end());
|
|
} else {
|
|
_hidl_cb(Status::BAD_VALUE, toHidlVec(request), KeyRequestType::UNKNOWN,
|
|
defaultUrl.string());
|
|
return Void();
|
|
}
|
|
|
|
std::string cdmInitDataType = 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 (initData.size() > 0 &&
|
|
WvContentDecryptionModule::IsCenc(cdmInitDataType) &&
|
|
!initDataResemblesPSSH(toVector(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*>(initData.data()),
|
|
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(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*>(initData.data()),
|
|
initData.size());
|
|
}
|
|
|
|
CdmAppParameterMap cdmParameters;
|
|
for (size_t i = 0; i < optionalParameters.size(); ++i) {
|
|
const String8& key = String8(optionalParameters[i].key);
|
|
const String8& value = String8(optionalParameters[i].value);
|
|
|
|
std::string cdmKey(key.string(), key.size());
|
|
std::string cdmValue(value.string(), value.size());
|
|
|
|
cdmParameters[cdmKey] = cdmValue;
|
|
}
|
|
|
|
CdmKeyRequest keyRequest;
|
|
CdmResponseType res = mCDM->GenerateKeyRequest(
|
|
cdmSessionId, cdmKeySetId, cdmInitDataType, processedInitData,
|
|
cdmLicenseType, cdmParameters, &mPropertySet,
|
|
mCdmIdentifierBuilder.get_identifier(), &keyRequest);
|
|
|
|
requestType = ConvertFromCdmKeyRequestType(keyRequest.type);
|
|
|
|
if (isCdmResponseTypeSuccess(res)) {
|
|
defaultUrl.clear();
|
|
defaultUrl.setTo(keyRequest.url.data(), keyRequest.url.size());
|
|
|
|
request = StrToVector(keyRequest.message);
|
|
}
|
|
|
|
if (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(scopeId, res);
|
|
}
|
|
_hidl_cb(toStatus(status), toHidlVec(request), requestType,
|
|
defaultUrl.string());
|
|
return Void();
|
|
}
|
|
|
|
Return<void> WVDrmPlugin::provideKeyResponse(
|
|
const hidl_vec<uint8_t>& scope,
|
|
const hidl_vec<uint8_t>& response,
|
|
provideKeyResponse_cb _hidl_cb) {
|
|
|
|
const Vector<uint8_t> resp = toVector(response);
|
|
const Vector<uint8_t> scopeId = toVector(scope);
|
|
|
|
CdmKeySetId cdmKeySetId;
|
|
CdmSessionId cdmSessionId;
|
|
CdmKeyResponse cdmResponse(resp.begin(), resp.end());
|
|
|
|
bool isRequest = (memcmp(scopeId.array(), wvcdm::SESSION_ID_PREFIX,
|
|
sizeof(wvcdm::SESSION_ID_PREFIX) - 1) == 0);
|
|
bool isRelease = (memcmp(scopeId.array(), wvcdm::KEY_SET_ID_PREFIX,
|
|
sizeof(wvcdm::KEY_SET_ID_PREFIX) - 1) == 0);
|
|
|
|
Vector<uint8_t> keySetId;
|
|
|
|
if (isRequest) {
|
|
cdmSessionId.assign(scopeId.begin(), scopeId.end());
|
|
} else if (isRelease) {
|
|
cdmKeySetId.assign(scopeId.begin(), scopeId.end());
|
|
} else {
|
|
_hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, toHidlVec(keySetId));
|
|
return Void();
|
|
}
|
|
|
|
CdmResponseType res = mCDM->AddKey(cdmSessionId, cdmResponse, &cdmKeySetId);
|
|
|
|
if (isRequest && isCdmResponseTypeSuccess(res)) {
|
|
keySetId = StrToVector(cdmKeySetId);
|
|
}
|
|
|
|
status_t status = android::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(scopeId, res);
|
|
// For "NEED_KEY," we still want to send the notifcation, 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 = android::OK;
|
|
}
|
|
}
|
|
_hidl_cb(toStatus(status), toHidlVec(keySetId));
|
|
return Void();
|
|
}
|
|
|
|
Return<Status> WVDrmPlugin::removeKeys(const hidl_vec<uint8_t>& sessionId) {
|
|
|
|
const Vector<uint8_t> sId = toVector(sessionId);
|
|
CdmSessionId cdmSessionId(sId.begin(), sId.end());
|
|
|
|
CdmResponseType res = mCDM->RemoveKeys(cdmSessionId);
|
|
|
|
return toStatus(mapAndNotifyOfCdmResponseType(sId, res));
|
|
}
|
|
|
|
Return<Status> WVDrmPlugin::restoreKeys(const hidl_vec<uint8_t>& sessionId,
|
|
const hidl_vec<uint8_t>& keySetId) {
|
|
|
|
const Vector<uint8_t> kId = toVector(keySetId);
|
|
const Vector<uint8_t> sId = toVector(sessionId);
|
|
CdmSessionId cdmSessionId(sId.begin(), sId.end());
|
|
CdmKeySetId cdmKeySetId(kId.begin(), kId.end());
|
|
|
|
CdmResponseType res = mCDM->RestoreKey(cdmSessionId, cdmKeySetId);
|
|
|
|
return toStatus(mapAndNotifyOfCdmResponseType(sId, res));
|
|
}
|
|
|
|
Return<void> WVDrmPlugin::queryKeyStatus(const hidl_vec<uint8_t>& sessionId,
|
|
queryKeyStatus_cb _hidl_cb) {
|
|
|
|
const Vector<uint8_t> sId = toVector(sessionId);
|
|
CdmSessionId cdmSessionId(sId.begin(), sId.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 = String8(cdmKey.data(), cdmKey.size());
|
|
keyValuePair.value = String8(cdmValue.data(), cdmValue.size());
|
|
infoMapVec.push_back(keyValuePair);
|
|
}
|
|
}
|
|
|
|
_hidl_cb(toStatus(mapCdmResponseType(res)), toHidlVec(infoMapVec));
|
|
return Void();
|
|
}
|
|
|
|
Return<void> WVDrmPlugin::getProvisionRequest(
|
|
const hidl_string& certificateType,
|
|
const hidl_string& certificateAuthority,
|
|
getProvisionRequest_cb _hidl_cb) {
|
|
|
|
CdmProvisioningRequest cdmProvisionRequest;
|
|
std::string cdmDefaultUrl;
|
|
|
|
CdmCertificateType cdmCertType = wvcdm::kCertificateWidevine;
|
|
if (certificateType == "X.509") {
|
|
cdmCertType = wvcdm::kCertificateX509;
|
|
}
|
|
|
|
std::string cdmCertAuthority = certificateAuthority;
|
|
|
|
CdmResponseType res = mCDM->GetProvisioningRequest(
|
|
cdmCertType, cdmCertAuthority, mCdmIdentifierBuilder.get_identifier(),
|
|
&cdmProvisionRequest, &cdmDefaultUrl);
|
|
String8 defaultUrl;
|
|
Vector<uint8_t> request;
|
|
if (isCdmResponseTypeSuccess(res)) {
|
|
request = StrToVector(cdmProvisionRequest);
|
|
defaultUrl.clear();
|
|
defaultUrl.setTo(cdmDefaultUrl.data(), cdmDefaultUrl.size());
|
|
}
|
|
|
|
_hidl_cb(toStatus(mapCdmResponseType(res)), toHidlVec(request),
|
|
hidl_string(defaultUrl));
|
|
return Void();
|
|
}
|
|
|
|
Return<void> WVDrmPlugin::provideProvisionResponse(
|
|
const hidl_vec<uint8_t>& response,
|
|
provideProvisionResponse_cb _hidl_cb) {
|
|
|
|
const Vector<uint8_t> resp = toVector(response);
|
|
Vector<uint8_t> certificate;
|
|
Vector<uint8_t> wrappedKey;
|
|
|
|
CdmProvisioningResponse cdmResponse(resp.begin(), resp.end());
|
|
if (cdmResponse == kSpecialUnprovisionResponse) {
|
|
if (mCdmIdentifierBuilder.get_identifier() == kDefaultCdmIdentifier) {
|
|
_hidl_cb(toStatus(kErrorNoOriginSpecified), toHidlVec(certificate),
|
|
toHidlVec(wrappedKey));
|
|
return Void();
|
|
}
|
|
_hidl_cb(toStatus(unprovision(mCdmIdentifierBuilder.get_identifier())),
|
|
toHidlVec(certificate),
|
|
toHidlVec(wrappedKey));
|
|
return Void();
|
|
} else {
|
|
std::string cdmCertificate;
|
|
std::string cdmWrappedKey;
|
|
CdmResponseType res = mCDM->HandleProvisioningResponse(
|
|
mCdmIdentifierBuilder.get_identifier(), cdmResponse, &cdmCertificate,
|
|
&cdmWrappedKey);
|
|
if (isCdmResponseTypeSuccess(res)) {
|
|
certificate = StrToVector(cdmCertificate);
|
|
wrappedKey = StrToVector(cdmWrappedKey);
|
|
}
|
|
|
|
_hidl_cb(toStatus(mapCdmResponseType(res)), toHidlVec(certificate),
|
|
toHidlVec(wrappedKey));
|
|
return Void();
|
|
}
|
|
}
|
|
|
|
status_t WVDrmPlugin::unprovisionDevice() {
|
|
return unprovision(kDefaultCdmIdentifier);
|
|
}
|
|
|
|
Return<void> WVDrmPlugin::getSecureStop(
|
|
const hidl_vec<uint8_t>& secureStopId,
|
|
getSecureStop_cb _hidl_cb) {
|
|
|
|
const Vector<uint8_t> id = toVector(secureStopId);
|
|
CdmUsageInfo cdmUsageInfo;
|
|
CdmSecureStopId cdmSsId(id.begin(), id.end());
|
|
CdmResponseType res = mCDM->GetUsageInfo(
|
|
mPropertySet.app_id(), cdmSsId, &cdmUsageInfo);
|
|
|
|
SecureStop secureStop;
|
|
if (isCdmResponseTypeSuccess(res)) {
|
|
|
|
Vector<uint8_t> cdmStopVec;
|
|
for (CdmUsageInfo::const_iterator iter = cdmUsageInfo.begin();
|
|
iter != cdmUsageInfo.end();
|
|
++iter) {
|
|
const std::string& cdmStop = *iter;
|
|
cdmStopVec.appendArray(reinterpret_cast<const uint8_t*>(cdmStop.data()),
|
|
cdmStop.size());
|
|
}
|
|
secureStop.opaqueData = toHidlVec(cdmStopVec);
|
|
}
|
|
|
|
_hidl_cb(toStatus(mapCdmResponseType(res)), secureStop);
|
|
return Void();
|
|
}
|
|
|
|
Return<void> WVDrmPlugin::getSecureStops(getSecureStops_cb _hidl_cb) {
|
|
|
|
List<Vector<uint8_t> > secureStops;
|
|
|
|
CdmUsageInfo cdmUsageInfo;
|
|
CdmResponseType res =
|
|
mCDM->GetUsageInfo(mPropertySet.app_id(), &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));
|
|
}
|
|
}
|
|
|
|
Vector<SecureStop> secureStopsVec;
|
|
List<Vector<uint8_t> >::iterator iter = secureStops.begin();
|
|
while (iter != secureStops.end()) {
|
|
SecureStop secureStop;
|
|
secureStop.opaqueData = toHidlVec(*iter++);
|
|
secureStopsVec.push_back(secureStop);
|
|
}
|
|
|
|
_hidl_cb(toStatus(mapCdmResponseType(res)), toHidlVec(secureStopsVec));
|
|
return Void();
|
|
}
|
|
|
|
Return<Status> WVDrmPlugin::releaseAllSecureStops() {
|
|
|
|
CdmResponseType res = mCDM->ReleaseAllUsageInfo(mPropertySet.app_id());
|
|
return toStatus(mapCdmResponseType(res));
|
|
}
|
|
|
|
Return<Status> WVDrmPlugin::releaseSecureStop(
|
|
const hidl_vec<uint8_t>& secureStopId) {
|
|
|
|
const Vector<uint8_t> ssRelease = toVector(secureStopId);
|
|
CdmUsageInfoReleaseMessage cdmMessage(ssRelease.begin(), ssRelease.end());
|
|
CdmResponseType res = mCDM->ReleaseUsageInfo(cdmMessage);
|
|
return toStatus(mapCdmResponseType(res));
|
|
}
|
|
|
|
Return<void> WVDrmPlugin::getPropertyString(const hidl_string& propertyName,
|
|
getPropertyString_cb _hidl_cb) {
|
|
status_t status = android::OK;
|
|
String8 name(propertyName);
|
|
String8 value;
|
|
|
|
if (name == "vendor") {
|
|
value = "Google";
|
|
} else if (name == "version") {
|
|
value = "1.0";
|
|
} 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 {
|
|
ALOGE("App requested unknown string property %s", name.string());
|
|
status = android::ERROR_DRM_CANNOT_HANDLE;
|
|
}
|
|
|
|
_hidl_cb(toStatus(status), value.string());
|
|
return Void();
|
|
}
|
|
|
|
Return<void> WVDrmPlugin::getPropertyByteArray(
|
|
const hidl_string& propertyName,
|
|
getPropertyByteArray_cb _hidl_cb) {
|
|
|
|
status_t status = android::OK;
|
|
String8 name(propertyName);
|
|
Vector<uint8_t> value;
|
|
|
|
if (name == "deviceUniqueId") {
|
|
value = StrToVector(mCdmIdentifierBuilder.get_device_unique_id());
|
|
} else if (name == "provisioningUniqueId") {
|
|
status = queryProperty(wvcdm::QUERY_KEY_PROVISIONING_ID, value);
|
|
} else if (name == "serviceCertificate") {
|
|
value = StrToVector(mPropertySet.service_certificate());
|
|
} else {
|
|
ALOGE("App requested unknown byte array property %s", name.string());
|
|
status = android::ERROR_DRM_CANNOT_HANDLE;
|
|
}
|
|
|
|
_hidl_cb(toStatus(status), toHidlVec(value));
|
|
return Void();
|
|
}
|
|
|
|
Return<Status> WVDrmPlugin::setPropertyString(const hidl_string& propertyName,
|
|
const hidl_string& value) {
|
|
String8 name(propertyName);
|
|
String8 _value(value);
|
|
|
|
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_t status = queryProperty(
|
|
wvcdm::kLevelDefault, wvcdm::QUERY_KEY_SECURITY_LEVEL,
|
|
current_security_level);
|
|
if (status != android::OK) {
|
|
return toStatus(status);
|
|
}
|
|
if (current_security_level != wvcdm::QUERY_VALUE_SECURITY_LEVEL_L1) {
|
|
ALOGE("App requested L1 security on a non-L1 device.");
|
|
return Status::BAD_VALUE;
|
|
} else {
|
|
mPropertySet.set_security_level(kResetSecurityLevel);
|
|
}
|
|
} else if (_value == kResetSecurityLevel) {
|
|
mPropertySet.set_security_level(kResetSecurityLevel);
|
|
} else {
|
|
ALOGE("App requested invalid security level %s", _value.string());
|
|
return Status::BAD_VALUE;
|
|
}
|
|
} else {
|
|
ALOGE("App tried to change security level while sessions are open.");
|
|
return toStatus(kErrorSessionIsOpen);
|
|
}
|
|
} 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.string());
|
|
return 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.string());
|
|
return Status::BAD_VALUE;
|
|
}
|
|
} else {
|
|
ALOGE("App tried to change key sharing while sessions are open.");
|
|
return toStatus(kErrorSessionIsOpen);
|
|
}
|
|
} else if (name == "appId") {
|
|
if (mCryptoSessions.size() == 0) {
|
|
mPropertySet.set_app_id(_value.string());
|
|
} else {
|
|
ALOGE("App tried to set the application id while sessions are opened.");
|
|
return toStatus(kErrorSessionIsOpen);
|
|
}
|
|
} else if (name == "origin") {
|
|
if (mCryptoSessions.size() != 0) {
|
|
ALOGE("App tried to set the origin while sessions are opened.");
|
|
return toStatus(kErrorSessionIsOpen);
|
|
} else {
|
|
if (!mCdmIdentifierBuilder.set_origin(_value.string())) {
|
|
return Status::BAD_VALUE;
|
|
}
|
|
}
|
|
} else {
|
|
ALOGE("App set unknown string property %s", name.string());
|
|
return Status::ERROR_DRM_CANNOT_HANDLE;
|
|
}
|
|
|
|
return Status::OK;
|
|
}
|
|
|
|
Return<Status> WVDrmPlugin::setPropertyByteArray(
|
|
const hidl_string& propertyName, const hidl_vec<uint8_t>& value) {
|
|
|
|
String8 name(propertyName);
|
|
Vector<uint8_t> _value = toVector(value);
|
|
|
|
if (name == "serviceCertificate") {
|
|
std::string cert(_value.begin(), _value.end());
|
|
if (_value.isEmpty() || mCDM->IsValidServiceCertificate(cert)) {
|
|
mPropertySet.set_service_certificate(cert);
|
|
} else {
|
|
return Status::BAD_VALUE;
|
|
}
|
|
} else {
|
|
ALOGE("App set unknown byte array property %s", name.string());
|
|
return Status::ERROR_DRM_CANNOT_HANDLE;
|
|
}
|
|
|
|
return Status::OK;
|
|
}
|
|
|
|
Return<Status> WVDrmPlugin::setCipherAlgorithm(
|
|
const hidl_vec<uint8_t>& sessionId, const hidl_string& algorithm) {
|
|
|
|
String8 algo(algorithm);
|
|
Vector<uint8_t> sId = toVector(sessionId);
|
|
|
|
CdmSessionId cdmSessionId(sId.begin(), sId.end());
|
|
if (!mCryptoSessions.count(cdmSessionId)) {
|
|
return 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 Status::ERROR_DRM_CANNOT_HANDLE;
|
|
}
|
|
|
|
return Status::OK;
|
|
}
|
|
|
|
Return<Status> WVDrmPlugin::setMacAlgorithm(
|
|
const hidl_vec<uint8_t>& sessionId, const hidl_string& algorithm) {
|
|
|
|
String8 algo(algorithm);
|
|
Vector<uint8_t> sId = toVector(sessionId);
|
|
|
|
CdmSessionId cdmSessionId(sId.begin(), sId.end());
|
|
if (!mCryptoSessions.count(cdmSessionId)) {
|
|
return Status::ERROR_DRM_SESSION_NOT_OPENED;
|
|
}
|
|
|
|
CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId];
|
|
|
|
if (algo == "HmacSHA256") {
|
|
cryptoSession.setMacAlgorithm(OEMCrypto_HMAC_SHA256);
|
|
} else {
|
|
return Status::ERROR_DRM_CANNOT_HANDLE;
|
|
}
|
|
|
|
return Status::OK;
|
|
}
|
|
|
|
Return<void> WVDrmPlugin::encrypt(
|
|
const hidl_vec<uint8_t>& sessionId,
|
|
const hidl_vec<uint8_t>& keyId,
|
|
const hidl_vec<uint8_t>& input,
|
|
const hidl_vec<uint8_t>& iv,
|
|
encrypt_cb _hidl_cb) {
|
|
|
|
const Vector<uint8_t> sId = toVector(sessionId);
|
|
Vector<uint8_t> output;
|
|
|
|
CdmSessionId cdmSessionId(sId.begin(), sId.end());
|
|
if (!mCryptoSessions.count(cdmSessionId)) {
|
|
_hidl_cb(Status::ERROR_DRM_SESSION_NOT_OPENED, toHidlVec(output));
|
|
return Void();
|
|
}
|
|
|
|
const CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId];
|
|
|
|
if (cryptoSession.cipherAlgorithm() == kInvalidCryptoAlgorithm) {
|
|
_hidl_cb(toStatus(android::NO_INIT), toHidlVec(output));
|
|
return Void();
|
|
}
|
|
|
|
const Vector<uint8_t> _keyId = toVector(keyId);
|
|
OEMCryptoResult res = mCrypto->selectKey(cryptoSession.oecSessionId(),
|
|
_keyId.array(), _keyId.size());
|
|
|
|
if (res != OEMCrypto_SUCCESS) {
|
|
ALOGE("OEMCrypto_SelectKey failed with %u", res);
|
|
_hidl_cb(toStatus(mapAndNotifyOfOEMCryptoResult(sId, res)),
|
|
toHidlVec(output));
|
|
return Void();
|
|
}
|
|
|
|
const Vector<uint8_t> _input = toVector(input);
|
|
const Vector<uint8_t> _iv = toVector(iv);
|
|
output.resize(_input.size());
|
|
|
|
res = mCrypto->encrypt(cryptoSession.oecSessionId(), _input.array(),
|
|
_input.size(), _iv.array(),
|
|
cryptoSession.cipherAlgorithm(), output.editArray());
|
|
|
|
if (res == OEMCrypto_SUCCESS) {
|
|
_hidl_cb(Status::OK, toHidlVec(output));
|
|
} else {
|
|
ALOGE("OEMCrypto_Generic_Encrypt failed with %u", res);
|
|
_hidl_cb(toStatus(mapAndNotifyOfOEMCryptoResult(sId, res)),
|
|
toHidlVec(output));
|
|
}
|
|
return Void();
|
|
}
|
|
|
|
Return<void> WVDrmPlugin::decrypt(
|
|
const hidl_vec<uint8_t>& sessionId,
|
|
const hidl_vec<uint8_t>& keyId,
|
|
const hidl_vec<uint8_t>& input,
|
|
const hidl_vec<uint8_t>& iv,
|
|
decrypt_cb _hidl_cb) {
|
|
|
|
const Vector<uint8_t> sId = toVector(sessionId);
|
|
Vector<uint8_t> output;
|
|
|
|
CdmSessionId cdmSessionId(sId.begin(), sId.end());
|
|
if (!mCryptoSessions.count(cdmSessionId)) {
|
|
_hidl_cb(Status::ERROR_DRM_SESSION_NOT_OPENED, toHidlVec(output));
|
|
return Void();
|
|
}
|
|
|
|
const CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId];
|
|
|
|
if (cryptoSession.cipherAlgorithm() == kInvalidCryptoAlgorithm) {
|
|
_hidl_cb(toStatus(android::NO_INIT), toHidlVec(output));
|
|
return Void();
|
|
}
|
|
|
|
const Vector<uint8_t> _keyId = toVector(keyId);
|
|
OEMCryptoResult res = mCrypto->selectKey(cryptoSession.oecSessionId(),
|
|
_keyId.array(), _keyId.size());
|
|
|
|
if (res != OEMCrypto_SUCCESS) {
|
|
ALOGE("OEMCrypto_SelectKey failed with %u", res);
|
|
_hidl_cb(toStatus(mapAndNotifyOfOEMCryptoResult(sId, res)),
|
|
toHidlVec(output));
|
|
return Void();
|
|
}
|
|
|
|
const Vector<uint8_t> _input = toVector(input);
|
|
const Vector<uint8_t> _iv = toVector(iv);
|
|
output.resize(_input.size());
|
|
|
|
res = mCrypto->decrypt(cryptoSession.oecSessionId(), _input.array(),
|
|
_input.size(), _iv.array(),
|
|
cryptoSession.cipherAlgorithm(), output.editArray());
|
|
|
|
if (res == OEMCrypto_SUCCESS) {
|
|
_hidl_cb(Status::OK, toHidlVec(output));
|
|
} else {
|
|
ALOGE("OEMCrypto_Generic_Decrypt failed with %u", res);
|
|
_hidl_cb(toStatus(mapAndNotifyOfOEMCryptoResult(sId, res)),
|
|
toHidlVec(output));
|
|
}
|
|
return Void();
|
|
}
|
|
|
|
Return<void> WVDrmPlugin::sign(
|
|
const hidl_vec<uint8_t>& sessionId,
|
|
const hidl_vec<uint8_t>& keyId,
|
|
const hidl_vec<uint8_t>& message,
|
|
sign_cb _hidl_cb) {
|
|
|
|
const Vector<uint8_t> sId = toVector(sessionId);
|
|
Vector<uint8_t> signature;
|
|
|
|
CdmSessionId cdmSessionId(sId.begin(), sId.end());
|
|
if (!mCryptoSessions.count(cdmSessionId)) {
|
|
_hidl_cb(Status::ERROR_DRM_SESSION_NOT_OPENED, toHidlVec(signature));
|
|
return Void();
|
|
}
|
|
|
|
const CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId];
|
|
|
|
if (cryptoSession.macAlgorithm() == kInvalidCryptoAlgorithm) {
|
|
_hidl_cb(toStatus(android::NO_INIT), toHidlVec(signature));
|
|
return Void();
|
|
}
|
|
|
|
const Vector<uint8_t> _keyId = toVector(keyId);
|
|
OEMCryptoResult res = mCrypto->selectKey(cryptoSession.oecSessionId(),
|
|
_keyId.array(), _keyId.size());
|
|
|
|
if (res != OEMCrypto_SUCCESS) {
|
|
ALOGE("OEMCrypto_SelectKey failed with %u", res);
|
|
_hidl_cb(toStatus(mapAndNotifyOfOEMCryptoResult(sId, res)),
|
|
toHidlVec(signature));
|
|
return Void();
|
|
}
|
|
|
|
size_t signatureSize = 0;
|
|
|
|
const Vector<uint8_t> msg = toVector(message);
|
|
res = mCrypto->sign(cryptoSession.oecSessionId(), msg.array(),
|
|
msg.size(), cryptoSession.macAlgorithm(),
|
|
NULL, &signatureSize);
|
|
|
|
if (res != OEMCrypto_ERROR_SHORT_BUFFER) {
|
|
ALOGE("OEMCrypto_Generic_Sign failed with %u when requesting signature "
|
|
"size", res);
|
|
if (res != OEMCrypto_SUCCESS) {
|
|
_hidl_cb(toStatus(mapAndNotifyOfOEMCryptoResult(sId, res)),
|
|
toHidlVec(signature));
|
|
} else {
|
|
_hidl_cb(Status::ERROR_DRM_UNKNOWN, toHidlVec(signature));
|
|
}
|
|
return Void();
|
|
}
|
|
|
|
signature.resize(signatureSize);
|
|
|
|
res = mCrypto->sign(cryptoSession.oecSessionId(), msg.array(),
|
|
msg.size(), cryptoSession.macAlgorithm(),
|
|
signature.editArray(), &signatureSize);
|
|
|
|
if (res == OEMCrypto_SUCCESS) {
|
|
_hidl_cb(Status::OK, toHidlVec(signature));
|
|
} else {
|
|
ALOGE("OEMCrypto_Generic_Sign failed with %u", res);
|
|
_hidl_cb(toStatus(mapAndNotifyOfOEMCryptoResult(sId, res)),
|
|
toHidlVec(signature));
|
|
}
|
|
return Void();
|
|
}
|
|
|
|
Return<void> WVDrmPlugin::verify(
|
|
const hidl_vec<uint8_t>& sessionId,
|
|
const hidl_vec<uint8_t>& keyId,
|
|
const hidl_vec<uint8_t>& message,
|
|
const hidl_vec<uint8_t>& signature,
|
|
verify_cb _hidl_cb) {
|
|
|
|
bool match = false;
|
|
const Vector<uint8_t> sId = toVector(sessionId);
|
|
|
|
CdmSessionId cdmSessionId(sId.begin(), sId.end());
|
|
if (!mCryptoSessions.count(cdmSessionId)) {
|
|
_hidl_cb(Status::ERROR_DRM_SESSION_NOT_OPENED, match);
|
|
return Void();
|
|
}
|
|
|
|
const CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId];
|
|
|
|
if (cryptoSession.macAlgorithm() == kInvalidCryptoAlgorithm) {
|
|
_hidl_cb(toStatus(android::NO_INIT), match);
|
|
return Void();
|
|
}
|
|
|
|
const Vector<uint8_t> _keyId = toVector(keyId);
|
|
OEMCryptoResult res = mCrypto->selectKey(cryptoSession.oecSessionId(),
|
|
_keyId.array(), _keyId.size());
|
|
|
|
if (res != OEMCrypto_SUCCESS) {
|
|
ALOGE("OEMCrypto_SelectKey failed with %u", res);
|
|
_hidl_cb(toStatus(mapAndNotifyOfOEMCryptoResult(sId, res)), match);
|
|
return Void();
|
|
}
|
|
|
|
const Vector<uint8_t> _message = toVector(message);
|
|
const Vector<uint8_t> _signature = toVector(signature);
|
|
res = mCrypto->verify(cryptoSession.oecSessionId(), _message.array(),
|
|
_message.size(), cryptoSession.macAlgorithm(),
|
|
_signature.array(), _signature.size());
|
|
|
|
if (res == OEMCrypto_SUCCESS) {
|
|
match = true;
|
|
_hidl_cb(Status::OK, match);
|
|
} else if (res == OEMCrypto_ERROR_SIGNATURE_FAILURE) {
|
|
match = false;
|
|
_hidl_cb(Status::OK, match);
|
|
} else {
|
|
ALOGE("OEMCrypto_Generic_Verify failed with %u", res);
|
|
_hidl_cb(toStatus(mapAndNotifyOfOEMCryptoResult(sId, res)), match);
|
|
}
|
|
return Void();
|
|
}
|
|
|
|
Return<void> WVDrmPlugin::signRSA(
|
|
const hidl_vec<uint8_t>& /* sessionId */,
|
|
const hidl_string& algorithm,
|
|
const hidl_vec<uint8_t>& message,
|
|
const hidl_vec<uint8_t>& wrappedKey,
|
|
signRSA_cb _hidl_cb) {
|
|
|
|
const String8 algo(algorithm);
|
|
Vector<uint8_t> 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.string());
|
|
_hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, toHidlVec(signature));
|
|
return Void();
|
|
}
|
|
|
|
const Vector<uint8_t> msg = toVector(message);
|
|
const Vector<uint8_t> _wrappedKey = toVector(wrappedKey);
|
|
OEMCryptoResult res = mCrypto->signRSA(_wrappedKey.array(),
|
|
_wrappedKey.size(),
|
|
msg.array(), msg.size(),
|
|
signature,
|
|
padding_scheme);
|
|
|
|
if (res != OEMCrypto_SUCCESS) {
|
|
ALOGE("OEMCrypto_GenerateRSASignature failed with %u", res);
|
|
_hidl_cb(toStatus(mapOEMCryptoResult(res)), toHidlVec(signature));
|
|
return Void();
|
|
}
|
|
|
|
_hidl_cb(Status::OK, toHidlVec(signature));
|
|
return Void();
|
|
}
|
|
|
|
Return<void> WVDrmPlugin::setListener(const sp<IDrmPluginListener>& listener) {
|
|
mListener = listener;
|
|
return Void();
|
|
}
|
|
|
|
Return<void> WVDrmPlugin::sendEvent(
|
|
EventType eventType,
|
|
const hidl_vec<uint8_t>& sessionId, const hidl_vec<uint8_t>& data) {
|
|
if (mListener != NULL) {
|
|
mListener->sendEvent(eventType, sessionId, data);
|
|
} else {
|
|
ALOGE("Null event listener, event not sent");
|
|
}
|
|
return Void();
|
|
}
|
|
|
|
Return<void> WVDrmPlugin::sendExpirationUpdate(
|
|
const hidl_vec<uint8_t>& sessionId,
|
|
int64_t expiryTimeInMS) {
|
|
if (mListener != NULL) {
|
|
mListener->sendExpirationUpdate(sessionId, expiryTimeInMS);
|
|
} else {
|
|
ALOGE("Null event listener, event not sent");
|
|
}
|
|
return Void();
|
|
}
|
|
|
|
Return<void> WVDrmPlugin::sendKeysChange(
|
|
const hidl_vec<uint8_t>& sessionId,
|
|
const hidl_vec<KeyStatus>& keyStatusList, bool hasNewUsableKey) {
|
|
if (mListener != NULL) {
|
|
mListener->sendKeysChange(sessionId, keyStatusList, hasNewUsableKey);
|
|
} else {
|
|
ALOGE("Null event listener, event not sent");
|
|
}
|
|
return Void();
|
|
}
|
|
|
|
void WVDrmPlugin::OnSessionRenewalNeeded(const CdmSessionId& cdmSessionId) {
|
|
const Vector<uint8_t> sessionId = StrToVector(cdmSessionId);
|
|
const hidl_vec<uint8_t> data; // data is ignored
|
|
const hidl_vec<uint8_t> sid = toHidlVec(sessionId);
|
|
sendEvent(EventType::KEY_NEEDED, sid, 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 = toHidlVec(StrToVector(keyId));
|
|
keyStatus.type = ConvertFromCdmKeyStatus(cdmKeyStatus);
|
|
keyStatusList.push_back(keyStatus);
|
|
}
|
|
|
|
const Vector<uint8_t> sessionId = StrToVector(cdmSessionId);
|
|
const hidl_vec<uint8_t> data; // data is ignored
|
|
const hidl_vec<uint8_t> sid = toHidlVec(sessionId);
|
|
sendKeysChange(sid, toHidlVec(keyStatusList), hasNewUsableKey);
|
|
// For backward compatibility.
|
|
if (expired) {
|
|
sendEvent(EventType::KEY_EXPIRED, sid, 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(toHidlVec(sessionId), newExpiryTimeMilliseconds);
|
|
}
|
|
|
|
status_t WVDrmPlugin::queryProperty(const std::string& property,
|
|
std::string& stringValue) const {
|
|
SecurityLevel securityLevel =
|
|
mPropertySet.security_level().compare(
|
|
wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3) == 0
|
|
? wvcdm::kLevel3
|
|
: wvcdm::kLevelDefault;
|
|
return queryProperty(securityLevel, property, stringValue);
|
|
}
|
|
|
|
status_t WVDrmPlugin::queryProperty(SecurityLevel 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_t WVDrmPlugin::queryProperty(const std::string& property,
|
|
String8& string8_value) const {
|
|
std::string string_value;
|
|
status_t status = queryProperty(property, string_value);
|
|
if (status != android::OK) return status;
|
|
string8_value = string_value.c_str();
|
|
return android::OK;
|
|
}
|
|
|
|
status_t WVDrmPlugin::queryProperty(const std::string& property,
|
|
Vector<uint8_t>& vector_value) const {
|
|
std::string string_value;
|
|
status_t status = queryProperty(property, string_value);
|
|
if (status != android::OK) return status;
|
|
vector_value = StrToVector(string_value);
|
|
return android::OK;
|
|
}
|
|
|
|
status_t WVDrmPlugin::mapAndNotifyOfCdmResponseType(
|
|
const Vector<uint8_t>& sessionId,
|
|
CdmResponseType res) {
|
|
|
|
const hidl_vec<uint8_t> data; // data is ignored
|
|
if (res == wvcdm::NEED_PROVISIONING) {
|
|
sendEvent(EventType::PROVISION_REQUIRED, toHidlVec(sessionId), data);
|
|
} else if (res == wvcdm::NEED_KEY) {
|
|
sendEvent(EventType::KEY_NEEDED, toHidlVec(sessionId), data);
|
|
}
|
|
|
|
return mapCdmResponseType(res);
|
|
}
|
|
|
|
status_t WVDrmPlugin::mapAndNotifyOfOEMCryptoResult(
|
|
const Vector<uint8_t>& sessionId,
|
|
OEMCryptoResult res) {
|
|
|
|
const hidl_vec<uint8_t> data; // data is ignored
|
|
if (res == OEMCrypto_ERROR_NO_DEVICE_KEY) {
|
|
sendEvent(EventType::PROVISION_REQUIRED, toHidlVec(sessionId), data);
|
|
}
|
|
return mapOEMCryptoResult(res);
|
|
}
|
|
|
|
status_t WVDrmPlugin::mapOEMCryptoResult(OEMCryptoResult res) {
|
|
switch (res) {
|
|
case OEMCrypto_SUCCESS:
|
|
return android::OK;
|
|
case OEMCrypto_ERROR_SIGNATURE_FAILURE:
|
|
return android::ERROR_DRM_TAMPER_DETECTED;
|
|
case OEMCrypto_ERROR_SHORT_BUFFER:
|
|
return kErrorIncorrectBufferSize;
|
|
case OEMCrypto_ERROR_NO_DEVICE_KEY:
|
|
return android::ERROR_DRM_NOT_PROVISIONED;
|
|
case OEMCrypto_ERROR_INVALID_SESSION:
|
|
return android::ERROR_DRM_SESSION_NOT_OPENED;
|
|
case OEMCrypto_ERROR_TOO_MANY_SESSIONS:
|
|
return android::ERROR_DRM_RESOURCE_BUSY;
|
|
case OEMCrypto_ERROR_INVALID_RSA_KEY:
|
|
return kErrorInvalidKey;
|
|
case OEMCrypto_ERROR_INSUFFICIENT_RESOURCES:
|
|
return android::ERROR_DRM_RESOURCE_BUSY;
|
|
case OEMCrypto_ERROR_NOT_IMPLEMENTED:
|
|
return android::ERROR_DRM_CANNOT_HANDLE;
|
|
case OEMCrypto_ERROR_UNKNOWN_FAILURE:
|
|
case OEMCrypto_ERROR_OPEN_SESSION_FAILED:
|
|
return android::ERROR_DRM_UNKNOWN;
|
|
default:
|
|
return android::UNKNOWN_ERROR;
|
|
}
|
|
}
|
|
|
|
bool WVDrmPlugin::initDataResemblesPSSH(const Vector<uint8_t>& initData) {
|
|
const uint8_t* const initDataArray = initData.array();
|
|
|
|
// 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_t WVDrmPlugin::unprovision(const CdmIdentifier& identifier) {
|
|
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()
|
|
: mCdmIdentifier(),
|
|
mIsIdentifierSealed(false),
|
|
mDeviceId(),
|
|
mAppPackageName() {
|
|
// Determine if this device supports SPOIDs.
|
|
int32_t firstApiLevel = property_get_int32("ro.product.first_api_level", 0);
|
|
if (firstApiLevel == 0) {
|
|
// First API Level is 0 on factory ROMs, but we can assume the current SDK
|
|
// version is the first if it's a factory ROM.
|
|
firstApiLevel = property_get_int32("ro.build.version.sdk", 0);
|
|
}
|
|
// TODO(juce): b/34548395 Make sure this API version is correct.
|
|
mUseSpoid = firstApiLevel >= 26; // Android O
|
|
}
|
|
|
|
const CdmIdentifier& WVDrmPlugin::CdmIdentifierBuilder::get_identifier() {
|
|
if (!mIsIdentifierSealed) calculateSpoid();
|
|
mIsIdentifierSealed = true;
|
|
return mCdmIdentifier;
|
|
}
|
|
|
|
const std::string& WVDrmPlugin::CdmIdentifierBuilder::get_device_unique_id() {
|
|
if (mUseSpoid) {
|
|
return get_identifier().spoid;
|
|
} else {
|
|
return mDeviceId;
|
|
}
|
|
}
|
|
|
|
bool WVDrmPlugin::CdmIdentifierBuilder::set_device_id(const std::string& id) {
|
|
if (mIsIdentifierSealed) return false;
|
|
mDeviceId = id;
|
|
return true;
|
|
}
|
|
|
|
bool WVDrmPlugin::CdmIdentifierBuilder::set_app_package_name(const std::string& id) {
|
|
if (mIsIdentifierSealed) return false;
|
|
mAppPackageName = id;
|
|
return true;
|
|
}
|
|
|
|
bool WVDrmPlugin::CdmIdentifierBuilder::set_origin(const std::string& id) {
|
|
if (mIsIdentifierSealed) return false;
|
|
mCdmIdentifier.origin = id;
|
|
return true;
|
|
}
|
|
|
|
void WVDrmPlugin::CdmIdentifierBuilder::calculateSpoid() {
|
|
if (mUseSpoid) {
|
|
uint8_t hash[SHA256_DIGEST_LENGTH];
|
|
SHA256_CTX ctx;
|
|
SHA256_Init(&ctx);
|
|
SHA256_Update(&ctx, mDeviceId.data(), mDeviceId.length());
|
|
SHA256_Update(&ctx, mAppPackageName.data(), mAppPackageName.length());
|
|
SHA256_Update(&ctx, origin().data(), origin().length());
|
|
SHA256_Final(hash, &ctx);
|
|
|
|
mCdmIdentifier.spoid =
|
|
std::string(reinterpret_cast<char*>(hash), SHA256_DIGEST_LENGTH);
|
|
}
|
|
}
|
|
|
|
} // namespace widevine
|
|
} // namespace V1_0
|
|
} // namespace drm
|
|
} // namespace hardware
|
|
} // namespace wvdrm
|