Create unique cdm engines per WVDrmPlugin instance
This change creates a unique id in the cdm identifier in order to force a one-to-one mapping between WVDrmPlugin instances and CDM Engines. This change simplifies some assumptions. This includes ensuring that the metrics for a given MediaDrm instance map to a given CdmEngine instance. This change contains the original change go/ag/3819203 and a fix to the deadlock that was seen on Marlin and Taimen. Bug: 73724453 Test: Updated unit tests. GTS test pass. Shaka Player, Netflix and Google Play test. Change-Id: Ib208204a1b794df9f306fa11d13a8bb6cd6889f7
This commit is contained in:
@@ -32,12 +32,28 @@ struct CdmIdentifier {
|
|||||||
// provide a friendly name of the application package for the purposes of
|
// provide a friendly name of the application package for the purposes of
|
||||||
// logging and metrics.
|
// logging and metrics.
|
||||||
std::string app_package_name;
|
std::string app_package_name;
|
||||||
|
|
||||||
|
// The unique identifier guarantees that no two identifiers share the same
|
||||||
|
// CdmEngine instance. We're moving to a model where a plugin maps 1 to 1
|
||||||
|
// with a CdmEngine instance. This is a simple way to implement that.
|
||||||
|
uint32_t unique_id;
|
||||||
|
|
||||||
|
// This method is needed to check to see if the identifier is equivalent
|
||||||
|
// to the default cdm. E.g. no spoid, origin or app package name. Use this
|
||||||
|
// comparison in lieu of the == operator when checking to see if the
|
||||||
|
// identifier would cause the default provisioned certificate to be used.
|
||||||
|
bool IsEquivalentToDefault() {
|
||||||
|
return spoid == EMPTY_SPOID
|
||||||
|
&& origin == EMPTY_ORIGIN
|
||||||
|
&& app_package_name == EMPTY_APP_PACKAGE_NAME;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Provide comparison operators
|
// Provide comparison operators
|
||||||
inline bool operator==(const CdmIdentifier& lhs, const CdmIdentifier& rhs) {
|
inline bool operator==(const CdmIdentifier& lhs, const CdmIdentifier& rhs) {
|
||||||
return lhs.spoid == rhs.spoid && lhs.origin == rhs.origin
|
return lhs.spoid == rhs.spoid && lhs.origin == rhs.origin
|
||||||
&& lhs.app_package_name == rhs.app_package_name;
|
&& lhs.app_package_name == rhs.app_package_name
|
||||||
|
&& lhs.unique_id == rhs.unique_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool operator!=(const CdmIdentifier& lhs, const CdmIdentifier& rhs) {
|
inline bool operator!=(const CdmIdentifier& lhs, const CdmIdentifier& rhs) {
|
||||||
@@ -49,7 +65,9 @@ inline bool operator<(const CdmIdentifier& lhs, const CdmIdentifier& rhs) {
|
|||||||
|| ((lhs.spoid == rhs.spoid)
|
|| ((lhs.spoid == rhs.spoid)
|
||||||
&& (lhs.origin < rhs.origin
|
&& (lhs.origin < rhs.origin
|
||||||
|| (lhs.origin == rhs.origin
|
|| (lhs.origin == rhs.origin
|
||||||
&& lhs.app_package_name < rhs.app_package_name)));
|
&& (lhs.app_package_name < rhs.app_package_name
|
||||||
|
|| (lhs.app_package_name == rhs.app_package_name
|
||||||
|
&& lhs.unique_id < rhs.unique_id)))));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool operator>(const CdmIdentifier& lhs, const CdmIdentifier& rhs) {
|
inline bool operator>(const CdmIdentifier& lhs, const CdmIdentifier& rhs) {
|
||||||
@@ -68,7 +86,8 @@ inline bool operator>=(const CdmIdentifier& lhs, const CdmIdentifier& rhs) {
|
|||||||
static const CdmIdentifier kDefaultCdmIdentifier = {
|
static const CdmIdentifier kDefaultCdmIdentifier = {
|
||||||
EMPTY_SPOID,
|
EMPTY_SPOID,
|
||||||
EMPTY_ORIGIN,
|
EMPTY_ORIGIN,
|
||||||
EMPTY_APP_PACKAGE_NAME
|
EMPTY_APP_PACKAGE_NAME,
|
||||||
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace wvcdm
|
} // namespace wvcdm
|
||||||
|
|||||||
@@ -129,9 +129,14 @@ class WvContentDecryptionModule : public android::RefBase, public TimerHandler {
|
|||||||
// Validate a passed-in service certificate
|
// Validate a passed-in service certificate
|
||||||
virtual bool IsValidServiceCertificate(const std::string& certificate);
|
virtual bool IsValidServiceCertificate(const std::string& certificate);
|
||||||
|
|
||||||
// Retrieve the serialized metrics from CdmEngine and CdmSession instances
|
// Fill the metrics parameter with the metrics data for the CdmEngine
|
||||||
// that have been closed.
|
// associated with the given CdmIdentifier. If the CdmEngine instance does
|
||||||
virtual void GetSerializedMetrics(std::string* serialized_metrics);
|
// not exist, this will return an error.
|
||||||
|
virtual CdmResponseType GetMetrics(const CdmIdentifier& identifier,
|
||||||
|
drm_metrics::WvCdmMetrics* metrics);
|
||||||
|
|
||||||
|
// Closes the CdmEngine and sessions associated with the given CdmIdentifier.
|
||||||
|
virtual CdmResponseType CloseCdm(const CdmIdentifier& identifier);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct CdmInfo {
|
struct CdmInfo {
|
||||||
@@ -147,17 +152,16 @@ class WvContentDecryptionModule : public android::RefBase, public TimerHandler {
|
|||||||
// Finds the CdmEngine instance for the given session id, returning NULL if
|
// Finds the CdmEngine instance for the given session id, returning NULL if
|
||||||
// not found.
|
// not found.
|
||||||
CdmEngine* GetCdmForSessionId(const std::string& session_id);
|
CdmEngine* GetCdmForSessionId(const std::string& session_id);
|
||||||
// Closes CdmEngine instances that don't have any open sessions. Also stores
|
|
||||||
// metrics data for closed CdmEngine instances.
|
// Close all of the open CdmEngine instances. This is used when ready to close
|
||||||
// Callers must acquire the cdms_lock_ before calling this method.
|
// the WvContentDecryptionModule instance.
|
||||||
void CloseCdmsWithoutSessions();
|
void CloseAllCdms();
|
||||||
|
|
||||||
uint32_t GenerateSessionSharingId();
|
uint32_t GenerateSessionSharingId();
|
||||||
|
|
||||||
// timer related methods to drive policy decisions
|
// timer related methods to drive policy decisions
|
||||||
void EnablePolicyTimer();
|
void EnablePolicyTimer();
|
||||||
void DisablePolicyTimer(); // Disable if all cdm engines are closed.
|
void DisablePolicyTimer();
|
||||||
void ForceDisablePolicyTimer(); // Force disable the policy timer.
|
|
||||||
void OnTimerEvent();
|
void OnTimerEvent();
|
||||||
|
|
||||||
static Lock session_sharing_id_generation_lock_;
|
static Lock session_sharing_id_generation_lock_;
|
||||||
@@ -172,9 +176,6 @@ class WvContentDecryptionModule : public android::RefBase, public TimerHandler {
|
|||||||
// This contains weak pointers to the CDM instances contained in |cdms_|.
|
// This contains weak pointers to the CDM instances contained in |cdms_|.
|
||||||
std::map<std::string, CdmEngine*> cdm_by_session_id_;
|
std::map<std::string, CdmEngine*> cdm_by_session_id_;
|
||||||
|
|
||||||
// The metrics for cdm engines and sessions that have been closed.
|
|
||||||
drm_metrics::WvCdmMetricsGroup metrics_group_;
|
|
||||||
|
|
||||||
CORE_DISALLOW_COPY_AND_ASSIGN(WvContentDecryptionModule);
|
CORE_DISALLOW_COPY_AND_ASSIGN(WvContentDecryptionModule);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ Lock WvContentDecryptionModule::session_sharing_id_generation_lock_;
|
|||||||
WvContentDecryptionModule::WvContentDecryptionModule() {}
|
WvContentDecryptionModule::WvContentDecryptionModule() {}
|
||||||
|
|
||||||
WvContentDecryptionModule::~WvContentDecryptionModule() {
|
WvContentDecryptionModule::~WvContentDecryptionModule() {
|
||||||
ForceDisablePolicyTimer();
|
CloseAllCdms();
|
||||||
|
DisablePolicyTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WvContentDecryptionModule::IsSupported(const std::string& init_data_type) {
|
bool WvContentDecryptionModule::IsSupported(const std::string& init_data_type) {
|
||||||
@@ -77,8 +78,6 @@ CdmResponseType WvContentDecryptionModule::CloseSession(
|
|||||||
cdm_by_session_id_.erase(session_id);
|
cdm_by_session_id_.erase(session_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
DisablePolicyTimer();
|
|
||||||
|
|
||||||
return sts;
|
return sts;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,12 +335,21 @@ bool WvContentDecryptionModule::IsValidServiceCertificate(
|
|||||||
return cert.has_certificate();
|
return cert.has_certificate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WvContentDecryptionModule::GetSerializedMetrics(
|
|
||||||
std::string* serialized_metrics) {
|
CdmResponseType WvContentDecryptionModule::GetMetrics(
|
||||||
|
const CdmIdentifier& identifier, drm_metrics::WvCdmMetrics* metrics) {
|
||||||
|
if (!metrics) {
|
||||||
|
return PARAMETER_NULL;
|
||||||
|
}
|
||||||
AutoLock auto_lock(cdms_lock_);
|
AutoLock auto_lock(cdms_lock_);
|
||||||
CloseCdmsWithoutSessions();
|
auto it = cdms_.find(identifier);
|
||||||
metrics_group_.SerializeToString(serialized_metrics);
|
if (it == cdms_.end()) {
|
||||||
metrics_group_.Clear();
|
LOGE("WVContentDecryptionModule::GetMetrics. cdm_identifier not found");
|
||||||
|
// TODO(blueeyes): Add a better error.
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
it->second.cdm_engine->GetMetrics()->Serialize(metrics);
|
||||||
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
WvContentDecryptionModule::CdmInfo::CdmInfo()
|
WvContentDecryptionModule::CdmInfo::CdmInfo()
|
||||||
@@ -377,30 +385,43 @@ CdmEngine* WvContentDecryptionModule::GetCdmForSessionId(
|
|||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method requires that the caller first acquire cdms_lock_.
|
void WvContentDecryptionModule::CloseAllCdms() {
|
||||||
void WvContentDecryptionModule::CloseCdmsWithoutSessions() {
|
AutoLock auto_lock(cdms_lock_);
|
||||||
for (auto it = cdms_.begin(); it != cdms_.end();) {
|
|
||||||
if (it->second.cdm_engine->SessionSize() != 0) {
|
|
||||||
++it;
|
|
||||||
} else {
|
|
||||||
// Retrieve the metrics from the engine and any completed
|
|
||||||
// sessions. Clear the metrics from any completed sessions.
|
|
||||||
metrics::EngineMetrics* engine_metrics =
|
|
||||||
it->second.cdm_engine->GetMetrics();
|
|
||||||
// engine_metrics should never be null.
|
|
||||||
if (engine_metrics != NULL) {
|
|
||||||
engine_metrics->Serialize(metrics_group_.add_metrics());
|
|
||||||
} else {
|
|
||||||
// Engine metrics should never be null.
|
|
||||||
LOGI(
|
|
||||||
"WvContentDecryptionModule::CloseCdmsWithoutSessions."
|
|
||||||
"engine_metrics was unexpectedly NULL.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// The CDM is no longer used for this identifier, delete it.
|
for (auto it = cdms_.begin(); it != cdms_.end();) {
|
||||||
it = cdms_.erase(it);
|
it = cdms_.erase(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CdmResponseType WvContentDecryptionModule::CloseCdm(
|
||||||
|
const CdmIdentifier& cdm_identifier) {
|
||||||
|
// The policy timer ultimately calls OnTimerEvent (which wants to
|
||||||
|
// acquire cdms_lock_). Therefore, we cannot acquire cdms_lock_ and then the
|
||||||
|
// policy_timer_lock_ (via DisablePolicyTimer) at the same time.
|
||||||
|
// Acquire the cdms_lock_ first, in its own scope.
|
||||||
|
bool cdms_empty = false;
|
||||||
|
{
|
||||||
|
AutoLock auto_lock(cdms_lock_);
|
||||||
|
auto it = cdms_.find(cdm_identifier);
|
||||||
|
if (it == cdms_.end()) {
|
||||||
|
LOGE("WVContentDecryptionModule::Close. cdm_identifier not found.");
|
||||||
|
// TODO(blueeyes): Create a better error.
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
// Remove any sessions that point to this engine.
|
||||||
|
for (auto session_it : cdm_by_session_id_) {
|
||||||
|
if (session_it.second == it->second.cdm_engine.get()) {
|
||||||
|
cdm_by_session_id_.erase(session_it.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cdms_.erase(it);
|
||||||
|
cdms_empty = cdms_.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cdms_empty) {
|
||||||
|
DisablePolicyTimer();
|
||||||
|
}
|
||||||
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WvContentDecryptionModule::EnablePolicyTimer() {
|
void WvContentDecryptionModule::EnablePolicyTimer() {
|
||||||
@@ -410,27 +431,6 @@ void WvContentDecryptionModule::EnablePolicyTimer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WvContentDecryptionModule::DisablePolicyTimer() {
|
void WvContentDecryptionModule::DisablePolicyTimer() {
|
||||||
bool cdms_is_empty = false;
|
|
||||||
{
|
|
||||||
AutoLock auto_lock(cdms_lock_);
|
|
||||||
CloseCdmsWithoutSessions();
|
|
||||||
cdms_is_empty = cdms_.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
AutoLock auto_lock(policy_timer_lock_);
|
|
||||||
if (cdms_is_empty) {
|
|
||||||
if (policy_timer_.IsRunning()) {
|
|
||||||
policy_timer_.Stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WvContentDecryptionModule::ForceDisablePolicyTimer() {
|
|
||||||
{
|
|
||||||
AutoLock auto_lock(cdms_lock_);
|
|
||||||
CloseCdmsWithoutSessions();
|
|
||||||
}
|
|
||||||
|
|
||||||
AutoLock auto_lock(policy_timer_lock_);
|
AutoLock auto_lock(policy_timer_lock_);
|
||||||
if (policy_timer_.IsRunning()) {
|
if (policy_timer_.IsRunning()) {
|
||||||
policy_timer_.Stop();
|
policy_timer_.Stop();
|
||||||
|
|||||||
@@ -607,12 +607,9 @@ class WvCdmExtendedDurationTest : public WvCdmTestBase {
|
|||||||
EXPECT_TRUE(license_renewal.has_key_control_nonce());
|
EXPECT_TRUE(license_renewal.has_key_control_nonce());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ValidateHasUpdateUsageEntry(const std::string& serialized_metrics)
|
void ValidateHasUpdateUsageEntry(const drm_metrics::WvCdmMetrics& metrics)
|
||||||
const {
|
const {
|
||||||
drm_metrics::WvCdmMetricsGroup group;
|
|
||||||
ASSERT_TRUE(group.ParseFromString(serialized_metrics));
|
|
||||||
bool has_update_usage_entry_metrics = false;
|
bool has_update_usage_entry_metrics = false;
|
||||||
for (const auto& metrics : group.metrics()) {
|
|
||||||
for (const auto& session : metrics.session_metrics()) {
|
for (const auto& session : metrics.session_metrics()) {
|
||||||
has_update_usage_entry_metrics |=
|
has_update_usage_entry_metrics |=
|
||||||
session.crypto_metrics()
|
session.crypto_metrics()
|
||||||
@@ -620,11 +617,11 @@ class WvCdmExtendedDurationTest : public WvCdmTestBase {
|
|||||||
has_update_usage_entry_metrics |=
|
has_update_usage_entry_metrics |=
|
||||||
session.crypto_metrics().oemcrypto_update_usage_entry().size() > 0;
|
session.crypto_metrics().oemcrypto_update_usage_entry().size() > 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
std::string serialized_metrics;
|
||||||
|
ASSERT_TRUE(metrics.SerializeToString(&serialized_metrics));
|
||||||
EXPECT_TRUE(has_update_usage_entry_metrics)
|
EXPECT_TRUE(has_update_usage_entry_metrics)
|
||||||
<< "metrics: " << wvcdm::b2a_hex(serialized_metrics);
|
<< "metrics: " << wvcdm::b2a_hex(serialized_metrics);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QueryKeyStatus(bool streaming, bool expect_renewal,
|
void QueryKeyStatus(bool streaming, bool expect_renewal,
|
||||||
@@ -1454,9 +1451,9 @@ TEST_P(WvCdmStreamingUsageReportTest, UsageTest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Validate that update usage table entry is exercised.
|
// Validate that update usage table entry is exercised.
|
||||||
std::string serialized_metrics;
|
drm_metrics::WvCdmMetrics metrics;
|
||||||
decryptor_.GetSerializedMetrics(&serialized_metrics);
|
ASSERT_EQ(NO_ERROR, decryptor_.GetMetrics(kDefaultCdmIdentifier, &metrics));
|
||||||
ValidateHasUpdateUsageEntry(serialized_metrics);
|
ValidateHasUpdateUsageEntry(metrics);
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(Cdm, WvCdmStreamingUsageReportTest,
|
INSTANTIATE_TEST_CASE_P(Cdm, WvCdmStreamingUsageReportTest,
|
||||||
|
|||||||
@@ -53,7 +53,8 @@ const int kHttpInternalServerError = 500;
|
|||||||
const wvcdm::CdmIdentifier kExampleIdentifier = {
|
const wvcdm::CdmIdentifier kExampleIdentifier = {
|
||||||
wvcdm::EMPTY_SPOID,
|
wvcdm::EMPTY_SPOID,
|
||||||
"com.example",
|
"com.example",
|
||||||
"com.example"
|
"com.example",
|
||||||
|
7
|
||||||
};
|
};
|
||||||
|
|
||||||
// Protobuf generated classes
|
// Protobuf generated classes
|
||||||
@@ -394,15 +395,19 @@ const uint32_t kSingleEncryptedSubSampleIcpLicenseExpirationWindow = 2;
|
|||||||
struct SessionSharingSubSampleInfo {
|
struct SessionSharingSubSampleInfo {
|
||||||
SubSampleInfo* sub_sample;
|
SubSampleInfo* sub_sample;
|
||||||
bool session_sharing_enabled;
|
bool session_sharing_enabled;
|
||||||
|
wvcdm::CdmIdentifier cdm_identifier;
|
||||||
};
|
};
|
||||||
|
|
||||||
SessionSharingSubSampleInfo session_sharing_sub_samples[] = {
|
SessionSharingSubSampleInfo session_sharing_sub_samples[] = {
|
||||||
{&clear_sub_sample, false},
|
{&clear_sub_sample, false, wvcdm::kDefaultCdmIdentifier},
|
||||||
{&clear_sub_sample, true},
|
{&clear_sub_sample, true, wvcdm::kDefaultCdmIdentifier},
|
||||||
{&clear_sub_sample_no_key, false},
|
{&clear_sub_sample_no_key, false, wvcdm::kDefaultCdmIdentifier},
|
||||||
{&clear_sub_sample_no_key, true},
|
{&clear_sub_sample_no_key, true, wvcdm::kDefaultCdmIdentifier},
|
||||||
{&single_encrypted_sub_sample, false},
|
{&single_encrypted_sub_sample, false, wvcdm::kDefaultCdmIdentifier},
|
||||||
{&single_encrypted_sub_sample, true}};
|
{&single_encrypted_sub_sample, true, wvcdm::kDefaultCdmIdentifier},
|
||||||
|
// The last entry simulates session sharing using the non default
|
||||||
|
// identifier.
|
||||||
|
{&single_encrypted_sub_sample, true, kExampleIdentifier}};
|
||||||
|
|
||||||
struct UsageInfoSubSampleInfo {
|
struct UsageInfoSubSampleInfo {
|
||||||
SubSampleInfo* sub_sample;
|
SubSampleInfo* sub_sample;
|
||||||
@@ -1303,6 +1308,14 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase {
|
|||||||
GenerateKeyRequest(init_data, license_type, NULL);
|
GenerateKeyRequest(init_data, license_type, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GenerateKeyRequest(const std::string& init_data,
|
||||||
|
CdmLicenseType license_type,
|
||||||
|
const CdmIdentifier& identifier) {
|
||||||
|
CdmAppParameterMap app_parameters;
|
||||||
|
GenerateKeyRequest(wvcdm::KEY_MESSAGE, "video/mp4", init_data,
|
||||||
|
app_parameters, license_type, identifier, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
void GenerateKeyRequest(const std::string& init_data,
|
void GenerateKeyRequest(const std::string& init_data,
|
||||||
CdmLicenseType license_type,
|
CdmLicenseType license_type,
|
||||||
CdmClientPropertySet* property_set) {
|
CdmClientPropertySet* property_set) {
|
||||||
@@ -1344,7 +1357,13 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase {
|
|||||||
decryptor_.GenerateKeyRequest(
|
decryptor_.GenerateKeyRequest(
|
||||||
session_id_, key_set_id, init_data_type, init_data,
|
session_id_, key_set_id, init_data_type, init_data,
|
||||||
license_type, app_parameters, property_set,
|
license_type, app_parameters, property_set,
|
||||||
cdm_identifier, &key_request));
|
cdm_identifier, &key_request))
|
||||||
|
<< "session_id_ " << session_id_ << std::endl
|
||||||
|
<< "init_data (hex) " << wvcdm::b2a_hex(init_data) << std::endl
|
||||||
|
<< "key_set_id " << key_set_id << std::endl
|
||||||
|
<< "cdm_identifier.origin " << cdm_identifier.origin << std::endl
|
||||||
|
<< "cdm_identifier.app_package_name " << cdm_identifier.app_package_name << std::endl
|
||||||
|
<< "cdm_identifier.unique_id " << cdm_identifier.unique_id << std::endl;
|
||||||
key_msg_ = key_request.message;
|
key_msg_ = key_request.message;
|
||||||
EXPECT_EQ(0u, key_request.url.size());
|
EXPECT_EQ(0u, key_request.url.size());
|
||||||
}
|
}
|
||||||
@@ -4186,14 +4205,20 @@ class WvCdmSessionSharingTest
|
|||||||
TEST_P(WvCdmSessionSharingTest, SessionSharingTest) {
|
TEST_P(WvCdmSessionSharingTest, SessionSharingTest) {
|
||||||
SessionSharingSubSampleInfo* session_sharing_info = GetParam();
|
SessionSharingSubSampleInfo* session_sharing_info = GetParam();
|
||||||
|
|
||||||
|
CdmIdentifier cdm_identifier = session_sharing_info->cdm_identifier;
|
||||||
|
if (!cdm_identifier.IsEquivalentToDefault()) {
|
||||||
|
Provision(session_sharing_info->cdm_identifier, kLevelDefault);
|
||||||
|
}
|
||||||
|
|
||||||
TestWvCdmClientPropertySet property_set;
|
TestWvCdmClientPropertySet property_set;
|
||||||
property_set.set_session_sharing_mode(
|
property_set.set_session_sharing_mode(
|
||||||
session_sharing_info->session_sharing_enabled);
|
session_sharing_info->session_sharing_enabled);
|
||||||
|
|
||||||
decryptor_.OpenSession(g_key_system, &property_set, kDefaultCdmIdentifier,
|
ASSERT_EQ(NO_ERROR, decryptor_.OpenSession(g_key_system, &property_set,
|
||||||
NULL, &session_id_);
|
cdm_identifier, NULL,
|
||||||
|
&session_id_));
|
||||||
CdmSessionId gp_session_id_1 = session_id_;
|
CdmSessionId gp_session_id_1 = session_id_;
|
||||||
GenerateKeyRequest(g_key_id, kLicenseTypeStreaming);
|
GenerateKeyRequest(g_key_id, kLicenseTypeStreaming, cdm_identifier);
|
||||||
VerifyKeyRequestResponse(g_license_server, g_client_auth);
|
VerifyKeyRequestResponse(g_license_server, g_client_auth);
|
||||||
|
|
||||||
// TODO(rfrias): Move content information to ConfigTestEnv
|
// TODO(rfrias): Move content information to ConfigTestEnv
|
||||||
@@ -4204,10 +4229,11 @@ TEST_P(WvCdmSessionSharingTest, SessionSharingTest) {
|
|||||||
"edef8ba979d64acea3c827dcd51d21ed00000014" // Widevine system id
|
"edef8ba979d64acea3c827dcd51d21ed00000014" // Widevine system id
|
||||||
"08011210bdf1cb4fffc6506b8b7945b0bd2917fb"); // pssh data
|
"08011210bdf1cb4fffc6506b8b7945b0bd2917fb"); // pssh data
|
||||||
|
|
||||||
decryptor_.OpenSession(g_key_system, &property_set, kDefaultCdmIdentifier,
|
ASSERT_EQ(NO_ERROR, decryptor_.OpenSession(g_key_system, &property_set,
|
||||||
NULL, &session_id_);
|
cdm_identifier, NULL,
|
||||||
|
&session_id_));
|
||||||
CdmSessionId gp_session_id_2 = session_id_;
|
CdmSessionId gp_session_id_2 = session_id_;
|
||||||
GenerateKeyRequest(gp_key_id2, kLicenseTypeStreaming);
|
GenerateKeyRequest(gp_key_id2, kLicenseTypeStreaming, cdm_identifier);
|
||||||
VerifyKeyRequestResponse(g_license_server, gp_client_auth2);
|
VerifyKeyRequestResponse(g_license_server, gp_client_auth2);
|
||||||
|
|
||||||
SubSampleInfo* data = session_sharing_info->sub_sample;
|
SubSampleInfo* data = session_sharing_info->sub_sample;
|
||||||
@@ -4222,7 +4248,10 @@ TEST_P(WvCdmSessionSharingTest, SessionSharingTest) {
|
|||||||
if (session_sharing_info->session_sharing_enabled || !data->is_encrypted) {
|
if (session_sharing_info->session_sharing_enabled || !data->is_encrypted) {
|
||||||
EXPECT_EQ(NO_ERROR,
|
EXPECT_EQ(NO_ERROR,
|
||||||
decryptor_.Decrypt(gp_session_id_2, data->validate_key_id,
|
decryptor_.Decrypt(gp_session_id_2, data->validate_key_id,
|
||||||
decryption_parameters));
|
decryption_parameters))
|
||||||
|
<< "session_sharing_info->session_sharing_enabled "
|
||||||
|
<< session_sharing_info->session_sharing_enabled << std::endl
|
||||||
|
<< "data->is_encrypted " << data->is_encrypted << std::endl;
|
||||||
EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(),
|
EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(),
|
||||||
decrypt_buffer.begin()));
|
decrypt_buffer.begin()));
|
||||||
} else {
|
} else {
|
||||||
@@ -4231,13 +4260,19 @@ TEST_P(WvCdmSessionSharingTest, SessionSharingTest) {
|
|||||||
decryption_parameters));
|
decryption_parameters));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!cdm_identifier.IsEquivalentToDefault()) {
|
||||||
|
// Unprovision both security level certs.
|
||||||
|
decryptor_.Unprovision(kSecurityLevelL1, cdm_identifier);
|
||||||
|
decryptor_.Unprovision(kSecurityLevelL3, cdm_identifier);
|
||||||
|
}
|
||||||
|
|
||||||
decryptor_.CloseSession(gp_session_id_1);
|
decryptor_.CloseSession(gp_session_id_1);
|
||||||
decryptor_.CloseSession(gp_session_id_2);
|
decryptor_.CloseSession(gp_session_id_2);
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(Cdm, WvCdmSessionSharingTest,
|
INSTANTIATE_TEST_CASE_P(Cdm, WvCdmSessionSharingTest,
|
||||||
::testing::Range(&session_sharing_sub_samples[0],
|
::testing::Range(&session_sharing_sub_samples[0],
|
||||||
&session_sharing_sub_samples[6]));
|
&session_sharing_sub_samples[7]));
|
||||||
|
|
||||||
TEST_F(WvCdmRequestLicenseTest, SessionSharingTest) {
|
TEST_F(WvCdmRequestLicenseTest, SessionSharingTest) {
|
||||||
TestWvCdmClientPropertySet property_set;
|
TestWvCdmClientPropertySet property_set;
|
||||||
|
|||||||
@@ -32,11 +32,10 @@ class WvContentDecryptionModuleMetricsTest : public ::testing::Test {
|
|||||||
wvcdm::WvContentDecryptionModule decryptor_;
|
wvcdm::WvContentDecryptionModule decryptor_;
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(WvContentDecryptionModuleMetricsTest, NoMetrics) {
|
TEST_F(WvContentDecryptionModuleMetricsTest, IdentifierNotFound) {
|
||||||
// Get metrics before any operations are performed.
|
drm_metrics::WvCdmMetrics metrics;
|
||||||
std::string serialized_metrics;
|
ASSERT_EQ(wvcdm::UNKNOWN_ERROR,
|
||||||
decryptor_.GetSerializedMetrics(&serialized_metrics);
|
decryptor_.GetMetrics(kDefaultCdmIdentifier, &metrics));
|
||||||
EXPECT_TRUE(serialized_metrics.empty());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WvContentDecryptionModuleMetricsTest, EngineOnlyMetrics) {
|
TEST_F(WvContentDecryptionModuleMetricsTest, EngineOnlyMetrics) {
|
||||||
@@ -50,27 +49,21 @@ TEST_F(WvContentDecryptionModuleMetricsTest, EngineOnlyMetrics) {
|
|||||||
decryptor_.GetProvisioningRequest(cert_type, cert_authority,
|
decryptor_.GetProvisioningRequest(cert_type, cert_authority,
|
||||||
kDefaultCdmIdentifier, &request,
|
kDefaultCdmIdentifier, &request,
|
||||||
&provisioning_server_url));
|
&provisioning_server_url));
|
||||||
std::string serialized_metrics;
|
|
||||||
decryptor_.GetSerializedMetrics(&serialized_metrics);
|
|
||||||
|
|
||||||
drm_metrics::WvCdmMetricsGroup metrics_group;
|
drm_metrics::WvCdmMetrics metrics;
|
||||||
ASSERT_TRUE(metrics_group.ParseFromString(serialized_metrics))
|
ASSERT_EQ(wvcdm::NO_ERROR,
|
||||||
<< "Unexpected failure deserializing metrics: "
|
decryptor_.GetMetrics(kDefaultCdmIdentifier, &metrics));
|
||||||
<< wvcdm::b2a_hex(serialized_metrics);
|
|
||||||
ASSERT_THAT(metrics_group.metrics().size(), Eq(1));
|
|
||||||
|
|
||||||
// 100 is an arbitrary high value that shouldn't ever occur.
|
// 100 is an arbitrary high value that shouldn't ever occur.
|
||||||
EXPECT_THAT(
|
EXPECT_THAT(
|
||||||
metrics_group.metrics(0).engine_metrics()
|
metrics.engine_metrics().oemcrypto_initialization_mode().int_value(),
|
||||||
.oemcrypto_initialization_mode().int_value(),
|
|
||||||
Lt(100));
|
Lt(100));
|
||||||
EXPECT_THAT(
|
EXPECT_THAT(
|
||||||
metrics_group.metrics(0).engine_metrics()
|
metrics.engine_metrics().oemcrypto_initialization_mode().int_value(),
|
||||||
.oemcrypto_initialization_mode().int_value(),
|
|
||||||
Ge(0));
|
Ge(0));
|
||||||
ASSERT_THAT(metrics_group.metrics(0).engine_metrics()
|
ASSERT_THAT(metrics.engine_metrics()
|
||||||
.cdm_engine_get_provisioning_request_time_us().size(), Eq(1));
|
.cdm_engine_get_provisioning_request_time_us().size(), Eq(1));
|
||||||
EXPECT_THAT(metrics_group.metrics(0).engine_metrics()
|
EXPECT_THAT(metrics.engine_metrics()
|
||||||
.cdm_engine_get_provisioning_request_time_us(0)
|
.cdm_engine_get_provisioning_request_time_us(0)
|
||||||
.operation_count(),
|
.operation_count(),
|
||||||
Eq(1u));
|
Eq(1u));
|
||||||
@@ -87,81 +80,80 @@ TEST_F(WvContentDecryptionModuleMetricsTest, EngineAndSessionMetrics) {
|
|||||||
decryptor_.OpenSession(key_system, NULL,
|
decryptor_.OpenSession(key_system, NULL,
|
||||||
kDefaultCdmIdentifier, NULL, &session_id));
|
kDefaultCdmIdentifier, NULL, &session_id));
|
||||||
|
|
||||||
// The metrics will have a single engine and single session stats.
|
drm_metrics::WvCdmMetrics metrics;
|
||||||
|
ASSERT_EQ(wvcdm::NO_ERROR,
|
||||||
|
decryptor_.GetMetrics(kDefaultCdmIdentifier, &metrics));
|
||||||
std::string serialized_metrics;
|
std::string serialized_metrics;
|
||||||
decryptor_.GetSerializedMetrics(&serialized_metrics);
|
ASSERT_TRUE(metrics.SerializeToString(&serialized_metrics));
|
||||||
|
|
||||||
// Spot check some metric values.
|
// Spot check some metric values.
|
||||||
drm_metrics::WvCdmMetricsGroup metrics_group;
|
|
||||||
ASSERT_TRUE(metrics_group.ParseFromString(serialized_metrics))
|
|
||||||
<< "Unexpected failure deserializing metrics: "
|
|
||||||
<< wvcdm::b2a_hex(serialized_metrics);
|
|
||||||
ASSERT_THAT(metrics_group.metrics().size(), Eq(1));
|
|
||||||
|
|
||||||
// Validate engine-level metrics.
|
// Validate engine-level metrics.
|
||||||
EXPECT_TRUE(metrics_group.metrics(0).engine_metrics()
|
EXPECT_TRUE(metrics.engine_metrics().has_oemcrypto_initialization_mode());
|
||||||
.has_oemcrypto_initialization_mode());
|
ASSERT_THAT(metrics.engine_metrics().cdm_engine_open_session().size(), Eq(1));
|
||||||
ASSERT_THAT(
|
EXPECT_THAT(metrics.engine_metrics().cdm_engine_open_session(0).count(),
|
||||||
metrics_group.metrics(0).engine_metrics().cdm_engine_open_session().size(),
|
|
||||||
Eq(1));
|
Eq(1));
|
||||||
EXPECT_THAT(metrics_group.metrics(0).engine_metrics()
|
EXPECT_THAT(metrics.engine_metrics()
|
||||||
.cdm_engine_open_session(0).count(), Eq(1));
|
|
||||||
EXPECT_THAT(metrics_group.metrics(0).engine_metrics()
|
|
||||||
.cdm_engine_open_session(0).attributes().error_code(),
|
.cdm_engine_open_session(0).attributes().error_code(),
|
||||||
Eq(CdmResponseType::NEED_PROVISIONING));
|
Eq(CdmResponseType::NEED_PROVISIONING));
|
||||||
|
|
||||||
// Validate a session-level metric.
|
// Validate a session-level metric.
|
||||||
ASSERT_THAT(metrics_group.metrics(0).session_metrics().size(), Eq(1));
|
ASSERT_THAT(metrics.session_metrics().size(), Eq(1));
|
||||||
EXPECT_THAT(
|
EXPECT_THAT(
|
||||||
metrics_group.metrics(0).session_metrics(0)
|
metrics.session_metrics(0).cdm_session_life_span_ms().double_value(),
|
||||||
.cdm_session_life_span_ms().double_value(),
|
|
||||||
Gt(0.0))
|
Gt(0.0))
|
||||||
<< "Unexpected failure with session_metrics: "
|
<< "Unexpected failure with session_metrics: "
|
||||||
<< wvcdm::b2a_hex(serialized_metrics);
|
<< wvcdm::b2a_hex(serialized_metrics);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WvContentDecryptionModuleMetricsTest, MultipleEngineMetric) {
|
TEST_F(WvContentDecryptionModuleMetricsTest,
|
||||||
|
DifferentCdmIdentifiersHaveDifferentMetrics) {
|
||||||
CdmSessionId session_id;
|
CdmSessionId session_id;
|
||||||
wvcdm::CdmKeySystem key_system("com.widevine");
|
wvcdm::CdmKeySystem key_system("com.widevine");
|
||||||
CdmIdentifier identifier = { "foo", "bar", "baz" };
|
CdmIdentifier identifiers[] = { kDefaultCdmIdentifier,
|
||||||
|
{ "foo", "bar", "baz", 7 },
|
||||||
// Openning the session will fail with NEEDS_PROVISIONING error. But it will
|
// Note that this has all the same parameters
|
||||||
// still create some session-level stats.
|
// as the one above except for the unique_id.
|
||||||
|
{ "foo", "bar", "baz", 8 }};
|
||||||
|
const int cdm_engine_count = 3;
|
||||||
|
for (int i = 0; i < cdm_engine_count; i++) {
|
||||||
|
// To make sure we can detect different engine metrics,
|
||||||
|
// make the open session call a different number of times for
|
||||||
|
// each identifier.
|
||||||
|
for (int j = 0; j <= i; j ++) {
|
||||||
EXPECT_EQ(CdmResponseType::NEED_PROVISIONING,
|
EXPECT_EQ(CdmResponseType::NEED_PROVISIONING,
|
||||||
decryptor_.OpenSession(key_system, NULL,
|
decryptor_.OpenSession(key_system, NULL,
|
||||||
kDefaultCdmIdentifier, NULL, &session_id));
|
identifiers[i], NULL, &session_id));
|
||||||
// Open a second engine with a custom identifier.
|
}
|
||||||
EXPECT_EQ(CdmResponseType::NEED_PROVISIONING,
|
}
|
||||||
decryptor_.OpenSession(key_system, NULL,
|
|
||||||
identifier, NULL, &session_id));
|
|
||||||
|
|
||||||
// The metrics will now have two engines with single session stats each.
|
for (int i = 0; i < cdm_engine_count; i++) {
|
||||||
|
drm_metrics::WvCdmMetrics metrics;
|
||||||
|
metrics.Clear();
|
||||||
|
ASSERT_EQ(wvcdm::NO_ERROR,
|
||||||
|
decryptor_.GetMetrics(identifiers[i], &metrics));
|
||||||
std::string serialized_metrics;
|
std::string serialized_metrics;
|
||||||
decryptor_.GetSerializedMetrics(&serialized_metrics);
|
ASSERT_TRUE(metrics.SerializeToString(&serialized_metrics));
|
||||||
|
|
||||||
// Spot check some metric values.
|
ASSERT_THAT(metrics.engine_metrics().cdm_engine_open_session().size(),
|
||||||
drm_metrics::WvCdmMetricsGroup metrics_group;
|
Eq(1));
|
||||||
ASSERT_TRUE(metrics_group.ParseFromString(serialized_metrics));
|
// The number of times open session was called should match the index
|
||||||
|
// of the identifier
|
||||||
// Two engine-level metrics are expected.
|
EXPECT_THAT(metrics.engine_metrics().cdm_engine_open_session(0).count(),
|
||||||
ASSERT_THAT(metrics_group.metrics().size(), Eq(2));
|
Eq(i + 1));
|
||||||
|
|
||||||
for (int i = 0; i < metrics_group.metrics().size(); i++) {
|
|
||||||
drm_metrics::WvCdmMetrics::EngineMetrics engine_metrics =
|
|
||||||
metrics_group.metrics(i).engine_metrics();
|
|
||||||
// Validate an engine-level metric.
|
|
||||||
EXPECT_TRUE(engine_metrics.has_oemcrypto_initialization_mode());
|
|
||||||
ASSERT_THAT(engine_metrics.cdm_engine_open_session().size(), Eq(1));
|
|
||||||
EXPECT_THAT(engine_metrics.cdm_engine_open_session(0).count(), Eq(1));
|
|
||||||
EXPECT_THAT(
|
EXPECT_THAT(
|
||||||
engine_metrics.cdm_engine_open_session(0).attributes().error_code(),
|
metrics.engine_metrics()
|
||||||
|
.cdm_engine_open_session(0).attributes().error_code(),
|
||||||
Eq(CdmResponseType::NEED_PROVISIONING));
|
Eq(CdmResponseType::NEED_PROVISIONING));
|
||||||
|
|
||||||
// Validate a session-level metric.
|
// Spot check a session-level metric.
|
||||||
ASSERT_THAT(metrics_group.metrics(i).session_metrics().size(), Eq(1));
|
ASSERT_THAT(metrics.session_metrics().size(), Eq(i + 1))
|
||||||
EXPECT_THAT(metrics_group.metrics(i).session_metrics(0)
|
<< "Unexpected failure with session_metrics: "
|
||||||
.cdm_session_life_span_ms().double_value(), Gt(0.0));
|
<< wvcdm::b2a_hex(serialized_metrics);
|
||||||
|
EXPECT_THAT(metrics.session_metrics(0)
|
||||||
|
.cdm_session_life_span_ms().double_value(), Gt(0.0))
|
||||||
|
<< "Unexpected failure with session_metrics: "
|
||||||
|
<< wvcdm::b2a_hex(serialized_metrics);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} // wvcdm namespace
|
||||||
|
|||||||
@@ -367,6 +367,10 @@ struct WVDrmPlugin : public IDrmPlugin, IDrmPluginListener,
|
|||||||
const std::string& origin() const { return mCdmIdentifier.origin; }
|
const std::string& origin() const { return mCdmIdentifier.origin; }
|
||||||
bool set_origin(const std::string& id);
|
bool set_origin(const std::string& id);
|
||||||
|
|
||||||
|
// Indicates whether the builder can still be modified. This returns false
|
||||||
|
// until a call to getCdmIdentifier.
|
||||||
|
bool is_sealed() { return mIsIdentifierSealed; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
WVDRM_DISALLOW_COPY_AND_ASSIGN(CdmIdentifierBuilder);
|
WVDRM_DISALLOW_COPY_AND_ASSIGN(CdmIdentifierBuilder);
|
||||||
|
|
||||||
@@ -384,6 +388,13 @@ struct WVDrmPlugin : public IDrmPlugin, IDrmPluginListener,
|
|||||||
// outside this class should use getDeviceUniqueId() to get the
|
// outside this class should use getDeviceUniqueId() to get the
|
||||||
// application-safe device-unique ID.
|
// application-safe device-unique ID.
|
||||||
Status getOemcryptoDeviceId(std::string* id);
|
Status getOemcryptoDeviceId(std::string* id);
|
||||||
|
|
||||||
|
// The unique identifier is meant to ensure that two clients with the
|
||||||
|
// same spoid, origin and app package name still get different cdm engine
|
||||||
|
// instances. This is a stepping stone to simplifying the implementation.
|
||||||
|
// Note that we do not have a lock or mutex around this object. We assume
|
||||||
|
// that locking is handled external to this object.
|
||||||
|
uint32_t getNextUniqueId();
|
||||||
} mCdmIdentifierBuilder;
|
} mCdmIdentifierBuilder;
|
||||||
|
|
||||||
sp<wvcdm::WvContentDecryptionModule> const mCDM;
|
sp<wvcdm::WvContentDecryptionModule> const mCDM;
|
||||||
|
|||||||
@@ -394,7 +394,7 @@ status_t WVDrmPlugin::provideProvisionResponse(
|
|||||||
}
|
}
|
||||||
CdmProvisioningResponse cdmResponse(response.begin(), response.end());
|
CdmProvisioningResponse cdmResponse(response.begin(), response.end());
|
||||||
if (cdmResponse == kSpecialUnprovisionResponse) {
|
if (cdmResponse == kSpecialUnprovisionResponse) {
|
||||||
if (mCdmIdentifier == kDefaultCdmIdentifier) {
|
if (mCdmIdentifier.IsEquivalentToDefault()) {
|
||||||
return kErrorNoOriginSpecified;
|
return kErrorNoOriginSpecified;
|
||||||
}
|
}
|
||||||
return unprovision(mCdmIdentifier);
|
return unprovision(mCdmIdentifier);
|
||||||
@@ -542,9 +542,14 @@ status_t WVDrmPlugin::getPropertyByteArray(const String8& name,
|
|||||||
} else if (name == "serviceCertificate") {
|
} else if (name == "serviceCertificate") {
|
||||||
value = ToVector(mPropertySet.service_certificate());
|
value = ToVector(mPropertySet.service_certificate());
|
||||||
} else if (name == "metrics") {
|
} else if (name == "metrics") {
|
||||||
std::string metrics_value;
|
std::string serialized_metrics;
|
||||||
mCDM->GetSerializedMetrics(&metrics_value);
|
drm_metrics::WvCdmMetrics metrics;
|
||||||
value = ToVector(metrics_value);
|
mCDM->GetMetrics(mCdmIdentifier, &metrics);
|
||||||
|
if (!metrics.SerializeToString(&serialized_metrics)) {
|
||||||
|
return android::ERROR_DRM_UNKNOWN;
|
||||||
|
} else {
|
||||||
|
value = ToVector(serialized_metrics);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ALOGE("App requested unknown byte array property %s", name.string());
|
ALOGE("App requested unknown byte array property %s", name.string());
|
||||||
return android::ERROR_DRM_CANNOT_HANDLE;
|
return android::ERROR_DRM_CANNOT_HANDLE;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include "mapErrors-inl.h"
|
#include "mapErrors-inl.h"
|
||||||
#include "media/stagefright/MediaErrors.h"
|
#include "media/stagefright/MediaErrors.h"
|
||||||
|
#include "metrics.pb.h"
|
||||||
#include "openssl/sha.h"
|
#include "openssl/sha.h"
|
||||||
#include "wv_cdm_constants.h"
|
#include "wv_cdm_constants.h"
|
||||||
|
|
||||||
@@ -141,6 +142,16 @@ WVDrmPlugin::~WVDrmPlugin() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
mCryptoSessions.clear();
|
mCryptoSessions.clear();
|
||||||
|
CdmIdentifier identifier;
|
||||||
|
Status status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
|
||||||
|
if (status != Status::OK) {
|
||||||
|
ALOGE("Failed to get cdm identifier %d", status);
|
||||||
|
} else {
|
||||||
|
status = mapCdmResponseType(mCDM->CloseCdm(identifier));
|
||||||
|
if (status != Status::OK) {
|
||||||
|
ALOGE("Failed to close cdm. status %d", status);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Status WVDrmPlugin::openSessionCommon(std::vector<uint8_t>& sessionId) {
|
Status WVDrmPlugin::openSessionCommon(std::vector<uint8_t>& sessionId) {
|
||||||
@@ -618,7 +629,7 @@ Return<void> WVDrmPlugin::provideProvisionResponse(
|
|||||||
|
|
||||||
CdmProvisioningResponse cdmResponse(resp.begin(), resp.end());
|
CdmProvisioningResponse cdmResponse(resp.begin(), resp.end());
|
||||||
if (cdmResponse == kSpecialUnprovisionResponse) {
|
if (cdmResponse == kSpecialUnprovisionResponse) {
|
||||||
if (identifier == kDefaultCdmIdentifier) {
|
if (identifier.IsEquivalentToDefault()) {
|
||||||
ALOGW("Returns UNKNOWN error for legacy status kErrorNoOriginSpecified");
|
ALOGW("Returns UNKNOWN error for legacy status kErrorNoOriginSpecified");
|
||||||
_hidl_cb(Status::ERROR_DRM_UNKNOWN, toHidlVec(certificate),
|
_hidl_cb(Status::ERROR_DRM_UNKNOWN, toHidlVec(certificate),
|
||||||
toHidlVec(wrappedKey));
|
toHidlVec(wrappedKey));
|
||||||
@@ -986,9 +997,27 @@ Return<void> WVDrmPlugin::getPropertyByteArray(
|
|||||||
} else if (name == "serviceCertificate") {
|
} else if (name == "serviceCertificate") {
|
||||||
value = StrToVector(mPropertySet.service_certificate());
|
value = StrToVector(mPropertySet.service_certificate());
|
||||||
} else if (name == "metrics") {
|
} else if (name == "metrics") {
|
||||||
std::string metrics_value;
|
drm_metrics::WvCdmMetrics metrics;
|
||||||
mCDM->GetSerializedMetrics(&metrics_value);
|
// If the cdm identifier is not yet sealed, then there are no metrics
|
||||||
value = StrToVector(metrics_value);
|
// for that cdm engine. Avoid calling getCdmIdentifier and sealing
|
||||||
|
// the identifier builder.
|
||||||
|
if (mCdmIdentifierBuilder.is_sealed()) {
|
||||||
|
CdmIdentifier identifier;
|
||||||
|
status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
|
||||||
|
if (status != Status::OK) {
|
||||||
|
ALOGE("Unexpected error retrieving cdm identifier: %d", status);
|
||||||
|
} else {
|
||||||
|
status = mapCdmResponseType(mCDM->GetMetrics(identifier, &metrics));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (status == Status::OK) {
|
||||||
|
std::string serialized_metrics;
|
||||||
|
if (!metrics.SerializeToString(&serialized_metrics)) {
|
||||||
|
status = Status::ERROR_DRM_UNKNOWN;
|
||||||
|
} else {
|
||||||
|
value = StrToVector(serialized_metrics);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ALOGE("App requested unknown byte array property %s", name.c_str());
|
ALOGE("App requested unknown byte array property %s", name.c_str());
|
||||||
status = Status::ERROR_DRM_CANNOT_HANDLE;
|
status = Status::ERROR_DRM_CANNOT_HANDLE;
|
||||||
@@ -1643,6 +1672,7 @@ WVDrmPlugin::CdmIdentifierBuilder::CdmIdentifierBuilder(
|
|||||||
mAppPackageName(appPackageName),
|
mAppPackageName(appPackageName),
|
||||||
mParent(parent) {
|
mParent(parent) {
|
||||||
mCdmIdentifier.app_package_name = mAppPackageName;
|
mCdmIdentifier.app_package_name = mAppPackageName;
|
||||||
|
mCdmIdentifier.unique_id = getNextUniqueId();
|
||||||
}
|
}
|
||||||
|
|
||||||
Status WVDrmPlugin::CdmIdentifierBuilder::getCdmIdentifier(
|
Status WVDrmPlugin::CdmIdentifierBuilder::getCdmIdentifier(
|
||||||
@@ -1650,7 +1680,6 @@ Status WVDrmPlugin::CdmIdentifierBuilder::getCdmIdentifier(
|
|||||||
if (!mIsIdentifierSealed) {
|
if (!mIsIdentifierSealed) {
|
||||||
Status res = calculateSpoid();
|
Status res = calculateSpoid();
|
||||||
if (res != Status::OK) return res;
|
if (res != Status::OK) return res;
|
||||||
|
|
||||||
mIsIdentifierSealed = true;
|
mIsIdentifierSealed = true;
|
||||||
}
|
}
|
||||||
*identifier = mCdmIdentifier;
|
*identifier = mCdmIdentifier;
|
||||||
@@ -1719,6 +1748,12 @@ Status WVDrmPlugin::CdmIdentifierBuilder::getOemcryptoDeviceId(
|
|||||||
return mParent.queryProperty(wvcdm::QUERY_KEY_DEVICE_ID, *id);
|
return mParent.queryProperty(wvcdm::QUERY_KEY_DEVICE_ID, *id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t WVDrmPlugin::CdmIdentifierBuilder::getNextUniqueId() {
|
||||||
|
// Start with 1. 0 is reserved for the default cdm identifier.
|
||||||
|
static uint32_t unique_id = 1;
|
||||||
|
return ++unique_id;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
} // namespace V1_1
|
} // namespace V1_1
|
||||||
} // namespace drm
|
} // namespace drm
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
#include "media/stagefright/foundation/ABase.h"
|
#include "media/stagefright/foundation/ABase.h"
|
||||||
#include "media/stagefright/MediaErrors.h"
|
#include "media/stagefright/MediaErrors.h"
|
||||||
|
#include "string_conversions.h"
|
||||||
#include "wv_cdm_constants.h"
|
#include "wv_cdm_constants.h"
|
||||||
#include "wv_cdm_types.h"
|
#include "wv_cdm_types.h"
|
||||||
#include "wv_content_decryption_module.h"
|
#include "wv_content_decryption_module.h"
|
||||||
@@ -122,15 +123,17 @@ const uint8_t* const kUnprovisionResponse =
|
|||||||
const size_t kUnprovisionResponseSize = 11;
|
const size_t kUnprovisionResponseSize = 11;
|
||||||
const std::string kDeviceId("0123456789\0ABCDEF", 17);
|
const std::string kDeviceId("0123456789\0ABCDEF", 17);
|
||||||
|
|
||||||
// This is a serialized MetricsGroup message containing a small amount of
|
// This is a serialized WvCdmMetrics message containing a small amount of
|
||||||
// sample data. This ensures we're able to extract it via a property.
|
// sample data. This ensures we're able to extract it via a property.
|
||||||
const char kSerializedMetrics[] = {
|
const char kSerializedMetricsHex[] =
|
||||||
0x0a, 0x0a, 0x0a, 0x04, 0x74, 0x65, 0x73, 0x74, 0x12, 0x02, 0x08, 0x00,
|
"0a580a001a0210072202100d2a02100832182216636f6d2e676f6f676c652e616e64726f69"
|
||||||
0x0a, 0x12, 0x0a, 0x05, 0x74, 0x65, 0x73, 0x74, 0x32, 0x12, 0x09, 0x11,
|
"642e676d734208220631342e302e304a06080112020800520610d5f3fad5056a0b1d00fd4c"
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x15, 0x0a, 0x05,
|
"47280132020804a2010608011202080012cb010a0622047369643412b5010a021001520919"
|
||||||
0x74, 0x65, 0x73, 0x74, 0x33, 0x12, 0x0c, 0x1a, 0x0a, 0x74, 0x65, 0x73,
|
"1d5a643bdfff50405a0b1d00d01945280132021001620d1d00f8e84528013204080020006a"
|
||||||
0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65
|
"0310b739820102100d8a01060801120248009a010310ff01da0106080112024800e2010e1d"
|
||||||
};
|
"005243472801320528800248009a020d1d00b016452801320428404800a202060801120248"
|
||||||
|
"19aa0206080212024800b2020b1d8098f047280132024800ba02021001ca020b1d00101945"
|
||||||
|
"280132024800e202021004fa02021002a203021000b2030210021a09196891ed7c3f355040";
|
||||||
|
|
||||||
#define N_ELEM(a) (sizeof(a)/sizeof(a[0]))
|
#define N_ELEM(a) (sizeof(a)/sizeof(a[0]))
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
@@ -200,7 +203,8 @@ class MockCDM : public WvContentDecryptionModule {
|
|||||||
|
|
||||||
MOCK_METHOD1(IsValidServiceCertificate, bool(const std::string&));
|
MOCK_METHOD1(IsValidServiceCertificate, bool(const std::string&));
|
||||||
|
|
||||||
MOCK_METHOD1(GetSerializedMetrics, void(std::string*));
|
MOCK_METHOD2(GetMetrics, CdmResponseType(const CdmIdentifier&,
|
||||||
|
drm_metrics::WvCdmMetrics*));
|
||||||
};
|
};
|
||||||
|
|
||||||
class MockCrypto : public WVGenericCryptoInterface {
|
class MockCrypto : public WVGenericCryptoInterface {
|
||||||
@@ -1128,8 +1132,9 @@ TEST_F(WVDrmPluginTest, ReturnsExpectedPropertyValues) {
|
|||||||
static const std::string oemCryptoApiVersion = "13";
|
static const std::string oemCryptoApiVersion = "13";
|
||||||
static const std::string currentSRMVersion = "1";
|
static const std::string currentSRMVersion = "1";
|
||||||
static const std::string cdmVersion = "Infinity Minus 1";
|
static const std::string cdmVersion = "Infinity Minus 1";
|
||||||
std::string serializedMetrics(
|
drm_metrics::WvCdmMetrics expected_metrics;
|
||||||
kSerializedMetrics, kSerializedMetrics + sizeof(kSerializedMetrics));
|
std::string serialized_metrics = wvcdm::a2bs_hex(kSerializedMetricsHex);
|
||||||
|
ASSERT_TRUE(expected_metrics.ParseFromString(serialized_metrics));
|
||||||
|
|
||||||
EXPECT_CALL(*cdm, QueryStatus(_, QUERY_KEY_SECURITY_LEVEL, _))
|
EXPECT_CALL(*cdm, QueryStatus(_, QUERY_KEY_SECURITY_LEVEL, _))
|
||||||
.WillOnce(DoAll(SetArgPointee<2>(QUERY_VALUE_SECURITY_LEVEL_L1),
|
.WillOnce(DoAll(SetArgPointee<2>(QUERY_VALUE_SECURITY_LEVEL_L1),
|
||||||
@@ -1173,8 +1178,9 @@ TEST_F(WVDrmPluginTest, ReturnsExpectedPropertyValues) {
|
|||||||
.WillOnce(DoAll(SetArgPointee<2>(cdmVersion),
|
.WillOnce(DoAll(SetArgPointee<2>(cdmVersion),
|
||||||
testing::Return(wvcdm::NO_ERROR)));
|
testing::Return(wvcdm::NO_ERROR)));
|
||||||
|
|
||||||
EXPECT_CALL(*cdm, GetSerializedMetrics(_))
|
EXPECT_CALL(*cdm, GetMetrics(_, _))
|
||||||
.WillOnce(SetArgPointee<0>(serializedMetrics));
|
.WillOnce(DoAll(SetArgPointee<1>(expected_metrics),
|
||||||
|
testing::Return(wvcdm::NO_ERROR)));
|
||||||
|
|
||||||
WVDrmPlugin plugin(cdm.get(), appPackageName, &crypto, false);
|
WVDrmPlugin plugin(cdm.get(), appPackageName, &crypto, false);
|
||||||
std::string stringResult;
|
std::string stringResult;
|
||||||
@@ -1277,14 +1283,51 @@ TEST_F(WVDrmPluginTest, ReturnsExpectedPropertyValues) {
|
|||||||
EXPECT_STREQ(currentSRMVersion.c_str(), stringResult.c_str());
|
EXPECT_STREQ(currentSRMVersion.c_str(), stringResult.c_str());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// This call occurs before any open session or other call. This means
|
||||||
|
// that the cdm identifer is not yet sealed, and metrics return empty
|
||||||
|
// metrics data.
|
||||||
plugin.getPropertyByteArray(
|
plugin.getPropertyByteArray(
|
||||||
hidl_string("metrics"),
|
hidl_string("metrics"),
|
||||||
[&](Status status, hidl_vec<uint8_t> vectorResult) {
|
[&](Status status, hidl_vec<uint8_t> vectorResult) {
|
||||||
ASSERT_EQ(Status::OK, status);
|
ASSERT_EQ(Status::OK, status);
|
||||||
std::vector<uint8_t> id(vectorResult);
|
std::vector<uint8_t> id(vectorResult);
|
||||||
EXPECT_THAT(id, ElementsAreArray(serializedMetrics.data(),
|
const char empty[] = {};
|
||||||
serializedMetrics.size()));
|
EXPECT_THAT(id, ElementsAreArray(empty, sizeof(empty)));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Set expectations for the OpenSession call and a CloseSession call.
|
||||||
|
EXPECT_CALL(*cdm,
|
||||||
|
OpenSession(StrEq("com.widevine"), _, HasOrigin(EMPTY_ORIGIN), _, _))
|
||||||
|
.WillOnce(DoAll(SetArgPointee<4>(cdmSessionId),
|
||||||
|
testing::Return(wvcdm::NO_ERROR)));
|
||||||
|
// Provide expected behavior when plugin requests session control info
|
||||||
|
EXPECT_CALL(*cdm, QueryOemCryptoSessionId(cdmSessionId, _))
|
||||||
|
.Times(AtLeast(1))
|
||||||
|
.WillRepeatedly(Invoke(setSessionIdOnMap<4>));
|
||||||
|
EXPECT_CALL(*cdm, CloseSession(_))
|
||||||
|
.Times(AtLeast(0));
|
||||||
|
|
||||||
|
// This call causes the cdm identifier to become sealed.
|
||||||
|
std::vector<uint8_t> sessionId;
|
||||||
|
plugin.openSession([&](Status status, hidl_vec<uint8_t> hSessionId) {
|
||||||
|
ASSERT_EQ(Status::OK, status);
|
||||||
|
sessionId.clear();
|
||||||
|
sessionId.assign(hSessionId.data(),
|
||||||
|
hSessionId.data() + hSessionId.size());
|
||||||
|
});
|
||||||
|
|
||||||
|
// This call occurs after open session. The CDM identifer should be sealed.
|
||||||
|
// And the call should populate the mock metrics data.
|
||||||
|
plugin.getPropertyByteArray(
|
||||||
|
hidl_string("metrics"),
|
||||||
|
[&](Status status, hidl_vec<uint8_t> vectorResult) {
|
||||||
|
ASSERT_EQ(Status::OK, status);
|
||||||
|
std::vector<uint8_t> id(vectorResult);
|
||||||
|
EXPECT_THAT(id, ElementsAreArray(serialized_metrics.data(),
|
||||||
|
serialized_metrics.size()));
|
||||||
|
});
|
||||||
|
|
||||||
|
ASSERT_EQ(Status::OK, plugin.closeSession(toHidlVec(sessionId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WVDrmPluginTest, DoesNotGetUnknownProperties) {
|
TEST_F(WVDrmPluginTest, DoesNotGetUnknownProperties) {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
#include "media/stagefright/foundation/ABase.h"
|
#include "media/stagefright/foundation/ABase.h"
|
||||||
#include "media/stagefright/foundation/AString.h"
|
#include "media/stagefright/foundation/AString.h"
|
||||||
#include "media/stagefright/MediaErrors.h"
|
#include "media/stagefright/MediaErrors.h"
|
||||||
|
#include "string_conversions.h"
|
||||||
#include "wv_cdm_constants.h"
|
#include "wv_cdm_constants.h"
|
||||||
#include "wv_cdm_types.h"
|
#include "wv_cdm_types.h"
|
||||||
#include "wv_content_decryption_module.h"
|
#include "wv_content_decryption_module.h"
|
||||||
@@ -35,14 +36,18 @@ const uint8_t* const kUnprovisionResponse =
|
|||||||
reinterpret_cast<const uint8_t*>("unprovision");
|
reinterpret_cast<const uint8_t*>("unprovision");
|
||||||
const size_t kUnprovisionResponseSize = 11;
|
const size_t kUnprovisionResponseSize = 11;
|
||||||
|
|
||||||
// This is a serialized MetricsGroup message containing a small amount of
|
// This is a serialized WvCdmMetrics message containing a small amount of
|
||||||
// sample data. This ensures we're able to extract it via a property.
|
// sample data. This ensures we're able to extract it via a property.
|
||||||
const char kSerializedMetrics[] = {
|
const char kSerializedMetricsHex[] =
|
||||||
0x0a, 0x0a, 0x0a, 0x04, 0x74, 0x65, 0x73, 0x74, 0x12, 0x02, 0x08, 0x00,
|
"0a580a001a0210072202100d2a02100832182216636f6d2e676f6f676c652e616e64726f69"
|
||||||
0x0a, 0x12, 0x0a, 0x05, 0x74, 0x65, 0x73, 0x74, 0x32, 0x12, 0x09, 0x11,
|
"642e676d734208220631342e302e304a06080112020800520610d5f3fad5056a0b1d00fd4c"
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x15, 0x0a, 0x05,
|
"47280132020804a2010608011202080012cb010a0622047369643412b5010a021001520919"
|
||||||
0x74, 0x65, 0x73, 0x74, 0x33, 0x12, 0x0c, 0x1a, 0x0a, 0x74, 0x65, 0x73,
|
"1d5a643bdfff50405a0b1d00d01945280132021001620d1d00f8e84528013204080020006a"
|
||||||
0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65};
|
"0310b739820102100d8a01060801120248009a010310ff01da0106080112024800e2010e1d"
|
||||||
|
"005243472801320528800248009a020d1d00b016452801320428404800a202060801120248"
|
||||||
|
"19aa0206080212024800b2020b1d8098f047280132024800ba02021001ca020b1d00101945"
|
||||||
|
"280132024800e202021004fa02021002a203021000b2030210021a09196891ed7c3f355040";
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
class MockCDM : public WvContentDecryptionModule {
|
class MockCDM : public WvContentDecryptionModule {
|
||||||
@@ -110,7 +115,8 @@ class MockCDM : public WvContentDecryptionModule {
|
|||||||
|
|
||||||
MOCK_METHOD1(IsValidServiceCertificate, bool(const std::string&));
|
MOCK_METHOD1(IsValidServiceCertificate, bool(const std::string&));
|
||||||
|
|
||||||
MOCK_METHOD1(GetSerializedMetrics, void(std::string*));
|
MOCK_METHOD2(GetMetrics, CdmResponseType(const CdmIdentifier&,
|
||||||
|
drm_metrics::WvCdmMetrics*));
|
||||||
};
|
};
|
||||||
|
|
||||||
class MockCrypto : public WVGenericCryptoInterface {
|
class MockCrypto : public WVGenericCryptoInterface {
|
||||||
@@ -852,8 +858,10 @@ TEST_F(WVDrmPluginTest, ReturnsExpectedPropertyValues) {
|
|||||||
static const string oemCryptoApiVersion = "10";
|
static const string oemCryptoApiVersion = "10";
|
||||||
static const string currentSRMVersion = "1";
|
static const string currentSRMVersion = "1";
|
||||||
static const string cdmVersion = "Infinity Minus 1";
|
static const string cdmVersion = "Infinity Minus 1";
|
||||||
string serializedMetrics(kSerializedMetrics,
|
|
||||||
kSerializedMetrics + sizeof(kSerializedMetrics));
|
drm_metrics::WvCdmMetrics expected_metrics;
|
||||||
|
std::string serialized_metrics = wvcdm::a2bs_hex(kSerializedMetricsHex);
|
||||||
|
ASSERT_TRUE(expected_metrics.ParseFromString(serialized_metrics));
|
||||||
|
|
||||||
EXPECT_CALL(*cdm, QueryStatus(_, QUERY_KEY_SECURITY_LEVEL, _))
|
EXPECT_CALL(*cdm, QueryStatus(_, QUERY_KEY_SECURITY_LEVEL, _))
|
||||||
.WillOnce(DoAll(SetArgPointee<2>(QUERY_VALUE_SECURITY_LEVEL_L1),
|
.WillOnce(DoAll(SetArgPointee<2>(QUERY_VALUE_SECURITY_LEVEL_L1),
|
||||||
@@ -897,8 +905,9 @@ TEST_F(WVDrmPluginTest, ReturnsExpectedPropertyValues) {
|
|||||||
.WillOnce(DoAll(SetArgPointee<2>(cdmVersion),
|
.WillOnce(DoAll(SetArgPointee<2>(cdmVersion),
|
||||||
Return(wvcdm::NO_ERROR)));
|
Return(wvcdm::NO_ERROR)));
|
||||||
|
|
||||||
EXPECT_CALL(*cdm, GetSerializedMetrics(_))
|
EXPECT_CALL(*cdm, GetMetrics(_, _))
|
||||||
.WillOnce(SetArgPointee<0>(serializedMetrics));
|
.WillOnce(DoAll(SetArgPointee<1>(expected_metrics),
|
||||||
|
testing::Return(wvcdm::NO_ERROR)));
|
||||||
|
|
||||||
String8 stringResult;
|
String8 stringResult;
|
||||||
Vector<uint8_t> vectorResult;
|
Vector<uint8_t> vectorResult;
|
||||||
@@ -964,8 +973,8 @@ TEST_F(WVDrmPluginTest, ReturnsExpectedPropertyValues) {
|
|||||||
vectorResult.clear();
|
vectorResult.clear();
|
||||||
res = plugin.getPropertyByteArray(String8("metrics"), vectorResult);
|
res = plugin.getPropertyByteArray(String8("metrics"), vectorResult);
|
||||||
ASSERT_EQ(OK, res);
|
ASSERT_EQ(OK, res);
|
||||||
EXPECT_THAT(vectorResult, ElementsAreArray(serializedMetrics.data(),
|
EXPECT_THAT(vectorResult, ElementsAreArray(serialized_metrics.data(),
|
||||||
serializedMetrics.size()));
|
serialized_metrics.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WVDrmPluginTest, DoesNotGetUnknownProperties) {
|
TEST_F(WVDrmPluginTest, DoesNotGetUnknownProperties) {
|
||||||
|
|||||||
Reference in New Issue
Block a user