Implement MediaDrm offline license support in Widevine hidl service.
Merged from http://go/wvgerrit/69723. The new APIs are getOfflineLicenseIds, getOfflineLicenseState and removeOfflineLicense. These methods are currently stubbed out in Widevine hidl service. This CL completes the implementation. Test: unit tests - libwvdrmdrmplugin_hidl_test Test: GTS --test com.google.android.media.gts.MediaDrmTest#testWidevineApi29 bug: 117570686 Change-Id: I96ffb75f453e36e931effefd3664b5faa8d69d30
This commit is contained in:
@@ -212,6 +212,17 @@ class CdmEngine {
|
||||
CdmSecurityLevel security_level,
|
||||
const std::string& key_set_id);
|
||||
|
||||
// Get offline license status: active, release or unknown
|
||||
virtual CdmResponseType GetOfflineLicenseState(
|
||||
const std::string& key_set_id,
|
||||
CdmSecurityLevel security_level,
|
||||
CdmOfflineLicenseState* license_state);
|
||||
|
||||
// Remove offline license with the given key_set_id
|
||||
virtual CdmResponseType RemoveOfflineLicense(
|
||||
const std::string& key_set_id,
|
||||
CdmSecurityLevel security_level);
|
||||
|
||||
// Usage related methods for streaming licenses
|
||||
// Retrieve a random usage info from the list of all usage infos for this app
|
||||
// id.
|
||||
|
||||
@@ -194,7 +194,7 @@ class CdmSession {
|
||||
const std::string& signature);
|
||||
|
||||
virtual CdmResponseType SetDecryptHash(uint32_t frame_number,
|
||||
const std::string& hash);
|
||||
const std::string& hash);
|
||||
|
||||
virtual CdmResponseType GetDecryptHashError(std::string* hash_error_string);
|
||||
|
||||
|
||||
@@ -91,6 +91,7 @@ class DeviceFiles {
|
||||
const CdmUsageEntry& usage_entry,
|
||||
uint32_t usage_entry_number,
|
||||
ResponseType* result);
|
||||
|
||||
virtual bool RetrieveLicense(
|
||||
const std::string& key_set_id, LicenseState* state,
|
||||
CdmInitData* pssh_data, CdmKeyMessage* key_request,
|
||||
|
||||
@@ -40,6 +40,12 @@ enum CdmKeyRequestType {
|
||||
kKeyRequestTypeRelease,
|
||||
};
|
||||
|
||||
enum CdmOfflineLicenseState {
|
||||
kLicenseStateActive,
|
||||
kLicenseStateReleasing,
|
||||
kLicenseStateUnknown,
|
||||
};
|
||||
|
||||
enum CdmResponseType {
|
||||
NO_ERROR = 0,
|
||||
UNKNOWN_ERROR = 1,
|
||||
@@ -355,6 +361,11 @@ enum CdmResponseType {
|
||||
INVALID_LICENSE_TYPE_2 = 310,
|
||||
SIGNATURE_NOT_FOUND_2 = 311,
|
||||
SESSION_KEYS_NOT_FOUND_2 = 312,
|
||||
GET_OFFLINE_LICENSE_STATE_ERROR_1 = 313,
|
||||
GET_OFFLINE_LICENSE_STATE_ERROR_2 = 314,
|
||||
REMOVE_OFFLINE_LICENSE_ERROR_1 = 315,
|
||||
REMOVE_OFFLINE_LICENSE_ERROR_2 = 316,
|
||||
SESSION_NOT_FOUND_21 = 317,
|
||||
// Don't forget to add new values to ../test/test_printers.cpp.
|
||||
};
|
||||
|
||||
|
||||
@@ -27,6 +27,18 @@ namespace {
|
||||
const uint64_t kReleaseSessionTimeToLive = 60; // seconds
|
||||
const uint32_t kUpdateUsageInformationPeriod = 60; // seconds
|
||||
const size_t kUsageReportsPerRequest = 1;
|
||||
|
||||
wvcdm::CdmOfflineLicenseState MapDeviceFilesLicenseState(
|
||||
wvcdm::DeviceFiles::LicenseState state) {
|
||||
switch (state) {
|
||||
case wvcdm::DeviceFiles::LicenseState::kLicenseStateActive:
|
||||
return wvcdm::kLicenseStateActive;
|
||||
case wvcdm::DeviceFiles::LicenseState::kLicenseStateReleasing:
|
||||
return wvcdm::kLicenseStateReleasing;
|
||||
default:
|
||||
return wvcdm::kLicenseStateUnknown;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
@@ -1100,6 +1112,98 @@ CdmResponseType CdmEngine::DeleteUsageRecord(const std::string& app_id,
|
||||
return status;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GetOfflineLicenseState(
|
||||
const CdmKeySetId &key_set_id,
|
||||
CdmSecurityLevel security_level,
|
||||
CdmOfflineLicenseState* license_state) {
|
||||
DeviceFiles handle(file_system_);
|
||||
if (!handle.Init(security_level)) {
|
||||
LOGE("CdmEngine::GetOfflineLicenseState: cannot initialize device files");
|
||||
return GET_OFFLINE_LICENSE_STATE_ERROR_1;
|
||||
}
|
||||
|
||||
DeviceFiles::LicenseState state;
|
||||
|
||||
// Dummy arguments to make the RetrieveLicense call,
|
||||
// do not care about the return value except for license state.
|
||||
CdmAppParameterMap app_parameters;
|
||||
CdmKeyMessage key_request;
|
||||
CdmKeyResponse key_response;
|
||||
CdmInitData offline_init_data;
|
||||
CdmKeyMessage offline_key_renewal_request;
|
||||
CdmKeyResponse offline_key_renewal_response;
|
||||
CdmUsageEntry usage_entry;
|
||||
int64_t grace_period_end_time;
|
||||
int64_t last_playback_time;
|
||||
std::string offline_release_server_url;
|
||||
int64_t playback_start_time;
|
||||
uint32_t usage_entry_number;
|
||||
DeviceFiles::ResponseType sub_error_code = DeviceFiles::kNoError;
|
||||
|
||||
if (handle.RetrieveLicense(key_set_id, &state, &offline_init_data,
|
||||
&key_request, &key_response,
|
||||
&offline_key_renewal_request, &offline_key_renewal_response,
|
||||
&offline_release_server_url,
|
||||
&playback_start_time, &last_playback_time, &grace_period_end_time,
|
||||
&app_parameters, &usage_entry, &usage_entry_number,
|
||||
&sub_error_code)) {
|
||||
*license_state = MapDeviceFilesLicenseState(state);
|
||||
} else {
|
||||
LOGE("CdmEngine::GetOfflineLicenseState:: failed to retrieve license state "
|
||||
"key set id = %s",
|
||||
key_set_id.c_str());
|
||||
return GET_OFFLINE_LICENSE_STATE_ERROR_2;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::RemoveOfflineLicense(
|
||||
const CdmKeySetId &key_set_id,
|
||||
CdmSecurityLevel security_level) {
|
||||
|
||||
UsagePropertySet property_set;
|
||||
property_set.set_security_level(
|
||||
security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault);
|
||||
DeviceFiles handle(file_system_);
|
||||
CdmResponseType sts = OpenKeySetSession(key_set_id,
|
||||
&property_set, nullptr /* event listener */);
|
||||
if (sts != NO_ERROR) {
|
||||
if (!handle.Init(security_level)) {
|
||||
LOGE("CdmEngine::RemoveOfflineLicense: cannot initialize device files");
|
||||
}
|
||||
handle.DeleteLicense(key_set_id);
|
||||
return REMOVE_OFFLINE_LICENSE_ERROR_1;
|
||||
}
|
||||
|
||||
CdmSessionId session_id;
|
||||
CdmAppParameterMap dummy_app_params;
|
||||
InitializationData dummy_init_data("", "", "");
|
||||
CdmKeyRequest key_request;
|
||||
// Calling with no session_id is okay
|
||||
sts = GenerateKeyRequest(session_id, key_set_id, dummy_init_data,
|
||||
kLicenseTypeRelease, dummy_app_params, &key_request);
|
||||
if (sts == KEY_MESSAGE) {
|
||||
std::unique_lock<std::mutex> lock(release_key_sets_lock_);
|
||||
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(key_set_id);
|
||||
if (iter == release_key_sets_.end()) {
|
||||
LOGE("CdmEngine::RemoveOfflineLicense: key set id not found = %s",
|
||||
key_set_id.c_str());
|
||||
sts = REMOVE_OFFLINE_LICENSE_ERROR_2;
|
||||
} else {
|
||||
session_id = iter->second.first;
|
||||
sts = RemoveLicense(session_id);
|
||||
}
|
||||
}
|
||||
|
||||
if (sts != NO_ERROR) {
|
||||
LOGE("CdmEngine: GenerateKeyRequest error=%d", sts);
|
||||
handle.DeleteLicense(key_set_id);
|
||||
}
|
||||
CloseKeySetSession(key_set_id);
|
||||
|
||||
return sts;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
|
||||
const CdmSecureStopId& ssid,
|
||||
CdmUsageInfo* usage_info) {
|
||||
|
||||
@@ -154,6 +154,12 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
|
||||
case GET_LICENSE_ERROR:
|
||||
*os << "GET_LICENSE_ERROR";
|
||||
break;
|
||||
case GET_OFFLINE_LICENSE_STATE_ERROR_1:
|
||||
*os << "GET_OFFLINE_LICENSE_STATE_ERROR_1";
|
||||
break;
|
||||
case GET_OFFLINE_LICENSE_STATE_ERROR_2:
|
||||
*os << "GET_OFFLINE_LICENSE_STATE_ERROR_2";
|
||||
break;
|
||||
case GET_RELEASED_LICENSE_ERROR:
|
||||
*os << "GET_RELEASED_LICENSE_ERROR";
|
||||
break;
|
||||
@@ -784,6 +790,9 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
|
||||
case SESSION_NOT_FOUND_20:
|
||||
*os << "SESSION_NOT_FOUND_20";
|
||||
break;
|
||||
case SESSION_NOT_FOUND_21:
|
||||
*os << "SESSION_NOT_FOUND_21";
|
||||
break;
|
||||
case INVALID_DECRYPT_HASH_FORMAT:
|
||||
*os << "INVALID_DECRYPT_HASH_FORMAT";
|
||||
break;
|
||||
@@ -825,6 +834,11 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
|
||||
break;
|
||||
case SESSION_KEYS_NOT_FOUND_2:
|
||||
*os << "SESSION_KEYS_NOT_FOUND_2";
|
||||
case REMOVE_OFFLINE_LICENSE_ERROR_1:
|
||||
*os << "REMOVE_OFFLINE_LICENSE_ERROR_1";
|
||||
break;
|
||||
case REMOVE_OFFLINE_LICENSE_ERROR_2:
|
||||
*os << "REMOVE_OFFLINE_LICENSE_ERROR_2";
|
||||
break;
|
||||
default:
|
||||
*os << "Unknown CdmResponseType";
|
||||
|
||||
@@ -147,6 +147,26 @@ class WvContentDecryptionModule : public android::RefBase, public TimerHandler {
|
||||
virtual CdmResponseType GetDecryptHashError(const CdmSessionId& session_id,
|
||||
std::string* hash_error_string);
|
||||
|
||||
// Return the list of key_set_ids stored on the current (origin-specific)
|
||||
// file system.
|
||||
virtual CdmResponseType ListStoredLicenses(
|
||||
CdmSecurityLevel security_level,
|
||||
const CdmIdentifier& identifier,
|
||||
std::vector<CdmKeySetId>* key_set_ids);
|
||||
|
||||
// Retrieve offline license state using key_set_id.
|
||||
virtual CdmResponseType GetOfflineLicenseState(
|
||||
const CdmKeySetId& key_set_id,
|
||||
CdmSecurityLevel security_level,
|
||||
const CdmIdentifier& identifier,
|
||||
CdmOfflineLicenseState* licenseState);
|
||||
|
||||
// Remove offline license using key_set_id.
|
||||
virtual CdmResponseType RemoveOfflineLicense(
|
||||
const CdmKeySetId& key_set_id,
|
||||
CdmSecurityLevel security_level,
|
||||
const CdmIdentifier& identifier);
|
||||
|
||||
private:
|
||||
struct CdmInfo {
|
||||
CdmInfo();
|
||||
|
||||
@@ -525,4 +525,32 @@ uint32_t WvContentDecryptionModule::GenerateSessionSharingId() {
|
||||
return ++next_session_sharing_id;
|
||||
}
|
||||
|
||||
CdmResponseType WvContentDecryptionModule::ListStoredLicenses(
|
||||
CdmSecurityLevel security_level,
|
||||
const CdmIdentifier& identifier,
|
||||
std::vector<CdmKeySetId>* key_set_ids) {
|
||||
CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier);
|
||||
return cdm_engine->ListStoredLicenses(
|
||||
security_level, key_set_ids);
|
||||
}
|
||||
|
||||
CdmResponseType WvContentDecryptionModule::GetOfflineLicenseState(
|
||||
const CdmKeySetId& key_set_id,
|
||||
CdmSecurityLevel security_level,
|
||||
const CdmIdentifier& identifier,
|
||||
CdmOfflineLicenseState* license_state) {
|
||||
CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier);
|
||||
return cdm_engine->GetOfflineLicenseState(
|
||||
key_set_id, security_level, license_state);
|
||||
}
|
||||
|
||||
CdmResponseType WvContentDecryptionModule::RemoveOfflineLicense(
|
||||
const CdmKeySetId& key_set_id,
|
||||
CdmSecurityLevel security_level,
|
||||
const CdmIdentifier& identifier) {
|
||||
CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier);
|
||||
return cdm_engine->RemoveOfflineLicense(
|
||||
key_set_id, security_level);
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -235,6 +235,7 @@ static android::status_t mapCdmResponseType(wvcdm::CdmResponseType res) {
|
||||
case wvcdm::SESSION_NOT_FOUND_18:
|
||||
case wvcdm::SESSION_NOT_FOUND_19:
|
||||
case wvcdm::SESSION_NOT_FOUND_20:
|
||||
case wvcdm::SESSION_NOT_FOUND_21:
|
||||
return android::ERROR_DRM_SESSION_NOT_OPENED;
|
||||
case wvcdm::SESSION_KEYS_NOT_FOUND:
|
||||
return kSessionKeysNotFound;
|
||||
@@ -557,6 +558,15 @@ static android::status_t mapCdmResponseType(wvcdm::CdmResponseType res) {
|
||||
return kSignatureNotFound2;
|
||||
case wvcdm::SESSION_KEYS_NOT_FOUND_2:
|
||||
return kSessionKeysNotFound2;
|
||||
|
||||
// This error is only returned in API 29 by the hidl service.
|
||||
// It should never be used in the legacy plugin.
|
||||
// It is mapped here to clear the compiler warning.
|
||||
case wvcdm::GET_OFFLINE_LICENSE_STATE_ERROR_1:
|
||||
case wvcdm::GET_OFFLINE_LICENSE_STATE_ERROR_2:
|
||||
case wvcdm::REMOVE_OFFLINE_LICENSE_ERROR_1:
|
||||
case wvcdm::REMOVE_OFFLINE_LICENSE_ERROR_2:
|
||||
return android::ERROR_DRM_UNKNOWN;
|
||||
}
|
||||
|
||||
// Return here instead of as a default case so that the compiler will warn
|
||||
|
||||
@@ -63,6 +63,7 @@ static Status mapCdmResponseType(wvcdm::CdmResponseType res) {
|
||||
case wvcdm::SESSION_NOT_FOUND_18:
|
||||
case wvcdm::SESSION_NOT_FOUND_19:
|
||||
case wvcdm::SESSION_NOT_FOUND_20:
|
||||
case wvcdm::SESSION_NOT_FOUND_21:
|
||||
return Status::ERROR_DRM_SESSION_NOT_OPENED;
|
||||
|
||||
case wvcdm::DECRYPT_ERROR:
|
||||
@@ -321,6 +322,10 @@ static Status mapCdmResponseType(wvcdm::CdmResponseType res) {
|
||||
case wvcdm::INVALID_LICENSE_TYPE_2:
|
||||
case wvcdm::SIGNATURE_NOT_FOUND_2:
|
||||
case wvcdm::SESSION_KEYS_NOT_FOUND_2:
|
||||
case wvcdm::GET_OFFLINE_LICENSE_STATE_ERROR_1:
|
||||
case wvcdm::GET_OFFLINE_LICENSE_STATE_ERROR_2:
|
||||
case wvcdm::REMOVE_OFFLINE_LICENSE_ERROR_1:
|
||||
case wvcdm::REMOVE_OFFLINE_LICENSE_ERROR_2:
|
||||
ALOGW("Returns UNKNOWN error for legacy status: %d", res);
|
||||
return Status::ERROR_DRM_UNKNOWN;
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ using wvcdm::CdmProvisioningRequest;
|
||||
using wvcdm::CdmProvisioningResponse;
|
||||
using wvcdm::CdmQueryMap;
|
||||
using wvcdm::CdmSecureStopId;
|
||||
using wvcdm::CdmSecurityLevel;
|
||||
using wvcdm::CdmUsageInfo;
|
||||
using wvcdm::CdmUsageInfoReleaseMessage;
|
||||
using wvcdm::KeyId;
|
||||
@@ -132,7 +133,7 @@ KeyStatusType ConvertFromCdmKeyStatus(CdmKeyStatus keyStatus) {
|
||||
}
|
||||
}
|
||||
|
||||
HdcpLevel mapHdcpLevel(const std::string level) {
|
||||
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)
|
||||
@@ -1048,20 +1049,91 @@ Return<void> WVDrmPlugin::getSecurityLevel(
|
||||
|
||||
Return<void> WVDrmPlugin::getOfflineLicenseKeySetIds(
|
||||
getOfflineLicenseKeySetIds_cb _hidl_cb) {
|
||||
std::vector<KeySetId> keySetIds;
|
||||
std::vector<std::vector<uint8_t> > keySetIds;
|
||||
std::vector<KeySetId> keySetIdsVec;
|
||||
|
||||
_hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, keySetIds);
|
||||
CdmIdentifier identifier;
|
||||
Status status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
|
||||
if (status != Status::OK) {
|
||||
_hidl_cb(status, toHidlVec(keySetIdsVec));
|
||||
return Void();
|
||||
}
|
||||
|
||||
std::vector<CdmSecurityLevel> levels = {
|
||||
wvcdm::kSecurityLevelL1, wvcdm::kSecurityLevelL3 };
|
||||
|
||||
CdmResponseType res = wvcdm::UNKNOWN_ERROR;
|
||||
|
||||
for (auto level : levels) {
|
||||
std::vector<CdmKeySetId> cdmKeySetIds;
|
||||
res = mCDM->ListStoredLicenses(level, identifier, &cdmKeySetIds);
|
||||
|
||||
if (isCdmResponseTypeSuccess(res)) {
|
||||
keySetIds.clear();
|
||||
for (auto id : cdmKeySetIds) {
|
||||
keySetIds.push_back(StrToVector(id));
|
||||
}
|
||||
for (auto id : keySetIds) {
|
||||
keySetIdsVec.push_back(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
_hidl_cb(mapCdmResponseType(res), toHidlVec(keySetIdsVec));
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> WVDrmPlugin::getOfflineLicenseState(const KeySetId &keySetId,
|
||||
getOfflineLicenseState_cb _hidl_cb) {
|
||||
OfflineLicenseState licenseState = OfflineLicenseState::UNKNOWN;
|
||||
|
||||
if (!keySetId.size()) {
|
||||
_hidl_cb(Status::BAD_VALUE, OfflineLicenseState::UNKNOWN);
|
||||
_hidl_cb(Status::BAD_VALUE, licenseState);
|
||||
return Void();
|
||||
}
|
||||
|
||||
_hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, OfflineLicenseState::UNKNOWN);
|
||||
CdmIdentifier identifier;
|
||||
Status status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
|
||||
if (status != Status::OK) {
|
||||
_hidl_cb(status, licenseState);
|
||||
return Void();
|
||||
}
|
||||
|
||||
CdmResponseType res = wvcdm::UNKNOWN_ERROR;
|
||||
CdmKeySetId keySetIdStr(keySetId.begin(), 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)) {
|
||||
_hidl_cb(Status::BAD_VALUE, licenseState);
|
||||
return Void();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
_hidl_cb(mapCdmResponseType(res), licenseState);
|
||||
return Void();
|
||||
}
|
||||
|
||||
@@ -1070,7 +1142,31 @@ Return<Status> WVDrmPlugin::removeOfflineLicense(const KeySetId &keySetId) {
|
||||
return Status::BAD_VALUE;
|
||||
}
|
||||
|
||||
return Status::ERROR_DRM_CANNOT_HANDLE;
|
||||
CdmIdentifier identifier;
|
||||
Status status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
|
||||
if (status != Status::OK) {
|
||||
return status;
|
||||
}
|
||||
|
||||
CdmResponseType res = wvcdm::UNKNOWN_ERROR;
|
||||
|
||||
res = mCDM->RemoveOfflineLicense(
|
||||
std::string(keySetId.begin(), keySetId.end()),
|
||||
wvcdm::kSecurityLevelL1,
|
||||
identifier);
|
||||
if (!isCdmResponseTypeSuccess(res)) {
|
||||
CdmResponseType res = mCDM->RemoveOfflineLicense(
|
||||
std::string(keySetId.begin(), keySetId.end()),
|
||||
wvcdm::kSecurityLevelL3,
|
||||
identifier);
|
||||
if (isCdmResponseTypeSuccess(res)) {
|
||||
status = Status::OK;
|
||||
} else {
|
||||
status = Status::ERROR_DRM_INVALID_STATE;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
Return<void> WVDrmPlugin::getPropertyString(const hidl_string& propertyName,
|
||||
|
||||
@@ -78,6 +78,7 @@ using wvcdm::CdmKeyRequest;
|
||||
using wvcdm::CdmKeySetId;
|
||||
using wvcdm::CdmKeySystem;
|
||||
using wvcdm::CdmLicenseType;
|
||||
using wvcdm::CdmOfflineLicenseState;
|
||||
using wvcdm::CdmProvisioningRequest;
|
||||
using wvcdm::CdmProvisioningResponse;
|
||||
using wvcdm::CdmQueryMap;
|
||||
@@ -159,7 +160,8 @@ class MockCDM : public WvContentDecryptionModule {
|
||||
MOCK_METHOD2(RestoreKey, CdmResponseType(const CdmSessionId&,
|
||||
const CdmKeySetId&));
|
||||
|
||||
MOCK_METHOD3(QueryStatus, CdmResponseType(wvcdm::SecurityLevel, const std::string&,
|
||||
MOCK_METHOD3(QueryStatus, CdmResponseType(wvcdm::SecurityLevel,
|
||||
const std::string&,
|
||||
std::string*));
|
||||
|
||||
MOCK_METHOD2(QueryKeyStatus, CdmResponseType(const CdmSessionId&,
|
||||
@@ -202,7 +204,24 @@ class MockCDM : public WvContentDecryptionModule {
|
||||
MOCK_METHOD2(GetMetrics, CdmResponseType(const CdmIdentifier&,
|
||||
drm_metrics::WvCdmMetrics*));
|
||||
|
||||
MOCK_METHOD2(GetDecryptHashError, CdmResponseType(const CdmSessionId&, std::string*));
|
||||
MOCK_METHOD2(GetDecryptHashError,
|
||||
CdmResponseType(const CdmSessionId&, std::string*));
|
||||
|
||||
MOCK_METHOD3(ListStoredLicenses,
|
||||
CdmResponseType(CdmSecurityLevel,
|
||||
const CdmIdentifier&,
|
||||
std::vector<std::string>*));
|
||||
|
||||
MOCK_METHOD4(GetOfflineLicenseState,
|
||||
CdmResponseType(const std::string&,
|
||||
CdmSecurityLevel,
|
||||
const CdmIdentifier&,
|
||||
CdmOfflineLicenseState*));
|
||||
|
||||
MOCK_METHOD3(RemoveOfflineLicense,
|
||||
CdmResponseType(const std::string&,
|
||||
CdmSecurityLevel,
|
||||
const CdmIdentifier&));
|
||||
};
|
||||
|
||||
class MockCrypto : public WVGenericCryptoInterface {
|
||||
@@ -260,8 +279,11 @@ MATCHER_P(HasOrigin, origin, "") {
|
||||
|
||||
class WVDrmPluginTest : public Test {
|
||||
protected:
|
||||
static const uint32_t kKeySetIdSize = 32;
|
||||
static const uint32_t kSessionIdSize = 16;
|
||||
uint8_t keySetIdRaw[kKeySetIdSize];
|
||||
uint8_t sessionIdRaw[kSessionIdSize];
|
||||
std::vector<uint8_t> keySetId;
|
||||
std::vector<uint8_t> sessionId;
|
||||
CdmSessionId cdmSessionId;
|
||||
|
||||
@@ -269,12 +291,19 @@ class WVDrmPluginTest : public Test {
|
||||
// Fill the session ID
|
||||
FILE* fp = fopen("/dev/urandom", "r");
|
||||
fread(sessionIdRaw, sizeof(uint8_t), kSessionIdSize, fp);
|
||||
fclose(fp);
|
||||
|
||||
memcpy(sessionIdRaw, SESSION_ID_PREFIX, sizeof(SESSION_ID_PREFIX) - 1);
|
||||
sessionId.assign(sessionIdRaw, sessionIdRaw + kSessionIdSize);
|
||||
cdmSessionId.assign(sessionId.begin(), sessionId.end());
|
||||
|
||||
fread(keySetIdRaw, sizeof(uint8_t), kKeySetIdSize, fp);
|
||||
fclose(fp);
|
||||
|
||||
memcpy(keySetIdRaw, KEY_SET_ID_PREFIX, sizeof(KEY_SET_ID_PREFIX));
|
||||
CdmKeySetId cdmKeySetId(reinterpret_cast<char*>(keySetIdRaw),
|
||||
kKeySetIdSize);
|
||||
keySetId.assign(keySetIdRaw, keySetIdRaw + kKeySetIdSize);
|
||||
|
||||
// Set default return values for gMock
|
||||
DefaultValue<CdmResponseType>::Set(wvcdm::NO_ERROR);
|
||||
DefaultValue<OEMCryptoResult>::Set(OEMCrypto_SUCCESS);
|
||||
@@ -728,15 +757,6 @@ TEST_F(WVDrmPluginTest, RestoresKeys) {
|
||||
StrictMock<MockCrypto> crypto;
|
||||
std::string appPackageName;
|
||||
|
||||
static const size_t kKeySetIdSize = 32;
|
||||
uint8_t keySetIdRaw[kKeySetIdSize];
|
||||
FILE* fp = fopen("/dev/urandom", "r");
|
||||
fread(keySetIdRaw, sizeof(uint8_t), kKeySetIdSize, fp);
|
||||
fclose(fp);
|
||||
|
||||
std::vector<uint8_t> keySetId;
|
||||
keySetId.assign(keySetIdRaw, keySetIdRaw + kKeySetIdSize);
|
||||
|
||||
EXPECT_CALL(*cdm, RestoreKey(cdmSessionId,
|
||||
ElementsAreArray(keySetIdRaw, kKeySetIdSize)))
|
||||
.Times(1);
|
||||
@@ -1039,7 +1059,7 @@ TEST_F(WVDrmPluginTest, GetsSecureStops) {
|
||||
.WillOnce(DoAll(SetArgPointee<2>(cdmStops),
|
||||
testing::Return(wvcdm::NO_ERROR)));
|
||||
|
||||
std::list<std::vector<uint8_t> > stops;
|
||||
std::vector<std::vector<uint8_t> > stops;
|
||||
|
||||
WVDrmPlugin plugin(cdm.get(), appPackageName, &crypto, false);
|
||||
Status status = plugin.setPropertyString(hidl_string("appId"),
|
||||
@@ -1058,17 +1078,11 @@ TEST_F(WVDrmPluginTest, GetsSecureStops) {
|
||||
}
|
||||
});
|
||||
|
||||
std::list<std::vector<uint8_t> >::iterator iter = stops.begin();
|
||||
uint32_t rawIter = 0;
|
||||
while (rawIter < kStopCount && iter != stops.end()) {
|
||||
EXPECT_THAT(*iter, ElementsAreArray(stopsRaw[rawIter], kStopSize));
|
||||
|
||||
++iter;
|
||||
++rawIter;
|
||||
size_t index = 0;
|
||||
for (auto stop : stops) {
|
||||
EXPECT_THAT(stop, ElementsAreArray(stopsRaw[index++], kStopSize));
|
||||
}
|
||||
// Assert that both lists are the same length
|
||||
EXPECT_EQ(kStopCount, rawIter);
|
||||
EXPECT_EQ(stops.end(), iter);
|
||||
EXPECT_EQ(kStopCount, index);
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, ReleasesAllSecureStops) {
|
||||
@@ -2715,6 +2729,124 @@ TEST_F(WVDrmPluginTest, DoesNotSetDecryptHashProperties) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, GetOfflineLicenseIds) {
|
||||
android::sp<StrictMock<MockCDM>> cdm = new StrictMock<MockCDM>();
|
||||
StrictMock<MockCrypto> crypto;
|
||||
std::string appPackageName;
|
||||
|
||||
const uint32_t kLicenseCount = 5;
|
||||
|
||||
uint8_t mockIdsRaw[kLicenseCount * 2][kKeySetIdSize];
|
||||
FILE* fp = fopen("/dev/urandom", "r");
|
||||
for (uint32_t i = 0; i < kLicenseCount * 2; ++i) {
|
||||
fread(mockIdsRaw[i], sizeof(uint8_t), kKeySetIdSize, fp);
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
std::vector<std::string> mockIdsL1;
|
||||
for (uint32_t i = 0; i < kLicenseCount; ++i) {
|
||||
mockIdsL1.push_back(std::string(mockIdsRaw[i],
|
||||
mockIdsRaw[i] + kKeySetIdSize));
|
||||
}
|
||||
|
||||
std::vector<std::string> mockIdsL3;
|
||||
for (uint32_t i = 0; i < kLicenseCount; ++i) {
|
||||
mockIdsL3.push_back(std::string(mockIdsRaw[i+5],
|
||||
mockIdsRaw[i+5] + kKeySetIdSize));
|
||||
}
|
||||
|
||||
EXPECT_CALL(*cdm,
|
||||
ListStoredLicenses(kSecurityLevelL1, HasOrigin(EMPTY_ORIGIN), _))
|
||||
.WillOnce(DoAll(SetArgPointee<2>(mockIdsL1),
|
||||
testing::Return(wvcdm::NO_ERROR)));
|
||||
|
||||
EXPECT_CALL(*cdm,
|
||||
ListStoredLicenses(kSecurityLevelL3, HasOrigin(EMPTY_ORIGIN), _))
|
||||
.WillOnce(DoAll(SetArgPointee<2>(mockIdsL3),
|
||||
testing::Return(wvcdm::NO_ERROR)));
|
||||
|
||||
WVDrmPlugin plugin(cdm.get(), appPackageName, &crypto, false);
|
||||
|
||||
std::vector<std::vector<uint8_t> > offlineIds;
|
||||
offlineIds.clear();
|
||||
plugin.getOfflineLicenseKeySetIds(
|
||||
[&](Status status, hidl_vec<KeySetId> hKeySetIds) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
|
||||
std::vector<KeySetId> ids(hKeySetIds);
|
||||
|
||||
for (auto id : ids) {
|
||||
offlineIds.push_back(id);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
size_t index = 0;
|
||||
for (auto id : offlineIds) {
|
||||
EXPECT_THAT(id, ElementsAreArray(mockIdsRaw[index++], kKeySetIdSize));
|
||||
}
|
||||
EXPECT_EQ(kLicenseCount * 2, index);
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, GetOfflineLicenseState) {
|
||||
android::sp<StrictMock<MockCDM>> cdm = new StrictMock<MockCDM>();
|
||||
StrictMock<MockCrypto> crypto;
|
||||
std::string appPackageName;
|
||||
|
||||
EXPECT_CALL(*cdm, QueryStatus(_, QUERY_KEY_SECURITY_LEVEL, _))
|
||||
.WillRepeatedly(DoAll(SetArgPointee<2>(QUERY_VALUE_SECURITY_LEVEL_L1),
|
||||
testing::Return(wvcdm::NO_ERROR)));
|
||||
|
||||
EXPECT_CALL(*cdm,
|
||||
GetOfflineLicenseState(_, kSecurityLevelL1,
|
||||
HasOrigin(EMPTY_ORIGIN), _))
|
||||
.WillOnce(DoAll(SetArgPointee<3>(wvcdm::kLicenseStateActive),
|
||||
testing::Return(wvcdm::NO_ERROR)))
|
||||
.WillOnce(DoAll(SetArgPointee<3>(wvcdm::kLicenseStateReleasing),
|
||||
testing::Return(wvcdm::NO_ERROR)))
|
||||
.WillOnce(DoAll(SetArgPointee<3>(wvcdm::kLicenseStateUnknown),
|
||||
testing::Return(wvcdm::NO_ERROR)));
|
||||
|
||||
WVDrmPlugin plugin(cdm.get(), appPackageName, &crypto, false);
|
||||
Status status = plugin.setPropertyString(
|
||||
hidl_string("securityLevel"), hidl_string("L1"));
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
|
||||
plugin.getOfflineLicenseState(toHidlVec(keySetId),
|
||||
[&](Status status, OfflineLicenseState hLicenseState) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
ASSERT_EQ(OfflineLicenseState::USABLE, hLicenseState);
|
||||
});
|
||||
|
||||
plugin.getOfflineLicenseState(toHidlVec(keySetId),
|
||||
[&](Status status, OfflineLicenseState hLicenseState) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
ASSERT_EQ(OfflineLicenseState::INACTIVE, hLicenseState);
|
||||
});
|
||||
|
||||
plugin.getOfflineLicenseState(toHidlVec(keySetId),
|
||||
[&](Status status, OfflineLicenseState hLicenseState) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
ASSERT_EQ(OfflineLicenseState::UNKNOWN, hLicenseState);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, RemoveOfflineLicense) {
|
||||
android::sp<StrictMock<MockCDM>> cdm = new StrictMock<MockCDM>();
|
||||
StrictMock<MockCrypto> crypto;
|
||||
std::string appPackageName;
|
||||
|
||||
EXPECT_CALL(*cdm,
|
||||
RemoveOfflineLicense(_, kSecurityLevelL1,
|
||||
HasOrigin(EMPTY_ORIGIN)))
|
||||
.Times(1);
|
||||
|
||||
WVDrmPlugin plugin(cdm.get(), appPackageName, &crypto, false);
|
||||
|
||||
Status status = plugin.removeOfflineLicense(toHidlVec(keySetId));
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
} // namespace V1_2
|
||||
} // namespace drm
|
||||
|
||||
Reference in New Issue
Block a user