Merge changes I442b7919,Ie5b4ff75
* changes: Improve android MediaDrm property latency Delay OEMCrypto Termination
This commit is contained in:
@@ -44,11 +44,11 @@ class CryptoSessionFactory;
|
|||||||
class CryptoSession {
|
class CryptoSession {
|
||||||
public:
|
public:
|
||||||
using HdcpCapability = OEMCrypto_HDCP_Capability;
|
using HdcpCapability = OEMCrypto_HDCP_Capability;
|
||||||
typedef enum {
|
enum UsageDurationStatus {
|
||||||
kUsageDurationsInvalid = 0,
|
kUsageDurationsInvalid = 0,
|
||||||
kUsageDurationPlaybackNotBegun = 1,
|
kUsageDurationPlaybackNotBegun = 1,
|
||||||
kUsageDurationsValid = 2,
|
kUsageDurationsValid = 2,
|
||||||
} UsageDurationStatus;
|
};
|
||||||
|
|
||||||
struct SupportedCertificateTypes {
|
struct SupportedCertificateTypes {
|
||||||
bool rsa_2048_bit;
|
bool rsa_2048_bit;
|
||||||
@@ -64,6 +64,17 @@ class CryptoSession {
|
|||||||
|
|
||||||
virtual ~CryptoSession();
|
virtual ~CryptoSession();
|
||||||
|
|
||||||
|
// This method will try to terminate OEMCrypto if |session_size_| is 0.
|
||||||
|
// A platform configured property |delay_oem_crypto_termination| will
|
||||||
|
// determine if termination occurs immediately or after a delay.
|
||||||
|
// If termination is delayed, a countdown mechanism is employed.
|
||||||
|
// Call |TryTerminate| periodically until it no longer returns true.
|
||||||
|
// To immediately terminate call |DisableDelayedTermination| before calling
|
||||||
|
// |TryTerminate|.
|
||||||
|
static bool TryTerminate();
|
||||||
|
|
||||||
|
static void DisableDelayedTermination();
|
||||||
|
|
||||||
virtual CdmResponseType GetProvisioningToken(std::string* client_token);
|
virtual CdmResponseType GetProvisioningToken(std::string* client_token);
|
||||||
virtual CdmClientTokenType GetPreProvisionTokenType() {
|
virtual CdmClientTokenType GetPreProvisionTokenType() {
|
||||||
return pre_provision_token_type_;
|
return pre_provision_token_type_;
|
||||||
@@ -282,7 +293,6 @@ class CryptoSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Init();
|
void Init();
|
||||||
void Terminate();
|
|
||||||
CdmResponseType GetTokenFromKeybox(std::string* token);
|
CdmResponseType GetTokenFromKeybox(std::string* token);
|
||||||
CdmResponseType GetTokenFromOemCert(std::string* token);
|
CdmResponseType GetTokenFromOemCert(std::string* token);
|
||||||
static bool ExtractSystemIdFromOemCert(const std::string& oem_cert,
|
static bool ExtractSystemIdFromOemCert(const std::string& oem_cert,
|
||||||
@@ -385,6 +395,7 @@ class CryptoSession {
|
|||||||
|
|
||||||
static bool initialized_;
|
static bool initialized_;
|
||||||
static int session_count_;
|
static int session_count_;
|
||||||
|
static int termination_counter_;
|
||||||
|
|
||||||
metrics::CryptoMetrics* metrics_;
|
metrics::CryptoMetrics* metrics_;
|
||||||
metrics::TimerMetric life_span_;
|
metrics::TimerMetric life_span_;
|
||||||
|
|||||||
@@ -61,6 +61,9 @@ class Properties {
|
|||||||
static void set_provisioning_messages_are_binary(bool flag) {
|
static void set_provisioning_messages_are_binary(bool flag) {
|
||||||
provisioning_messages_are_binary_ = flag;
|
provisioning_messages_are_binary_ = flag;
|
||||||
}
|
}
|
||||||
|
static inline bool delay_oem_crypto_termination() {
|
||||||
|
return delay_oem_crypto_termination_;
|
||||||
|
}
|
||||||
static bool GetCompanyName(std::string* company_name);
|
static bool GetCompanyName(std::string* company_name);
|
||||||
static bool GetModelName(std::string* model_name);
|
static bool GetModelName(std::string* model_name);
|
||||||
static bool GetArchitectureName(std::string* arch_name);
|
static bool GetArchitectureName(std::string* arch_name);
|
||||||
@@ -147,6 +150,7 @@ class Properties {
|
|||||||
static bool allow_service_certificate_requests_;
|
static bool allow_service_certificate_requests_;
|
||||||
static bool device_files_is_a_real_filesystem_;
|
static bool device_files_is_a_real_filesystem_;
|
||||||
static bool allow_restore_of_offline_licenses_with_release_;
|
static bool allow_restore_of_offline_licenses_with_release_;
|
||||||
|
static bool delay_oem_crypto_termination_;
|
||||||
static std::unique_ptr<CdmClientPropertySetMap> session_property_set_;
|
static std::unique_ptr<CdmClientPropertySetMap> session_property_set_;
|
||||||
|
|
||||||
CORE_DISALLOW_COPY_AND_ASSIGN(Properties);
|
CORE_DISALLOW_COPY_AND_ASSIGN(Properties);
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ const size_t kOemCryptoApiVersionSupportsSwitchingCipherMode = 14;
|
|||||||
|
|
||||||
// Constants and utility objects relating to OEM Certificates
|
// Constants and utility objects relating to OEM Certificates
|
||||||
const char* const kWidevineSystemIdExtensionOid = "1.3.6.1.4.1.11129.4.1.1";
|
const char* const kWidevineSystemIdExtensionOid = "1.3.6.1.4.1.11129.4.1.1";
|
||||||
|
constexpr int kMaxTerminateCountDown = 5;
|
||||||
|
|
||||||
const std::string kStringNotAvailable = "NA";
|
const std::string kStringNotAvailable = "NA";
|
||||||
|
|
||||||
@@ -96,6 +97,7 @@ shared_mutex CryptoSession::static_field_mutex_;
|
|||||||
shared_mutex CryptoSession::oem_crypto_mutex_;
|
shared_mutex CryptoSession::oem_crypto_mutex_;
|
||||||
bool CryptoSession::initialized_ = false;
|
bool CryptoSession::initialized_ = false;
|
||||||
int CryptoSession::session_count_ = 0;
|
int CryptoSession::session_count_ = 0;
|
||||||
|
int CryptoSession::termination_counter_ = 0;
|
||||||
UsageTableHeader* CryptoSession::usage_table_header_l1_ = nullptr;
|
UsageTableHeader* CryptoSession::usage_table_header_l1_ = nullptr;
|
||||||
UsageTableHeader* CryptoSession::usage_table_header_l3_ = nullptr;
|
UsageTableHeader* CryptoSession::usage_table_header_l3_ = nullptr;
|
||||||
std::atomic<uint64_t> CryptoSession::request_id_index_source_(0);
|
std::atomic<uint64_t> CryptoSession::request_id_index_source_(0);
|
||||||
@@ -189,7 +191,15 @@ CryptoSession::~CryptoSession() {
|
|||||||
if (open_) {
|
if (open_) {
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
Terminate();
|
WithStaticFieldWriteLock("~CryptoSession", [&] {
|
||||||
|
if (session_count_ > 0) {
|
||||||
|
--session_count_;
|
||||||
|
} else {
|
||||||
|
LOGE("Invalid crypto session count: session_count_ = %d", session_count_);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
TryTerminate();
|
||||||
M_RECORD(metrics_, crypto_session_life_span_, life_span_.AsMs());
|
M_RECORD(metrics_, crypto_session_life_span_, life_span_.AsMs());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,6 +254,9 @@ void CryptoSession::Init() {
|
|||||||
LOGE("OEMCrypto_Initialize failed: status = %d", static_cast<int>(sts));
|
LOGE("OEMCrypto_Initialize failed: status = %d", static_cast<int>(sts));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
termination_counter_ = Properties::delay_oem_crypto_termination()
|
||||||
|
? kMaxTerminateCountDown
|
||||||
|
: 0;
|
||||||
initialized_ = true;
|
initialized_ = true;
|
||||||
initialized = true;
|
initialized = true;
|
||||||
}
|
}
|
||||||
@@ -273,17 +286,20 @@ void CryptoSession::Init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CryptoSession::Terminate() {
|
bool CryptoSession::TryTerminate() {
|
||||||
LOGV("Terminating crypto session");
|
LOGV("Terminating crypto session");
|
||||||
WithStaticFieldWriteLock("Terminate", [&] {
|
WithStaticFieldWriteLock("TryTerminate", [&] {
|
||||||
LOGV("Terminating crypto session: initialized_ = %s, session_count_ = %d",
|
LOGV(
|
||||||
initialized_ ? "true" : "false", session_count_);
|
"Terminating crypto session: initialized_ = %s, session_count_ = %d, "
|
||||||
if (session_count_ > 0) {
|
"termination_counter_ = %d",
|
||||||
session_count_ -= 1;
|
initialized_ ? "true" : "false", session_count_, termination_counter_);
|
||||||
} else {
|
|
||||||
LOGE("Invalid crypto session count: session_count_ = %d", session_count_);
|
if (termination_counter_ > 0) {
|
||||||
|
--termination_counter_;
|
||||||
}
|
}
|
||||||
if (session_count_ > 0 || !initialized_) return;
|
if (session_count_ > 0 || termination_counter_ > 0 || !initialized_)
|
||||||
|
return false;
|
||||||
|
|
||||||
OEMCryptoResult sts;
|
OEMCryptoResult sts;
|
||||||
WithOecWriteLock("Terminate", [&] { sts = OEMCrypto_Terminate(); });
|
WithOecWriteLock("Terminate", [&] { sts = OEMCrypto_Terminate(); });
|
||||||
if (OEMCrypto_SUCCESS != sts) {
|
if (OEMCrypto_SUCCESS != sts) {
|
||||||
@@ -300,7 +316,15 @@ void CryptoSession::Terminate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initialized_ = false;
|
initialized_ = false;
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CryptoSession::DisableDelayedTermination() {
|
||||||
|
LOGV("Disable delayed termination");
|
||||||
|
WithStaticFieldWriteLock("DisableDelayedTermination",
|
||||||
|
[&] { termination_counter_ = 0; });
|
||||||
}
|
}
|
||||||
|
|
||||||
CdmResponseType CryptoSession::GetTokenFromKeybox(std::string* token) {
|
CdmResponseType CryptoSession::GetTokenFromKeybox(std::string* token) {
|
||||||
@@ -687,6 +711,11 @@ CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) {
|
|||||||
return MapOEMCryptoResult(sts, OPEN_CRYPTO_SESSION_ERROR, "Open");
|
return MapOEMCryptoResult(sts, OPEN_CRYPTO_SESSION_ERROR, "Open");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WithStaticFieldWriteLock("Open() termination_counter", [&] {
|
||||||
|
termination_counter_ =
|
||||||
|
Properties::delay_oem_crypto_termination() ? kMaxTerminateCountDown : 0;
|
||||||
|
});
|
||||||
|
|
||||||
oec_session_id_ = static_cast<CryptoSessionId>(sid);
|
oec_session_id_ = static_cast<CryptoSessionId>(sid);
|
||||||
LOGV("Opened session: id = %u", oec_session_id_);
|
LOGV("Opened session: id = %u", oec_session_id_);
|
||||||
open_ = true;
|
open_ = true;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ bool Properties::provisioning_messages_are_binary_;
|
|||||||
bool Properties::allow_service_certificate_requests_;
|
bool Properties::allow_service_certificate_requests_;
|
||||||
bool Properties::device_files_is_a_real_filesystem_;
|
bool Properties::device_files_is_a_real_filesystem_;
|
||||||
bool Properties::allow_restore_of_offline_licenses_with_release_;
|
bool Properties::allow_restore_of_offline_licenses_with_release_;
|
||||||
|
bool Properties::delay_oem_crypto_termination_;
|
||||||
std::unique_ptr<CdmClientPropertySetMap> Properties::session_property_set_;
|
std::unique_ptr<CdmClientPropertySetMap> Properties::session_property_set_;
|
||||||
|
|
||||||
bool Properties::AddSessionPropertySet(const CdmSessionId& session_id,
|
bool Properties::AddSessionPropertySet(const CdmSessionId& session_id,
|
||||||
|
|||||||
@@ -39,6 +39,11 @@ const bool kDeviceFilesIsARealFileSystem = true;
|
|||||||
// will be treated as a release request. If false, a restoration will fail.
|
// will be treated as a release request. If false, a restoration will fail.
|
||||||
const bool kAllowRestoreOfflineLicenseWithRelease = false;
|
const bool kAllowRestoreOfflineLicenseWithRelease = false;
|
||||||
|
|
||||||
|
// This controls whether an OEMCrypto_Terminate call is issued immediately
|
||||||
|
// after crypto session count declines to 0 or if a termination is
|
||||||
|
// delayed by a countdown timer. If false, termination is immediate.
|
||||||
|
const bool kPropertyDelayOemCryptoTermination = true;
|
||||||
|
|
||||||
} // namespace wvcdm
|
} // namespace wvcdm
|
||||||
|
|
||||||
#endif // CDM_BASE_WV_PROPERTIES_CONFIGURATION_H_
|
#endif // CDM_BASE_WV_PROPERTIES_CONFIGURATION_H_
|
||||||
|
|||||||
@@ -43,7 +43,10 @@ class Timer {
|
|||||||
~Timer();
|
~Timer();
|
||||||
|
|
||||||
bool Start(TimerHandler* handler, uint32_t time_in_secs);
|
bool Start(TimerHandler* handler, uint32_t time_in_secs);
|
||||||
void Stop();
|
// Enable |wait_for_exit| only if the method is being invoked from a thread
|
||||||
|
// other than the timer thread. Deadlock might occur if enabled and called
|
||||||
|
// from the timer thread.
|
||||||
|
void Stop(bool wait_for_exit);
|
||||||
bool IsRunning();
|
bool IsRunning();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -184,14 +184,17 @@ class WvContentDecryptionModule : public android::RefBase, public TimerHandler {
|
|||||||
|
|
||||||
uint32_t GenerateSessionSharingId();
|
uint32_t GenerateSessionSharingId();
|
||||||
|
|
||||||
// timer related methods to drive policy decisions
|
// Timer related methods to drive policy decisions
|
||||||
void EnablePolicyTimer();
|
void EnableTimer();
|
||||||
void DisablePolicyTimer();
|
void DisableTimer();
|
||||||
|
// Call this method only if invoked from a thread other than the timer thread.
|
||||||
|
// Otherwise this might result in deadlock.
|
||||||
|
void DisableTimerAndWaitForExit();
|
||||||
void OnTimerEvent();
|
void OnTimerEvent();
|
||||||
|
|
||||||
static std::mutex session_sharing_id_generation_lock_;
|
static std::mutex session_sharing_id_generation_lock_;
|
||||||
std::mutex policy_timer_lock_;
|
std::mutex timer_lock_;
|
||||||
Timer policy_timer_;
|
Timer timer_;
|
||||||
|
|
||||||
// instance variables
|
// instance variables
|
||||||
// This manages the lifetime of the CDM instances.
|
// This manages the lifetime of the CDM instances.
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ void Properties::InitOnce() {
|
|||||||
device_files_is_a_real_filesystem_ = kDeviceFilesIsARealFileSystem;
|
device_files_is_a_real_filesystem_ = kDeviceFilesIsARealFileSystem;
|
||||||
allow_restore_of_offline_licenses_with_release_ =
|
allow_restore_of_offline_licenses_with_release_ =
|
||||||
kAllowRestoreOfflineLicenseWithRelease;
|
kAllowRestoreOfflineLicenseWithRelease;
|
||||||
|
delay_oem_crypto_termination_ = kPropertyDelayOemCryptoTermination;
|
||||||
session_property_set_.reset(new CdmClientPropertySetMap());
|
session_property_set_.reset(new CdmClientPropertySetMap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,12 +28,13 @@ class Timer::Impl : virtual public android::RefBase {
|
|||||||
return run("wvcdm::Timer::Impl") == android::NO_ERROR;
|
return run("wvcdm::Timer::Impl") == android::NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stop() {
|
void Stop(bool wait_for_exit) {
|
||||||
{
|
stop_condition_.signal();
|
||||||
android::Mutex::Autolock autoLock(lock_);
|
if (wait_for_exit) {
|
||||||
stop_condition_.signal();
|
requestExitAndWait();
|
||||||
|
} else {
|
||||||
|
requestExit();
|
||||||
}
|
}
|
||||||
requestExitAndWait();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -63,8 +64,8 @@ class Timer::Impl : virtual public android::RefBase {
|
|||||||
return impl_thread_->Start(handler, time_in_secs);
|
return impl_thread_->Start(handler, time_in_secs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stop() {
|
void Stop(bool wait_for_exit) {
|
||||||
impl_thread_->Stop();
|
impl_thread_->Stop(wait_for_exit);
|
||||||
impl_thread_.clear();
|
impl_thread_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +79,7 @@ class Timer::Impl : virtual public android::RefBase {
|
|||||||
Timer::Timer() : impl_(new Timer::Impl()) {}
|
Timer::Timer() : impl_(new Timer::Impl()) {}
|
||||||
|
|
||||||
Timer::~Timer() {
|
Timer::~Timer() {
|
||||||
if (IsRunning()) Stop();
|
if (IsRunning()) Stop(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Timer::Start(TimerHandler* handler, uint32_t time_in_secs) {
|
bool Timer::Start(TimerHandler* handler, uint32_t time_in_secs) {
|
||||||
@@ -87,7 +88,7 @@ bool Timer::Start(TimerHandler* handler, uint32_t time_in_secs) {
|
|||||||
return impl_->Start(handler, time_in_secs);
|
return impl_->Start(handler, time_in_secs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timer::Stop() { impl_->Stop(); }
|
void Timer::Stop(bool wait_for_exit) { impl_->Stop(wait_for_exit); }
|
||||||
|
|
||||||
bool Timer::IsRunning() { return impl_->IsRunning(); }
|
bool Timer::IsRunning() { return impl_->IsRunning(); }
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
#include "wv_cdm_event_listener.h"
|
#include "wv_cdm_event_listener.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
const int kCdmPolicyTimerDurationSeconds = 1;
|
const int kCdmTimerDurationSeconds = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace wvcdm {
|
namespace wvcdm {
|
||||||
@@ -28,8 +28,10 @@ std::mutex WvContentDecryptionModule::session_sharing_id_generation_lock_;
|
|||||||
WvContentDecryptionModule::WvContentDecryptionModule() {}
|
WvContentDecryptionModule::WvContentDecryptionModule() {}
|
||||||
|
|
||||||
WvContentDecryptionModule::~WvContentDecryptionModule() {
|
WvContentDecryptionModule::~WvContentDecryptionModule() {
|
||||||
|
CryptoSession::DisableDelayedTermination();
|
||||||
CloseAllCdms();
|
CloseAllCdms();
|
||||||
DisablePolicyTimer();
|
CryptoSession::TryTerminate();
|
||||||
|
DisableTimerAndWaitForExit();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WvContentDecryptionModule::IsSupported(const std::string& init_data_type) {
|
bool WvContentDecryptionModule::IsSupported(const std::string& init_data_type) {
|
||||||
@@ -123,16 +125,9 @@ CdmResponseType WvContentDecryptionModule::GenerateKeyRequest(
|
|||||||
sts = cdm_engine->GenerateKeyRequest(session_id, key_set_id,
|
sts = cdm_engine->GenerateKeyRequest(session_id, key_set_id,
|
||||||
initialization_data, license_type,
|
initialization_data, license_type,
|
||||||
app_parameters, key_request);
|
app_parameters, key_request);
|
||||||
switch (license_type) {
|
if (license_type == kLicenseTypeRelease && sts != KEY_MESSAGE) {
|
||||||
case kLicenseTypeRelease:
|
cdm_engine->CloseKeySetSession(key_set_id);
|
||||||
if (sts != KEY_MESSAGE) {
|
cdm_by_session_id_.erase(key_set_id);
|
||||||
cdm_engine->CloseKeySetSession(key_set_id);
|
|
||||||
cdm_by_session_id_.erase(key_set_id);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (sts == KEY_MESSAGE) EnablePolicyTimer();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return sts;
|
return sts;
|
||||||
}
|
}
|
||||||
@@ -165,7 +160,6 @@ CdmResponseType WvContentDecryptionModule::RestoreKey(
|
|||||||
if (!cdm_engine) return SESSION_NOT_FOUND_4;
|
if (!cdm_engine) return SESSION_NOT_FOUND_4;
|
||||||
CdmResponseType sts;
|
CdmResponseType sts;
|
||||||
sts = cdm_engine->RestoreKey(session_id, key_set_id);
|
sts = cdm_engine->RestoreKey(session_id, key_set_id);
|
||||||
if (sts == KEY_ADDED) EnablePolicyTimer();
|
|
||||||
return sts;
|
return sts;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -355,21 +349,28 @@ WvContentDecryptionModule::CdmInfo::CdmInfo()
|
|||||||
|
|
||||||
CdmEngine* WvContentDecryptionModule::EnsureCdmForIdentifier(
|
CdmEngine* WvContentDecryptionModule::EnsureCdmForIdentifier(
|
||||||
const CdmIdentifier& identifier) {
|
const CdmIdentifier& identifier) {
|
||||||
std::unique_lock<std::mutex> auto_lock(cdms_lock_);
|
CdmEngine* cdm_engine;
|
||||||
if (cdms_.find(identifier) == cdms_.end()) {
|
bool enable_timer = false;
|
||||||
// Accessing the map entry will create a new instance using the default
|
{
|
||||||
// constructor. We then need to provide it with two pieces of info: The
|
std::unique_lock<std::mutex> auto_lock(cdms_lock_);
|
||||||
// origin provided by the app and an identifier that uniquely identifies
|
if (cdms_.size() == 0) enable_timer = true;
|
||||||
// this CDM. We concatenate all pieces of the CdmIdentifier in order to
|
if (cdms_.find(identifier) == cdms_.end()) {
|
||||||
// create an ID that is unique to that identifier.
|
// Accessing the map entry will create a new instance using the default
|
||||||
cdms_[identifier].file_system.set_origin(identifier.origin);
|
// constructor. We then need to provide it with two pieces of info: The
|
||||||
cdms_[identifier].file_system.set_identifier(identifier.spoid +
|
// origin provided by the app and an identifier that uniquely identifies
|
||||||
identifier.origin);
|
// this CDM. We concatenate all pieces of the CdmIdentifier in order to
|
||||||
cdms_[identifier].cdm_engine->SetAppPackageName(
|
// create an ID that is unique to that identifier.
|
||||||
identifier.app_package_name);
|
cdms_[identifier].file_system.set_origin(identifier.origin);
|
||||||
cdms_[identifier].cdm_engine->SetSpoid(identifier.spoid);
|
cdms_[identifier].file_system.set_identifier(identifier.spoid +
|
||||||
|
identifier.origin);
|
||||||
|
cdms_[identifier].cdm_engine->SetAppPackageName(
|
||||||
|
identifier.app_package_name);
|
||||||
|
cdms_[identifier].cdm_engine->SetSpoid(identifier.spoid);
|
||||||
|
}
|
||||||
|
cdm_engine = cdms_[identifier].cdm_engine.get();
|
||||||
}
|
}
|
||||||
CdmEngine* cdm_engine = cdms_[identifier].cdm_engine.get();
|
// Do not enable timer while holding on to the |cdms_lock_|
|
||||||
|
if (enable_timer) EnableTimer();
|
||||||
|
|
||||||
return cdm_engine;
|
return cdm_engine;
|
||||||
}
|
}
|
||||||
@@ -392,35 +393,23 @@ void WvContentDecryptionModule::CloseAllCdms() {
|
|||||||
|
|
||||||
CdmResponseType WvContentDecryptionModule::CloseCdm(
|
CdmResponseType WvContentDecryptionModule::CloseCdm(
|
||||||
const CdmIdentifier& cdm_identifier) {
|
const CdmIdentifier& cdm_identifier) {
|
||||||
// The policy timer ultimately calls OnTimerEvent (which wants to
|
std::unique_lock<std::mutex> auto_lock(cdms_lock_);
|
||||||
// acquire cdms_lock_). Therefore, we cannot acquire cdms_lock_ and then the
|
auto it = cdms_.find(cdm_identifier);
|
||||||
// policy_timer_lock_ (via DisablePolicyTimer) at the same time.
|
if (it == cdms_.end()) {
|
||||||
// Acquire the cdms_lock_ first, in its own scope.
|
LOGE("WVContentDecryptionModule::Close. cdm_identifier not found.");
|
||||||
bool cdms_empty = false;
|
// TODO(blueeyes): Create a better error.
|
||||||
{
|
return UNKNOWN_ERROR;
|
||||||
std::unique_lock<std::mutex> 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_.begin();
|
|
||||||
session_it != cdm_by_session_id_.end();) {
|
|
||||||
if (session_it->second == it->second.cdm_engine.get()) {
|
|
||||||
session_it = cdm_by_session_id_.erase(session_it);
|
|
||||||
} else {
|
|
||||||
++session_it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cdms_.erase(it);
|
|
||||||
cdms_empty = cdms_.empty();
|
|
||||||
}
|
}
|
||||||
|
// Remove any sessions that point to this engine.
|
||||||
if (cdms_empty) {
|
for (auto session_it = cdm_by_session_id_.begin();
|
||||||
DisablePolicyTimer();
|
session_it != cdm_by_session_id_.end();) {
|
||||||
|
if (session_it->second == it->second.cdm_engine.get()) {
|
||||||
|
session_it = cdm_by_session_id_.erase(session_it);
|
||||||
|
} else {
|
||||||
|
++session_it;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
cdms_.erase(it);
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -462,23 +451,60 @@ CdmResponseType WvContentDecryptionModule::GetDecryptHashError(
|
|||||||
return cdm_engine->GetDecryptHashError(session_id, hash_error_string);
|
return cdm_engine->GetDecryptHashError(session_id, hash_error_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WvContentDecryptionModule::EnablePolicyTimer() {
|
void WvContentDecryptionModule::EnableTimer() {
|
||||||
std::unique_lock<std::mutex> auto_lock(policy_timer_lock_);
|
std::unique_lock<std::mutex> auto_lock(timer_lock_);
|
||||||
if (!policy_timer_.IsRunning())
|
if (!timer_.IsRunning()) timer_.Start(this, kCdmTimerDurationSeconds);
|
||||||
policy_timer_.Start(this, kCdmPolicyTimerDurationSeconds);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WvContentDecryptionModule::DisablePolicyTimer() {
|
void WvContentDecryptionModule::DisableTimer() {
|
||||||
std::unique_lock<std::mutex> auto_lock(policy_timer_lock_);
|
std::unique_lock<std::mutex> auto_lock(timer_lock_);
|
||||||
if (policy_timer_.IsRunning()) {
|
if (timer_.IsRunning()) {
|
||||||
policy_timer_.Stop();
|
timer_.Stop(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WvContentDecryptionModule::DisableTimerAndWaitForExit() {
|
||||||
|
std::unique_lock<std::mutex> auto_lock(timer_lock_);
|
||||||
|
if (timer_.IsRunning()) {
|
||||||
|
timer_.Stop(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WvContentDecryptionModule::OnTimerEvent() {
|
void WvContentDecryptionModule::OnTimerEvent() {
|
||||||
std::unique_lock<std::mutex> auto_lock(cdms_lock_);
|
bool disable_timer = false;
|
||||||
for (auto it = cdms_.begin(); it != cdms_.end(); ++it) {
|
{
|
||||||
it->second.cdm_engine->OnTimerEvent();
|
std::unique_lock<std::mutex> auto_lock(cdms_lock_);
|
||||||
|
for (auto it = cdms_.begin(); it != cdms_.end(); ++it) {
|
||||||
|
it->second.cdm_engine->OnTimerEvent();
|
||||||
|
}
|
||||||
|
if (cdms_.empty()) {
|
||||||
|
if (CryptoSession::TryTerminate()) {
|
||||||
|
// If CryptoSession is in a state to be terminated, try acquiring the
|
||||||
|
// |timer_lock_| before deciding whether to disable the timer. If the
|
||||||
|
// lock cannot be acquired, there is no need to disable the timer.
|
||||||
|
// The |timer_lock_| is either being held by
|
||||||
|
// * EnableTimer - This does not appear possible, but if it did
|
||||||
|
// happen |OnTimerEvent| will be called again. The timer can be
|
||||||
|
// disabled during a future call.
|
||||||
|
// * DisableTimer - If so, allow that call to disable the timer. No
|
||||||
|
// need to call to disable it here.
|
||||||
|
// * DisableTimerAndWaitForExit - If so, allow that call to disable
|
||||||
|
// the timer. No need to call to disable it here. Note that
|
||||||
|
// |DisableTimerAndWaitForExit| will try to stop the timer but
|
||||||
|
// wait for it to exit. This might kick off the timer thread one
|
||||||
|
// last time, which will call into OnTimerEvent. Calling
|
||||||
|
// |DisableTimer| here will result in deadlock.
|
||||||
|
if (timer_lock_.try_lock()) {
|
||||||
|
disable_timer = true;
|
||||||
|
timer_lock_.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release |cdms_lock_| before attempting to disable the timer
|
||||||
|
if (disable_timer) {
|
||||||
|
DisableTimer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,17 +8,46 @@
|
|||||||
|
|
||||||
namespace wvcdm {
|
namespace wvcdm {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// duration in seconds after starting the timer
|
||||||
|
constexpr uint32_t kTestDuration = 5;
|
||||||
|
// duration in seconds after test completes, before evaluating the result
|
||||||
|
constexpr uint32_t kPostTestDuration = 3;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
class TestTimerHandler : public TimerHandler {
|
class TestTimerHandler : public TimerHandler {
|
||||||
public:
|
public:
|
||||||
TestTimerHandler() : timer_events_(0){};
|
TestTimerHandler() : timer_events_count_(0){};
|
||||||
virtual ~TestTimerHandler(){};
|
virtual ~TestTimerHandler(){};
|
||||||
|
|
||||||
virtual void OnTimerEvent() { timer_events_++; }
|
virtual void OnTimerEvent() { timer_events_count_++; }
|
||||||
|
|
||||||
uint32_t timer_events() { return timer_events_; }
|
uint32_t timer_events_count() { return timer_events_count_; }
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
uint32_t timer_events_;
|
uint32_t timer_events_count_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TestStopTimerHandler : public TestTimerHandler {
|
||||||
|
public:
|
||||||
|
TestStopTimerHandler(uint32_t stop_at_timer_events_count, Timer* timer)
|
||||||
|
: stop_at_timer_events_count_(stop_at_timer_events_count),
|
||||||
|
timer_(timer) {}
|
||||||
|
virtual ~TestStopTimerHandler() {}
|
||||||
|
|
||||||
|
virtual void OnTimerEvent() {
|
||||||
|
if (++timer_events_count_ >= stop_at_timer_events_count_) {
|
||||||
|
if (timer_ != nullptr) {
|
||||||
|
timer_->Stop(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint32_t stop_at_timer_events_count_;
|
||||||
|
Timer* timer_;
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST(TimerTest, ParametersCheck) {
|
TEST(TimerTest, ParametersCheck) {
|
||||||
@@ -29,25 +58,58 @@ TEST(TimerTest, ParametersCheck) {
|
|||||||
EXPECT_FALSE(timer.Start(&handler, 0));
|
EXPECT_FALSE(timer.Start(&handler, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(TimerTest, TimerCheck) {
|
TEST(TimerTest, TimerCheck_StopAndWaitForExit) {
|
||||||
TestTimerHandler handler;
|
TestTimerHandler handler;
|
||||||
Timer timer;
|
Timer timer;
|
||||||
uint32_t duration = 10;
|
|
||||||
|
|
||||||
EXPECT_EQ(0u, handler.timer_events());
|
EXPECT_EQ(0u, handler.timer_events_count());
|
||||||
EXPECT_FALSE(timer.IsRunning());
|
EXPECT_FALSE(timer.IsRunning());
|
||||||
|
|
||||||
EXPECT_TRUE(timer.Start(&handler, 1));
|
EXPECT_TRUE(timer.Start(&handler, 1));
|
||||||
EXPECT_TRUE(timer.IsRunning());
|
EXPECT_TRUE(timer.IsRunning());
|
||||||
sleep(duration);
|
sleep(kTestDuration);
|
||||||
|
|
||||||
EXPECT_LE(duration - 1, handler.timer_events());
|
EXPECT_LE(kTestDuration - 1, handler.timer_events_count());
|
||||||
EXPECT_LE(handler.timer_events(), duration + 1);
|
EXPECT_LE(handler.timer_events_count(), kTestDuration + 1);
|
||||||
timer.Stop();
|
timer.Stop(true);
|
||||||
EXPECT_FALSE(timer.IsRunning());
|
EXPECT_FALSE(timer.IsRunning());
|
||||||
sleep(duration);
|
sleep(kPostTestDuration);
|
||||||
|
|
||||||
EXPECT_LE(duration - 1, handler.timer_events());
|
EXPECT_LE(kTestDuration - 1, handler.timer_events_count());
|
||||||
EXPECT_LE(handler.timer_events(), duration + 1);
|
EXPECT_LE(handler.timer_events_count(), kTestDuration + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(TimerTest, TimerCheck_Stop) {
|
||||||
|
TestTimerHandler handler;
|
||||||
|
Timer timer;
|
||||||
|
|
||||||
|
EXPECT_EQ(0u, handler.timer_events_count());
|
||||||
|
EXPECT_FALSE(timer.IsRunning());
|
||||||
|
|
||||||
|
EXPECT_TRUE(timer.Start(&handler, 1));
|
||||||
|
EXPECT_TRUE(timer.IsRunning());
|
||||||
|
sleep(kTestDuration);
|
||||||
|
|
||||||
|
EXPECT_LE(kTestDuration - 1, handler.timer_events_count());
|
||||||
|
EXPECT_LE(handler.timer_events_count(), kTestDuration + 1);
|
||||||
|
timer.Stop(false);
|
||||||
|
sleep(kPostTestDuration);
|
||||||
|
|
||||||
|
EXPECT_FALSE(timer.IsRunning());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TimerTest, StopOnTimerThread) {
|
||||||
|
Timer timer;
|
||||||
|
TestStopTimerHandler handler(kTestDuration - 2, &timer);
|
||||||
|
|
||||||
|
EXPECT_EQ(0u, handler.timer_events_count());
|
||||||
|
EXPECT_FALSE(timer.IsRunning());
|
||||||
|
|
||||||
|
EXPECT_TRUE(timer.Start(&handler, 1));
|
||||||
|
EXPECT_TRUE(timer.IsRunning());
|
||||||
|
sleep(kTestDuration + kPostTestDuration);
|
||||||
|
|
||||||
|
EXPECT_FALSE(timer.IsRunning());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace wvcdm
|
} // namespace wvcdm
|
||||||
|
|||||||
Reference in New Issue
Block a user