File util, generic crypto, and key query

This CL merges several CLs from the widevine repo:

http://go/wvgerrit/18012 Add support for querying allowed usage for key.
http://go/wvgerrit/17971 Add per-origin storage.
http://go/wvgerrit/18152 Add OEMCrypto's generic crypto operations to CDM.
http://go/wvgerrit/17911 QueryKeyControlInfo => QueryOemCryptoSessionId

Note: numbering in wv_cdm_types.h was added in this CL and will be
back ported to wvgerrit in a future CL.

Change-Id: Idb9e9a67e94f62f25dc16c5307f75a08b3430b64
This commit is contained in:
Fred Gylys-Colwell
2016-09-14 12:44:09 -07:00
parent 24124ea6e3
commit eb3f8b786a
56 changed files with 4632 additions and 2083 deletions

View File

@@ -3,10 +3,7 @@
#include "policy_engine.h"
#include <limits.h>
#include <sstream>
#include <string>
#include <vector>
#include "clock.h"
#include "log.h"
@@ -17,6 +14,13 @@
using video_widevine_server::sdk::License;
namespace {
const int64_t kHdcpCheckInterval = 10;
const uint32_t kNoResolution = 0;
} // namespace
namespace wvcdm {
PolicyEngine::PolicyEngine(CdmSessionId session_id,
@@ -32,18 +36,43 @@ PolicyEngine::PolicyEngine(CdmSessionId session_id,
policy_max_duration_seconds_(0),
session_id_(session_id),
event_listener_(event_listener),
max_res_engine_(new MaxResEngine(crypto_session)),
clock_(new Clock) {}
license_keys_(new LicenseKeys),
clock_(new Clock) {
InitDevice(crypto_session);
}
PolicyEngine::~PolicyEngine() {}
bool PolicyEngine::CanDecrypt(const KeyId& key_id) {
if (keys_status_.find(key_id) == keys_status_.end()) {
if (license_keys_->IsContentKey(key_id)) {
return license_keys_->CanDecryptContent(key_id);
} else {
LOGE("PolicyEngine::CanDecrypt Key '%s' not in license.",
b2a_hex(key_id).c_str());
return false;
}
return keys_status_[key_id] == kKeyStatusUsable;
}
void PolicyEngine::InitDevice(CryptoSession* crypto_session) {
current_resolution_ = kNoResolution;
next_device_check_ = 0;
crypto_session_ = crypto_session;
}
void PolicyEngine::CheckDevice(int64_t current_time) {
if (current_time < next_device_check_) {
return;
}
if (!license_keys_->Empty() && current_resolution_ != kNoResolution) {
CryptoSession::HdcpCapability current_hdcp_level;
CryptoSession::HdcpCapability ignored;
if (!crypto_session_->GetHdcpCapabilities(&current_hdcp_level, &ignored)) {
current_hdcp_level = HDCP_NONE;
}
license_keys_->ApplyConstraints(current_resolution_, current_hdcp_level);
next_device_check_ = current_time + kHdcpCheckInterval;
}
}
void PolicyEngine::OnTimerEvent() {
@@ -57,7 +86,8 @@ void PolicyEngine::OnTimerEvent() {
return;
}
max_res_engine_->OnTimerEvent();
// Check device conditions that affect playability (HDCP, resolution)
CheckDevice(current_time);
bool renewal_needed = false;
@@ -110,17 +140,8 @@ void PolicyEngine::SetLicense(const License& license) {
license_id_.Clear();
license_id_.CopyFrom(license.id());
policy_.Clear();
// Extract content key ids.
keys_status_.clear();
for (int key_index = 0; key_index < license.key_size(); ++key_index) {
const License::KeyContainer& key = license.key(key_index);
if (key.type() == License::KeyContainer::CONTENT && key.has_id())
keys_status_[key.id()] = kKeyStatusInternalError;
}
license_keys_->SetFromLicense(license);
UpdateLicense(license);
max_res_engine_->SetLicense(license);
}
void PolicyEngine::SetLicenseForRelease(const License& license) {
@@ -130,7 +151,6 @@ void PolicyEngine::SetLicenseForRelease(const License& license) {
// Expire any old keys.
NotifyKeysChange(kKeyStatusExpired);
UpdateLicense(license);
}
@@ -220,7 +240,7 @@ void PolicyEngine::DecryptionEvent() {
}
void PolicyEngine::NotifyResolution(uint32_t width, uint32_t height) {
max_res_engine_->SetResolution(width, height);
SetDeviceResolution(width, height);
}
void PolicyEngine::NotifySessionExpiration() {
@@ -228,35 +248,46 @@ void PolicyEngine::NotifySessionExpiration() {
NotifyKeysChange(kKeyStatusExpired);
}
CdmResponseType PolicyEngine::Query(CdmQueryMap* key_info) {
CdmResponseType PolicyEngine::Query(CdmQueryMap* query_response) {
std::stringstream ss;
int64_t current_time = clock_->GetCurrentTime();
if (license_state_ == kLicenseStateInitial) {
key_info->clear();
query_response->clear();
return NO_ERROR;
}
(*key_info)[QUERY_KEY_LICENSE_TYPE] =
(*query_response)[QUERY_KEY_LICENSE_TYPE] =
license_id_.type() == video_widevine_server::sdk::STREAMING
? QUERY_VALUE_STREAMING
: QUERY_VALUE_OFFLINE;
(*key_info)[QUERY_KEY_PLAY_ALLOWED] =
(*query_response)[QUERY_KEY_PLAY_ALLOWED] =
policy_.can_play() ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
(*key_info)[QUERY_KEY_PERSIST_ALLOWED] =
(*query_response)[QUERY_KEY_PERSIST_ALLOWED] =
policy_.can_persist() ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
(*key_info)[QUERY_KEY_RENEW_ALLOWED] =
(*query_response)[QUERY_KEY_RENEW_ALLOWED] =
policy_.can_renew() ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
ss << GetLicenseDurationRemaining(current_time);
(*key_info)[QUERY_KEY_LICENSE_DURATION_REMAINING] = ss.str();
(*query_response)[QUERY_KEY_LICENSE_DURATION_REMAINING] = ss.str();
ss.str("");
ss << GetPlaybackDurationRemaining(current_time);
(*key_info)[QUERY_KEY_PLAYBACK_DURATION_REMAINING] = ss.str();
(*key_info)[QUERY_KEY_RENEWAL_SERVER_URL] = policy_.renewal_server_url();
(*query_response)[QUERY_KEY_PLAYBACK_DURATION_REMAINING] = ss.str();
(*query_response)[QUERY_KEY_RENEWAL_SERVER_URL] = policy_.renewal_server_url();
return NO_ERROR;
}
CdmResponseType PolicyEngine::QueryKeyAllowedUsage(
const KeyId& key_id, CdmKeyAllowedUsage* key_usage) {
if (NULL == key_usage) {
return INVALID_PARAMETERS_ENG_12;
}
if (license_keys_->GetAllowedUsage(key_id, key_usage)) {
return NO_ERROR;
}
return KEY_NOT_FOUND_1;
}
bool PolicyEngine::GetSecondsSinceStarted(int64_t* seconds_since_started) {
if (playback_start_time_ == 0) return false;
@@ -351,25 +382,14 @@ bool PolicyEngine::IsRenewalRetryIntervalExpired(int64_t current_time) {
}
void PolicyEngine::NotifyKeysChange(CdmKeyStatus new_status) {
bool keys_changed = false;
bool keys_changed;
bool has_new_usable_key = false;
for (std::map<KeyId, CdmKeyStatus>::iterator it = keys_status_.begin();
it != keys_status_.end(); ++it) {
const KeyId key_id = it->first;
CdmKeyStatus& key_status = it->second;
CdmKeyStatus updated_status = new_status;
if (updated_status == kKeyStatusUsable) {
if (!max_res_engine_->CanDecrypt(key_id))
updated_status = kKeyStatusOutputNotAllowed;
}
if (key_status != updated_status) {
key_status = updated_status;
if (updated_status == kKeyStatusUsable) has_new_usable_key = true;
keys_changed = true;
}
}
if (keys_changed && event_listener_) {
event_listener_->OnSessionKeysChange(session_id_, keys_status_,
keys_changed = license_keys_->ApplyStatusChange(new_status,
&has_new_usable_key);
if (event_listener_ && keys_changed) {
CdmKeyStatusMap content_keys;
license_keys_->ExtractKeyStatuses(&content_keys);
event_listener_->OnSessionKeysChange(session_id_, content_keys,
has_new_usable_key);
}
}
@@ -387,8 +407,4 @@ void PolicyEngine::NotifyExpirationUpdate() {
void PolicyEngine::set_clock(Clock* clock) { clock_.reset(clock); }
void PolicyEngine::set_max_res_engine(MaxResEngine* max_res_engine) {
max_res_engine_.reset(max_res_engine);
}
} // wvcdm
} // namespace wvcdm