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:
Rahul Frias
2014-05-17 09:31:41 -07:00
parent d68e1f8307
commit e56e58fbf5
20 changed files with 1573 additions and 261 deletions

View File

@@ -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