Files
android/libwvdrmengine/mediadrm/src_hidl/WVDrmPlugin.cpp
Edwin Wong 2dc53442e7 Implement Widevine drm HIDL HAL service.
Modify Android mediadrm and mediacrypto glue layer to use
HIDL interface.

Test: Play Movies (streaming and offline playback)

Test: ANDROID_BUILD_TOP= ./android-gts/tools/gts-tradefed
run gts -m GtsMediaTestCases

Test:
adb shell /system/bin/libwvdrmengine_hidl_test

Test:
adb shell /system/bin/libwvdrmmediacrypto_hidl_test

Test:
adb shell /system/bin/libwvdrmdrmplugin_hidl_test

bug: 34628973
Change-Id: Icd5f2dd556acb9874697963b4d7d62cb7c943e74
2017-03-02 13:46:11 -08:00

1250 lines
41 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 "mapErrors-inl.h"
#include "media/stagefright/MediaErrors.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 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)
: mAppPackageName(appPackageName),
mCDM(cdm),
mCdmIdentifier(kDefaultCdmIdentifier),
mCrypto(crypto),
mCryptoSessions() {}
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, mCdmIdentifier, 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, mCdmIdentifier,
&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,
mCdmIdentifier,
&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 (mCdmIdentifier == kDefaultCdmIdentifier) {
_hidl_cb(toStatus(kErrorNoOriginSpecified), toHidlVec(certificate),
toHidlVec(wrappedKey));
return Void();
}
_hidl_cb(toStatus(unprovision(mCdmIdentifier)), toHidlVec(certificate),
toHidlVec(wrappedKey));
return Void();
} else {
std::string cdmCertificate;
std::string cdmWrappedKey;
CdmResponseType res = mCDM->HandleProvisioningResponse(mCdmIdentifier,
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 = mCdmIdentifier.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") {
status = queryProperty(wvcdm::QUERY_KEY_DEVICE_ID, value);
} 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 {
mCdmIdentifier.origin = _value.string();
}
} 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);
}
}
} // namespace widevine
} // namespace V1_0
} // namespace drm
} // namespace hardware
} // namespace wvdrm