Improve android MediaDrm property latency
[ Merge of http://go/wvgerrit/89848 ] Apps query a number of properties at initialization. The mediaDrm API getProperty allows the query of a single property at a time. This causes a series of requests. If no crypto sessions are concurrently open, a series of expensive OEMCrypto Initialization and Termination calls will occur. In this change OEMCrypto termination is delayed. If an OEMCrypto Terminate is followed in close succession by an Initialize, neither will occur avoiding the overhead. A timer enables a countdown process. If no session activity occurs, the timer will eventually terminate OEMCrypto and exit. Bug: 136282358 Test: Android unit/integration tests Change-Id: I442b7919b4e7835c52583516c8bc64d0c150241d
This commit is contained in:
@@ -18,7 +18,7 @@
|
||||
#include "wv_cdm_event_listener.h"
|
||||
|
||||
namespace {
|
||||
const int kCdmPolicyTimerDurationSeconds = 1;
|
||||
const int kCdmTimerDurationSeconds = 1;
|
||||
}
|
||||
|
||||
namespace wvcdm {
|
||||
@@ -28,8 +28,10 @@ std::mutex WvContentDecryptionModule::session_sharing_id_generation_lock_;
|
||||
WvContentDecryptionModule::WvContentDecryptionModule() {}
|
||||
|
||||
WvContentDecryptionModule::~WvContentDecryptionModule() {
|
||||
CryptoSession::DisableDelayedTermination();
|
||||
CloseAllCdms();
|
||||
DisablePolicyTimer();
|
||||
CryptoSession::TryTerminate();
|
||||
DisableTimerAndWaitForExit();
|
||||
}
|
||||
|
||||
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,
|
||||
initialization_data, license_type,
|
||||
app_parameters, key_request);
|
||||
switch (license_type) {
|
||||
case kLicenseTypeRelease:
|
||||
if (sts != KEY_MESSAGE) {
|
||||
cdm_engine->CloseKeySetSession(key_set_id);
|
||||
cdm_by_session_id_.erase(key_set_id);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (sts == KEY_MESSAGE) EnablePolicyTimer();
|
||||
break;
|
||||
if (license_type == kLicenseTypeRelease && sts != KEY_MESSAGE) {
|
||||
cdm_engine->CloseKeySetSession(key_set_id);
|
||||
cdm_by_session_id_.erase(key_set_id);
|
||||
}
|
||||
return sts;
|
||||
}
|
||||
@@ -165,7 +160,6 @@ CdmResponseType WvContentDecryptionModule::RestoreKey(
|
||||
if (!cdm_engine) return SESSION_NOT_FOUND_4;
|
||||
CdmResponseType sts;
|
||||
sts = cdm_engine->RestoreKey(session_id, key_set_id);
|
||||
if (sts == KEY_ADDED) EnablePolicyTimer();
|
||||
return sts;
|
||||
}
|
||||
|
||||
@@ -355,21 +349,28 @@ WvContentDecryptionModule::CdmInfo::CdmInfo()
|
||||
|
||||
CdmEngine* WvContentDecryptionModule::EnsureCdmForIdentifier(
|
||||
const CdmIdentifier& identifier) {
|
||||
std::unique_lock<std::mutex> auto_lock(cdms_lock_);
|
||||
if (cdms_.find(identifier) == cdms_.end()) {
|
||||
// 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
|
||||
// origin provided by the app and an identifier that uniquely identifies
|
||||
// this CDM. We concatenate all pieces of the CdmIdentifier in order to
|
||||
// create an ID that is unique to that identifier.
|
||||
cdms_[identifier].file_system.set_origin(identifier.origin);
|
||||
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);
|
||||
CdmEngine* cdm_engine;
|
||||
bool enable_timer = false;
|
||||
{
|
||||
std::unique_lock<std::mutex> auto_lock(cdms_lock_);
|
||||
if (cdms_.size() == 0) enable_timer = true;
|
||||
if (cdms_.find(identifier) == cdms_.end()) {
|
||||
// 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
|
||||
// origin provided by the app and an identifier that uniquely identifies
|
||||
// this CDM. We concatenate all pieces of the CdmIdentifier in order to
|
||||
// create an ID that is unique to that identifier.
|
||||
cdms_[identifier].file_system.set_origin(identifier.origin);
|
||||
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;
|
||||
}
|
||||
@@ -392,35 +393,23 @@ void WvContentDecryptionModule::CloseAllCdms() {
|
||||
|
||||
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;
|
||||
{
|
||||
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();
|
||||
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;
|
||||
}
|
||||
|
||||
if (cdms_empty) {
|
||||
DisablePolicyTimer();
|
||||
// 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);
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
@@ -462,23 +451,60 @@ CdmResponseType WvContentDecryptionModule::GetDecryptHashError(
|
||||
return cdm_engine->GetDecryptHashError(session_id, hash_error_string);
|
||||
}
|
||||
|
||||
void WvContentDecryptionModule::EnablePolicyTimer() {
|
||||
std::unique_lock<std::mutex> auto_lock(policy_timer_lock_);
|
||||
if (!policy_timer_.IsRunning())
|
||||
policy_timer_.Start(this, kCdmPolicyTimerDurationSeconds);
|
||||
void WvContentDecryptionModule::EnableTimer() {
|
||||
std::unique_lock<std::mutex> auto_lock(timer_lock_);
|
||||
if (!timer_.IsRunning()) timer_.Start(this, kCdmTimerDurationSeconds);
|
||||
}
|
||||
|
||||
void WvContentDecryptionModule::DisablePolicyTimer() {
|
||||
std::unique_lock<std::mutex> auto_lock(policy_timer_lock_);
|
||||
if (policy_timer_.IsRunning()) {
|
||||
policy_timer_.Stop();
|
||||
void WvContentDecryptionModule::DisableTimer() {
|
||||
std::unique_lock<std::mutex> auto_lock(timer_lock_);
|
||||
if (timer_.IsRunning()) {
|
||||
timer_.Stop(false);
|
||||
}
|
||||
}
|
||||
|
||||
void WvContentDecryptionModule::DisableTimerAndWaitForExit() {
|
||||
std::unique_lock<std::mutex> auto_lock(timer_lock_);
|
||||
if (timer_.IsRunning()) {
|
||||
timer_.Stop(true);
|
||||
}
|
||||
}
|
||||
|
||||
void WvContentDecryptionModule::OnTimerEvent() {
|
||||
std::unique_lock<std::mutex> auto_lock(cdms_lock_);
|
||||
for (auto it = cdms_.begin(); it != cdms_.end(); ++it) {
|
||||
it->second.cdm_engine->OnTimerEvent();
|
||||
bool disable_timer = false;
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user