Enable usage reporting
[ Merge from Widevine CDM repo of https://widevine-internal-review.googlesource.com/#/c/10171/ and https://widevine-internal-review.googlesource.com/#/c/10172/ ] Updated license_protocol.proto from constituent protos in google3 These changes make use of OEMCrypto v9 changes to support usage reporting. Usage reporting may be enabled for streaming (by means of secure stops) and offline playback by a provider session token specified in the license. Changes include periodically updating usage information for relevant sessions and reporting and releasing usage information as needed. The CDM has removed all references to Secure Stops. This change updates the Android API implementation to comply. b/11987015 Change-Id: Ibb6f2ced4ef20ee349ca1ae6412ce686b2b5d085
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
#include "log.h"
|
||||
#include "openssl/sha.h"
|
||||
#include "properties.h"
|
||||
#include "string_conversions.h"
|
||||
|
||||
// Protobuf generated classes.
|
||||
using video_widevine_client::sdk::DeviceCertificate;
|
||||
@@ -17,9 +18,12 @@ using video_widevine_client::sdk::HashedFile;
|
||||
using video_widevine_client::sdk::License;
|
||||
using video_widevine_client::sdk::License_LicenseState_ACTIVE;
|
||||
using video_widevine_client::sdk::License_LicenseState_RELEASING;
|
||||
using video_widevine_client::sdk::UsageInfo;
|
||||
using video_widevine_client::sdk::UsageInfo_ProviderSession;
|
||||
|
||||
namespace {
|
||||
const char kCertificateFileName[] = "cert.bin";
|
||||
const char kUsageInfoFileName[] = "usage.bin";
|
||||
const char kLicenseFileNameExt[] = ".lic";
|
||||
const char kWildcard[] = "*";
|
||||
const char kDirectoryDelimiter = '/';
|
||||
@@ -27,15 +31,15 @@ const char* kSecurityLevelPathCompatibilityExclusionList[] = {"ay64.dat"};
|
||||
size_t kSecurityLevelPathCompatibilityExclusionListSize =
|
||||
sizeof(kSecurityLevelPathCompatibilityExclusionList) /
|
||||
sizeof(*kSecurityLevelPathCompatibilityExclusionList);
|
||||
} // namespace
|
||||
} // unnamed namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
bool DeviceFiles::Init(const File* handle, CdmSecurityLevel security_level) {
|
||||
if (handle == NULL) {
|
||||
LOGW("DeviceFiles::Init: Invalid file handle parameter");
|
||||
return false;
|
||||
}
|
||||
DeviceFiles::~DeviceFiles() {
|
||||
if (!file_ && !test_file_) delete file_;
|
||||
}
|
||||
|
||||
bool DeviceFiles::Init(CdmSecurityLevel security_level) {
|
||||
switch (security_level) {
|
||||
case kSecurityLevelL1:
|
||||
case kSecurityLevelL2:
|
||||
@@ -45,7 +49,7 @@ bool DeviceFiles::Init(const File* handle, CdmSecurityLevel security_level) {
|
||||
LOGW("DeviceFiles::Init: Unsupported security level %d", security_level);
|
||||
return false;
|
||||
}
|
||||
file_ = const_cast<File*>(handle);
|
||||
file_ = new File();
|
||||
security_level_ = security_level;
|
||||
initialized_ = true;
|
||||
return true;
|
||||
@@ -68,24 +72,10 @@ bool DeviceFiles::StoreCertificate(const std::string& certificate,
|
||||
device_certificate->set_certificate(certificate);
|
||||
device_certificate->set_wrapped_private_key(wrapped_private_key);
|
||||
|
||||
std::string serialized_string;
|
||||
file.SerializeToString(&serialized_string);
|
||||
std::string serialized_file;
|
||||
file.SerializeToString(&serialized_file);
|
||||
|
||||
// calculate SHA hash
|
||||
std::string hash;
|
||||
if (!Hash(serialized_string, &hash)) {
|
||||
LOGW("DeviceFiles::StoreCertificate: Hash computation failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fill in hashed file data
|
||||
HashedFile hashed_file;
|
||||
hashed_file.set_file(serialized_string);
|
||||
hashed_file.set_hash(hash);
|
||||
|
||||
hashed_file.SerializeToString(&serialized_string);
|
||||
|
||||
return StoreFile(kCertificateFileName, serialized_string);
|
||||
return StoreFile(kCertificateFileName, serialized_file);
|
||||
}
|
||||
|
||||
bool DeviceFiles::RetrieveCertificate(std::string* certificate,
|
||||
@@ -99,29 +89,12 @@ bool DeviceFiles::RetrieveCertificate(std::string* certificate,
|
||||
SecurityLevelPathBackwardCompatibility();
|
||||
}
|
||||
|
||||
std::string serialized_hashed_file;
|
||||
if (!RetrieveFile(kCertificateFileName, &serialized_hashed_file))
|
||||
std::string serialized_file;
|
||||
if (!RetrieveFile(kCertificateFileName, &serialized_file))
|
||||
return false;
|
||||
|
||||
HashedFile hashed_file;
|
||||
if (!hashed_file.ParseFromString(serialized_hashed_file)) {
|
||||
LOGW("DeviceFiles::RetrieveCertificate: Unable to parse hash file");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string hash;
|
||||
if (!Hash(hashed_file.file(), &hash)) {
|
||||
LOGW("DeviceFiles::RetrieveCertificate: Hash computation failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hash.compare(hashed_file.hash())) {
|
||||
LOGW("DeviceFiles::RetrieveCertificate: Hash mismatch");
|
||||
return false;
|
||||
}
|
||||
|
||||
video_widevine_client::sdk::File file;
|
||||
if (!file.ParseFromString(hashed_file.file())) {
|
||||
if (!file.ParseFromString(serialized_file)) {
|
||||
LOGW("DeviceFiles::RetrieveCertificate: Unable to parse file");
|
||||
return false;
|
||||
}
|
||||
@@ -187,25 +160,11 @@ bool DeviceFiles::StoreLicense(const std::string& key_set_id,
|
||||
license->set_renewal(license_renewal);
|
||||
license->set_release_server_url(release_server_url);
|
||||
|
||||
std::string serialized_string;
|
||||
file.SerializeToString(&serialized_string);
|
||||
|
||||
// calculate SHA hash
|
||||
std::string hash;
|
||||
if (!Hash(serialized_string, &hash)) {
|
||||
LOGW("DeviceFiles::StoreLicense: Hash computation failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
// File in hashed file data
|
||||
HashedFile hashed_file;
|
||||
hashed_file.set_file(serialized_string);
|
||||
hashed_file.set_hash(hash);
|
||||
|
||||
hashed_file.SerializeToString(&serialized_string);
|
||||
std::string serialized_file;
|
||||
file.SerializeToString(&serialized_file);
|
||||
|
||||
std::string file_name = key_set_id + kLicenseFileNameExt;
|
||||
return StoreFile(file_name.c_str(), serialized_string);
|
||||
return StoreFile(file_name.c_str(), serialized_file);
|
||||
}
|
||||
|
||||
bool DeviceFiles::RetrieveLicense(const std::string& key_set_id,
|
||||
@@ -220,29 +179,12 @@ bool DeviceFiles::RetrieveLicense(const std::string& key_set_id,
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string serialized_hashed_file;
|
||||
std::string serialized_file;
|
||||
std::string file_name = key_set_id + kLicenseFileNameExt;
|
||||
if (!RetrieveFile(file_name.c_str(), &serialized_hashed_file)) return false;
|
||||
|
||||
HashedFile hashed_file;
|
||||
if (!hashed_file.ParseFromString(serialized_hashed_file)) {
|
||||
LOGW("DeviceFiles::RetrieveLicense: Unable to parse hash file");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string hash;
|
||||
if (!Hash(hashed_file.file(), &hash)) {
|
||||
LOGW("DeviceFiles::RetrieveLicense: Hash computation failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hash.compare(hashed_file.hash())) {
|
||||
LOGW("DeviceFiles::RetrieveLicense: Hash mismatch");
|
||||
return false;
|
||||
}
|
||||
if (!RetrieveFile(file_name.c_str(), &serialized_file)) return false;
|
||||
|
||||
video_widevine_client::sdk::File file;
|
||||
if (!file.ParseFromString(hashed_file.file())) {
|
||||
if (!file.ParseFromString(serialized_file)) {
|
||||
LOGW("DeviceFiles::RetrieveLicense: Unable to parse file");
|
||||
return false;
|
||||
}
|
||||
@@ -352,6 +294,142 @@ bool DeviceFiles::LicenseExists(const std::string& key_set_id) {
|
||||
return file_->Exists(path);
|
||||
}
|
||||
|
||||
bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token,
|
||||
const CdmKeyMessage& key_request,
|
||||
const CdmKeyResponse& key_response) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::StoreUsageInfo: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string serialized_file;
|
||||
video_widevine_client::sdk::File file;
|
||||
if (!RetrieveFile(kUsageInfoFileName, &serialized_file)) {
|
||||
file.set_type(video_widevine_client::sdk::File::USAGE_INFO);
|
||||
file.set_version(video_widevine_client::sdk::File::VERSION_1);
|
||||
} else {
|
||||
if (!file.ParseFromString(serialized_file)) {
|
||||
LOGW("DeviceFiles::StoreUsageInfo: Unable to parse file");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
UsageInfo* usage_info = file.mutable_usage_info();
|
||||
UsageInfo_ProviderSession* provider_session = usage_info->add_sessions();
|
||||
|
||||
provider_session->set_token(provider_session_token.data(),
|
||||
provider_session_token.size());
|
||||
provider_session->set_license_request(key_request.data(),
|
||||
key_request.size());
|
||||
provider_session->set_license(key_response.data(),
|
||||
key_response.size());
|
||||
|
||||
file.SerializeToString(&serialized_file);
|
||||
return StoreFile(kUsageInfoFileName, serialized_file);
|
||||
}
|
||||
|
||||
bool DeviceFiles::DeleteUsageInfo(const std::string& provider_session_token) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::DeleteUsageInfo: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string serialized_file;
|
||||
if (!RetrieveFile(kUsageInfoFileName, &serialized_file)) return false;
|
||||
|
||||
video_widevine_client::sdk::File file;
|
||||
if (!file.ParseFromString(serialized_file)) {
|
||||
LOGW("DeviceFiles::DeleteUsageInfo: Unable to parse file");
|
||||
return false;
|
||||
}
|
||||
|
||||
UsageInfo* updated_info = file.mutable_usage_info();
|
||||
UsageInfo info(*(const_cast<const UsageInfo*>(updated_info)));
|
||||
updated_info->clear_sessions();
|
||||
bool found = false;
|
||||
for (int i = 0; i < info.sessions_size(); ++i) {
|
||||
if (info.sessions(i).token().compare(provider_session_token) == 0) {
|
||||
found = true;
|
||||
} else {
|
||||
updated_info->add_sessions()->set_token(info.sessions(i).token());
|
||||
updated_info->add_sessions()->set_license_request(
|
||||
info.sessions(i).license_request());
|
||||
updated_info->add_sessions()->set_license(info.sessions(i).license());
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
LOGW("DeviceFiles::DeleteUsageInfo: Unable to find provider session "
|
||||
"token: %s", b2a_hex(provider_session_token).c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
file.SerializeToString(&serialized_file);
|
||||
return StoreFile(kUsageInfoFileName, serialized_file);
|
||||
}
|
||||
|
||||
bool DeviceFiles::DeleteUsageInfo() {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::DeleteUsageInfo: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
LOGW("DeviceFiles::DeleteUsageInfo: Unable to get base path");
|
||||
return false;
|
||||
}
|
||||
path.append(kUsageInfoFileName);
|
||||
|
||||
return file_->Remove(path);
|
||||
}
|
||||
|
||||
bool DeviceFiles::RetrieveUsageInfo(std::vector<
|
||||
std::pair<CdmKeyMessage, CdmKeyResponse> >* usage_info) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::RetrieveUsageInfo: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (NULL == usage_info) {
|
||||
LOGW("DeviceFiles::RetrieveUsageInfo: license destination not "
|
||||
"provided");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string serialized_file;
|
||||
if (!RetrieveFile(kUsageInfoFileName, &serialized_file)) {
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
path += kUsageInfoFileName;
|
||||
|
||||
if (!file_->Exists(path) || 0 == file_->FileSize(path)) {
|
||||
usage_info->resize(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
video_widevine_client::sdk::File file;
|
||||
if (!file.ParseFromString(serialized_file)) {
|
||||
LOGW("DeviceFiles::RetrieveUsageInfo: Unable to parse file");
|
||||
return false;
|
||||
}
|
||||
|
||||
usage_info->resize(file.usage_info().sessions_size());
|
||||
for (int i = 0; i < file.usage_info().sessions_size(); ++i) {
|
||||
(*usage_info)[i] =
|
||||
std::make_pair(file.usage_info().sessions(i).license_request(),
|
||||
file.usage_info().sessions(i).license());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::Hash(const std::string& data, std::string* hash) {
|
||||
if (!hash) return false;
|
||||
|
||||
@@ -363,7 +441,8 @@ bool DeviceFiles::Hash(const std::string& data, std::string* hash) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::StoreFile(const char* name, const std::string& data) {
|
||||
bool DeviceFiles::StoreFile(const char* name,
|
||||
const std::string& serialized_file) {
|
||||
if (!file_) {
|
||||
LOGW("DeviceFiles::StoreFile: Invalid file handle");
|
||||
return false;
|
||||
@@ -374,6 +453,21 @@ bool DeviceFiles::StoreFile(const char* name, const std::string& data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// calculate SHA hash
|
||||
std::string hash;
|
||||
if (!Hash(serialized_file, &hash)) {
|
||||
LOGW("DeviceFiles::StoreFile: Hash computation failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fill in hashed file data
|
||||
HashedFile hash_file;
|
||||
hash_file.set_file(serialized_file);
|
||||
hash_file.set_hash(hash);
|
||||
|
||||
std::string serialized_hash_file;
|
||||
hash_file.SerializeToString(&serialized_hash_file);
|
||||
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
LOGW("DeviceFiles::StoreFile: Unable to get base path");
|
||||
@@ -391,19 +485,24 @@ bool DeviceFiles::StoreFile(const char* name, const std::string& data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ssize_t bytes = file_->Write(data.data(), data.size());
|
||||
ssize_t bytes = file_->Write(serialized_hash_file.data(),
|
||||
serialized_hash_file.size());
|
||||
file_->Close();
|
||||
|
||||
if (bytes != static_cast<ssize_t>(data.size())) {
|
||||
LOGW("DeviceFiles::StoreFile: write failed: %d %d", data.size(), bytes);
|
||||
if (bytes != static_cast<ssize_t>(serialized_hash_file.size())) {
|
||||
LOGW("DeviceFiles::StoreFile: write failed: (actual: %d, expected: %d)",
|
||||
bytes,
|
||||
serialized_hash_file.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGV("DeviceFiles::StoreFile: success: %s (%db)", path.c_str(), data.size());
|
||||
LOGV("DeviceFiles::StoreFile: success: %s (%db)",
|
||||
path.c_str(),
|
||||
serialized_hash_file.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::RetrieveFile(const char* name, std::string* data) {
|
||||
bool DeviceFiles::RetrieveFile(const char* name, std::string* serialized_file) {
|
||||
if (!file_) {
|
||||
LOGW("DeviceFiles::RetrieveFile: Invalid file handle");
|
||||
return false;
|
||||
@@ -414,8 +513,8 @@ bool DeviceFiles::RetrieveFile(const char* name, std::string* data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
LOGW("DeviceFiles::RetrieveFile: Unspecified data parameter");
|
||||
if (!serialized_file) {
|
||||
LOGW("DeviceFiles::RetrieveFile: Unspecified serialized_file parameter");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -434,7 +533,7 @@ bool DeviceFiles::RetrieveFile(const char* name, std::string* data) {
|
||||
|
||||
ssize_t bytes = file_->FileSize(path);
|
||||
if (bytes <= 0) {
|
||||
LOGW("DeviceFiles::RetrieveFile: File size invalid: %d", path.c_str());
|
||||
LOGW("DeviceFiles::RetrieveFile: File size invalid: %s", path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -442,17 +541,37 @@ bool DeviceFiles::RetrieveFile(const char* name, std::string* data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
data->resize(bytes);
|
||||
bytes = file_->Read(&(*data)[0], data->size());
|
||||
std::string serialized_hash_file;
|
||||
serialized_hash_file.resize(bytes);
|
||||
bytes = file_->Read(&serialized_hash_file[0], serialized_hash_file.size());
|
||||
file_->Close();
|
||||
|
||||
if (bytes != static_cast<ssize_t>(data->size())) {
|
||||
if (bytes != static_cast<ssize_t>(serialized_hash_file.size())) {
|
||||
LOGW("DeviceFiles::RetrieveFile: read failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGV("DeviceFiles::RetrieveFile: success: %s (%db)", path.c_str(),
|
||||
data->size());
|
||||
serialized_hash_file.size());
|
||||
|
||||
HashedFile hash_file;
|
||||
if (!hash_file.ParseFromString(serialized_hash_file)) {
|
||||
LOGW("DeviceFiles::RetrieveFile: Unable to parse hash file");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string hash;
|
||||
if (!Hash(hash_file.file(), &hash)) {
|
||||
LOGW("DeviceFiles::RetrieveFile: Hash computation failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hash.compare(hash_file.hash())) {
|
||||
LOGW("DeviceFiles::RetrieveFile: Hash mismatch");
|
||||
return false;
|
||||
}
|
||||
|
||||
*serialized_file = hash_file.file();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -528,4 +647,14 @@ std::string DeviceFiles::GetLicenseFileNameExtension() {
|
||||
return kLicenseFileNameExt;
|
||||
}
|
||||
|
||||
std::string DeviceFiles::GetUsageInfoFileName() {
|
||||
return kUsageInfoFileName;
|
||||
}
|
||||
|
||||
void DeviceFiles::SetTestFile(File* file) {
|
||||
if (file_) delete file_;
|
||||
file_ = file;
|
||||
test_file_ = true;
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
Reference in New Issue
Block a user