|
|
|
|
@@ -4,7 +4,7 @@
|
|
|
|
|
// Agreement.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
//#define LOG_NDEBUG 0
|
|
|
|
|
// #define LOG_NDEBUG 0
|
|
|
|
|
#define LOG_TAG "WVCdm"
|
|
|
|
|
|
|
|
|
|
#include "WVDrmPlugin.h"
|
|
|
|
|
@@ -154,6 +154,12 @@ HdcpLevel mapHdcpLevel(const std::string& level) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool isRootOrShell() {
|
|
|
|
|
const uid_t AID_ROOT = 0, AID_SHELL = 2000;
|
|
|
|
|
const uid_t uid = AIBinder_getCallingUid();
|
|
|
|
|
return (uid == AID_ROOT || uid == AID_SHELL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
WVDrmPlugin::WVDrmPlugin(const android::sp<WvContentDecryptionModule>& cdm,
|
|
|
|
|
@@ -759,7 +765,8 @@ Status WVDrmPlugin::unprovisionDevice() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
drm_metrics::WvCdmMetrics proto_metrics;
|
|
|
|
|
const CdmResponseType result = mCDM->GetCurrentMetrics(identifier, &proto_metrics);
|
|
|
|
|
const CdmResponseType result =
|
|
|
|
|
mCDM->GetCurrentMetrics(identifier, &proto_metrics);
|
|
|
|
|
if (result != wvcdm::NO_ERROR) {
|
|
|
|
|
return toNdkScopedAStatus(mapCdmResponseType(result));
|
|
|
|
|
}
|
|
|
|
|
@@ -1066,7 +1073,8 @@ Status WVDrmPlugin::unprovisionDevice() {
|
|
|
|
|
value = info->name;
|
|
|
|
|
} else {
|
|
|
|
|
auto err = info.error();
|
|
|
|
|
ALOGW("apex info error %d: %s", err.code().value(), err.message().c_str());
|
|
|
|
|
ALOGW("apex info error %d: %s", err.code().value(),
|
|
|
|
|
err.message().c_str());
|
|
|
|
|
value = "Google";
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
@@ -1165,6 +1173,26 @@ Status WVDrmPlugin::unprovisionDevice() {
|
|
|
|
|
return toNdkScopedAStatus(status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static WvStatus getDeviceSignedCsrPayload(
|
|
|
|
|
android::sp<wvcdm::WvContentDecryptionModule> const cdm,
|
|
|
|
|
std::string& certificate_signing_request_challenge,
|
|
|
|
|
std::string& device_info, std::string* csr) {
|
|
|
|
|
if (certificate_signing_request_challenge.empty() || device_info.empty()) {
|
|
|
|
|
ALOGW("CSR challenge or device info is empty.");
|
|
|
|
|
return WvStatus(Status::BAD_VALUE);
|
|
|
|
|
}
|
|
|
|
|
std::string signed_csr_payload;
|
|
|
|
|
CdmResponseType res = cdm->QueryDeviceSignedCsrPayload(
|
|
|
|
|
certificate_signing_request_challenge, device_info, &signed_csr_payload);
|
|
|
|
|
if (res != wvcdm::NO_ERROR) {
|
|
|
|
|
ALOGE("Error querying CDM device_signed_csr_payload: %d",
|
|
|
|
|
static_cast<int>(res));
|
|
|
|
|
return mapCdmResponseType(res);
|
|
|
|
|
}
|
|
|
|
|
*csr = signed_csr_payload;
|
|
|
|
|
return WvStatus(Status::OK);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
::ndk::ScopedAStatus WVDrmPlugin::getPropertyByteArray(
|
|
|
|
|
const std::string& in_propertyName, vector<uint8_t>* _aidl_return) {
|
|
|
|
|
WvStatus status(Status::OK);
|
|
|
|
|
@@ -1210,6 +1238,42 @@ Status WVDrmPlugin::unprovisionDevice() {
|
|
|
|
|
value = StrToVector(serialized_metrics);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (name == "bootCertificateChain" && isRootOrShell()) {
|
|
|
|
|
std::string boot_certificate_chain;
|
|
|
|
|
CdmResponseType res = mCDM->QueryStatus(
|
|
|
|
|
wvcdm::kLevelDefault, wvcdm::QUERY_KEY_DEBUG_BOOT_CERTIFICATE_CHAIN,
|
|
|
|
|
&boot_certificate_chain);
|
|
|
|
|
if (res != wvcdm::NO_ERROR) {
|
|
|
|
|
ALOGE("Error querying CDM boot certificate chain: %d",
|
|
|
|
|
static_cast<int>(res));
|
|
|
|
|
status = mapCdmResponseType(res);
|
|
|
|
|
} else {
|
|
|
|
|
value = StrToVector(boot_certificate_chain);
|
|
|
|
|
}
|
|
|
|
|
} else if (name == "verifiedDeviceInfo" && isRootOrShell()) {
|
|
|
|
|
std::string verified_device_info;
|
|
|
|
|
CdmResponseType res = mCDM->QueryStatus(wvcdm::kLevelDefault,
|
|
|
|
|
wvcdm::QUERY_KEY_DEVICE_INFORMATION,
|
|
|
|
|
&verified_device_info);
|
|
|
|
|
if (res != wvcdm::NO_ERROR) {
|
|
|
|
|
ALOGE("Error querying CDM verified device info: %d",
|
|
|
|
|
static_cast<int>(res));
|
|
|
|
|
status = mapCdmResponseType(res);
|
|
|
|
|
} else {
|
|
|
|
|
value = StrToVector(verified_device_info);
|
|
|
|
|
}
|
|
|
|
|
} else if (name == "deviceSignedCsrPayload" && isRootOrShell()) {
|
|
|
|
|
std::string signed_csr_payload;
|
|
|
|
|
status =
|
|
|
|
|
getDeviceSignedCsrPayload(mCDM, mCertificateSigningRequestChallenge,
|
|
|
|
|
mDeviceInfo, &signed_csr_payload);
|
|
|
|
|
if (status != Status::OK) {
|
|
|
|
|
ALOGE("Error querying CDM signed CSR payload: %d", status.get());
|
|
|
|
|
} else {
|
|
|
|
|
value = StrToVector(signed_csr_payload);
|
|
|
|
|
}
|
|
|
|
|
mCertificateSigningRequestChallenge.clear();
|
|
|
|
|
mDeviceInfo.clear();
|
|
|
|
|
} else {
|
|
|
|
|
ALOGE("App requested unknown byte array property %s", name.c_str());
|
|
|
|
|
status = WvStatus(Status::ERROR_DRM_CANNOT_HANDLE);
|
|
|
|
|
@@ -1349,8 +1413,9 @@ Status WVDrmPlugin::unprovisionDevice() {
|
|
|
|
|
}
|
|
|
|
|
} else if (name == "storeAtscLicense") {
|
|
|
|
|
if (!mCdmIdentifierBuilder.is_sealed()) {
|
|
|
|
|
ALOGE("Cdm identifier builder is not sealed. Storing ATSC license "
|
|
|
|
|
"prohibited");
|
|
|
|
|
ALOGE(
|
|
|
|
|
"Cdm identifier builder is not sealed. Storing ATSC license "
|
|
|
|
|
"prohibited");
|
|
|
|
|
return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
|
|
|
|
|
}
|
|
|
|
|
if (!mPropertySet.use_atsc_mode()) {
|
|
|
|
|
@@ -1366,8 +1431,7 @@ Status WVDrmPlugin::unprovisionDevice() {
|
|
|
|
|
}
|
|
|
|
|
std::string key_set_id, license_data;
|
|
|
|
|
status = parseAtscLicenseData(_value, &key_set_id, &license_data);
|
|
|
|
|
if (status != Status::OK)
|
|
|
|
|
return toNdkScopedAStatus(status);
|
|
|
|
|
if (status != Status::OK) return toNdkScopedAStatus(status);
|
|
|
|
|
|
|
|
|
|
const CdmResponseType res = mCDM->StoreAtscLicense(
|
|
|
|
|
identifier, getRequestedSecurityLevel(), key_set_id, license_data);
|
|
|
|
|
@@ -1400,6 +1464,11 @@ Status WVDrmPlugin::unprovisionDevice() {
|
|
|
|
|
} else {
|
|
|
|
|
return toNdkScopedAStatus(Status::BAD_VALUE);
|
|
|
|
|
}
|
|
|
|
|
} else if (name == "certificateSigningRequestChallenge" && isRootOrShell()) {
|
|
|
|
|
mCertificateSigningRequestChallenge =
|
|
|
|
|
std::string(_value.begin(), _value.end());
|
|
|
|
|
} else if (name == "deviceInfo" && isRootOrShell()) {
|
|
|
|
|
mDeviceInfo = std::string(_value.begin(), _value.end());
|
|
|
|
|
} else {
|
|
|
|
|
ALOGE("App set unknown byte array property %s", name.c_str());
|
|
|
|
|
return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
|
|
|
|
|
@@ -1830,8 +1899,7 @@ bool WVDrmPlugin::isProvisioned(wvcdm::CdmSecurityLevel securityLevel,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WvStatus WVDrmPlugin::parseAtscLicenseData(
|
|
|
|
|
const std::string& in_value,
|
|
|
|
|
std::string* key_set_id,
|
|
|
|
|
const std::string& in_value, std::string* key_set_id,
|
|
|
|
|
std::string* serialized_license_data) {
|
|
|
|
|
if (key_set_id == nullptr) {
|
|
|
|
|
ALOGE("key_set_id null");
|
|
|
|
|
@@ -1843,34 +1911,39 @@ WvStatus WVDrmPlugin::parseAtscLicenseData(
|
|
|
|
|
return WvStatus(Status::ERROR_DRM_CANNOT_HANDLE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (in_value.compare(0,
|
|
|
|
|
strlen(wvcdm::ATSC_KEY_SET_ID_PREFIX),
|
|
|
|
|
if (in_value.compare(0, strlen(wvcdm::ATSC_KEY_SET_ID_PREFIX),
|
|
|
|
|
&wvcdm::ATSC_KEY_SET_ID_PREFIX[0]) != 0) {
|
|
|
|
|
ALOGE("ATSC license input does not conform to expectations. Key set does "
|
|
|
|
|
"not have a valid ATSC Key set prefix %s", in_value.c_str());
|
|
|
|
|
ALOGE(
|
|
|
|
|
"ATSC license input does not conform to expectations. Key set does "
|
|
|
|
|
"not have a valid ATSC Key set prefix %s",
|
|
|
|
|
in_value.c_str());
|
|
|
|
|
return WvStatus(Status::BAD_VALUE);
|
|
|
|
|
}
|
|
|
|
|
const char kColon = ':';
|
|
|
|
|
const size_t pos = in_value.find(kColon);
|
|
|
|
|
if (pos == std::string::npos) {
|
|
|
|
|
ALOGE("ATSC license input does not conform to expectations. Missing colon "
|
|
|
|
|
"= %s", in_value.c_str());
|
|
|
|
|
ALOGE(
|
|
|
|
|
"ATSC license input does not conform to expectations. Missing colon "
|
|
|
|
|
"= %s",
|
|
|
|
|
in_value.c_str());
|
|
|
|
|
return WvStatus(Status::BAD_VALUE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pos == in_value.length()) {
|
|
|
|
|
ALOGE("ATSC license input does not conform to expectations. No data after "
|
|
|
|
|
"colon");
|
|
|
|
|
ALOGE(
|
|
|
|
|
"ATSC license input does not conform to expectations. No data after "
|
|
|
|
|
"colon");
|
|
|
|
|
return WvStatus(Status::BAD_VALUE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*key_set_id = in_value.substr(0, pos);
|
|
|
|
|
const std::vector<uint8_t> license_data_binary =
|
|
|
|
|
wvutil::Base64Decode(in_value.substr(pos+1));
|
|
|
|
|
wvutil::Base64Decode(in_value.substr(pos + 1));
|
|
|
|
|
|
|
|
|
|
if (license_data_binary.empty()) {
|
|
|
|
|
ALOGE("ATSC license input does not conform to expectations. License data "
|
|
|
|
|
"failed to decode from Base64");
|
|
|
|
|
ALOGE(
|
|
|
|
|
"ATSC license input does not conform to expectations. License data "
|
|
|
|
|
"failed to decode from Base64");
|
|
|
|
|
return WvStatus(Status::BAD_VALUE);
|
|
|
|
|
}
|
|
|
|
|
serialized_license_data->assign(license_data_binary.begin(),
|
|
|
|
|
@@ -1879,7 +1952,6 @@ WvStatus WVDrmPlugin::parseAtscLicenseData(
|
|
|
|
|
return WvStatus(Status::OK);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
WvStatus WVDrmPlugin::mapAndNotifyOfCdmResponseType(
|
|
|
|
|
const vector<uint8_t>& sessionId, CdmResponseType res) {
|
|
|
|
|
notifyOfCdmResponseType(sessionId, res);
|
|
|
|
|
|