[ Merge of http://go/wvgerrit/110923 ] The CDM is responsible for telling OEMCrypto the underlying DRM private key type when loading it into a session. To do this, the CDM must determine and store the key type of a successfully loaded provisioning response. The type of key is available from the DRM certificate proto that is provided in the reponse. This change introduces a class to contain the wrapped key and type together. To store the type, the CDM device files have been updated to include a key type with the DRM certificate and to store from and load to the new class. Unittests have been updated for using the new class where the wrapped key was used before. Test: Linux unit tests Bug: 140813486 Change-Id: I09249afe9c291632fb651ecd00eac697d6939ec7 (cherry picked from commit 6c457402e944079271cef488aa4699f986da6a2e) Merged-In: I09249afe9c291632fb651ecd00eac697d6939ec7
1301 lines
44 KiB
C++
1301 lines
44 KiB
C++
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
|
// source code may only be used and distributed under the Widevine License
|
|
// Agreement.
|
|
|
|
#include "device_files.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include <algorithm>
|
|
#include <string>
|
|
|
|
#include "certificate_provisioning.h"
|
|
#include "file_store.h"
|
|
#include "license_protocol.pb.h"
|
|
#include "log.h"
|
|
#include "privacy_crypto.h"
|
|
#include "properties.h"
|
|
#include "string_conversions.h"
|
|
#include "wv_cdm_constants.h"
|
|
|
|
// Protobuf generated classes.
|
|
using video_widevine_client::sdk::DeviceCertificate;
|
|
using video_widevine_client::sdk::HashedFile;
|
|
using video_widevine_client::sdk::HlsAttributes;
|
|
using video_widevine_client::sdk::HlsAttributes_Method_AES_128;
|
|
using video_widevine_client::sdk::HlsAttributes_Method_SAMPLE_AES;
|
|
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::NameValue;
|
|
using video_widevine_client::sdk::UsageInfo;
|
|
using video_widevine_client::sdk::UsageInfo_ProviderSession;
|
|
using video_widevine_client::sdk::UsageTableInfo;
|
|
using video_widevine_client::sdk::UsageTableInfo_UsageEntryInfo;
|
|
using video_widevine_client::sdk::
|
|
UsageTableInfo_UsageEntryInfo_UsageEntryStorage_LICENSE;
|
|
using video_widevine_client::sdk::
|
|
UsageTableInfo_UsageEntryInfo_UsageEntryStorage_UNKNOWN;
|
|
using video_widevine_client::sdk::
|
|
UsageTableInfo_UsageEntryInfo_UsageEntryStorage_USAGE_INFO;
|
|
|
|
// Stringify turns macro arguments into static C strings.
|
|
// Example: STRINGIFY(this_argument) -> "this_argument"
|
|
#define STRINGIFY(PARAM...) #PARAM
|
|
|
|
#define RETURN_FALSE_IF_NULL(PARAM) \
|
|
if ((PARAM) == nullptr) { \
|
|
LOGE("Output parameter |" STRINGIFY(PARAM) "| not provided"); \
|
|
return false; \
|
|
}
|
|
|
|
#define RETURN_FALSE_WITH_RESULT_IF_NULL(PARAM, result) \
|
|
if ((PARAM) == nullptr) { \
|
|
LOGE("Output parameter |" STRINGIFY(PARAM) "| not provided"); \
|
|
*result = kParameterNull; \
|
|
return false; \
|
|
}
|
|
|
|
#define RETURN_FALSE_IF_UNINITIALIZED() \
|
|
if (!initialized_) { \
|
|
LOGE("Device files is not initialized"); \
|
|
return false; \
|
|
}
|
|
|
|
#define RETURN_FALSE_WITH_RESULT_IF_UNINITIALIZED(result) \
|
|
if (!initialized_) { \
|
|
LOGE("Device files is not initialized"); \
|
|
*result = kObjectNotInitialized; \
|
|
return false; \
|
|
}
|
|
|
|
namespace {
|
|
|
|
const char kAtscCertificateFileName[] = "atsccert.bin";
|
|
const char kCertificateFileName[] = "cert.bin";
|
|
const char kHlsAttributesFileNameExt[] = ".hal";
|
|
const char kUsageInfoFileNamePrefix[] = "usage";
|
|
const char kUsageInfoFileNameExt[] = ".bin";
|
|
const char kLicenseFileNameExt[] = ".lic";
|
|
const char kEmptyFileName[] = "";
|
|
const char kUsageTableFileName[] = "usgtable.bin";
|
|
const char kWildcard[] = "*";
|
|
|
|
} // namespace
|
|
|
|
namespace wvcdm {
|
|
|
|
// static
|
|
std::set<std::string> DeviceFiles::reserved_license_ids_;
|
|
|
|
DeviceFiles::DeviceFiles(FileSystem* file_system)
|
|
: file_system_(file_system),
|
|
security_level_(kSecurityLevelUninitialized),
|
|
initialized_(false) {}
|
|
|
|
DeviceFiles::~DeviceFiles() {}
|
|
|
|
bool DeviceFiles::Init(CdmSecurityLevel security_level) {
|
|
if (!file_system_) {
|
|
LOGE("Invalid FileSystem given");
|
|
return false;
|
|
}
|
|
|
|
std::string path;
|
|
if (!Properties::GetDeviceFilesBasePath(security_level, &path)) {
|
|
LOGE("Unsupported security level: %d", security_level);
|
|
return false;
|
|
}
|
|
security_level_ = security_level;
|
|
initialized_ = true;
|
|
return true;
|
|
}
|
|
|
|
bool DeviceFiles::StoreCertificate(const std::string& certificate,
|
|
const CryptoWrappedKey& private_key) {
|
|
RETURN_FALSE_IF_UNINITIALIZED();
|
|
if (certificate.empty()) {
|
|
LOGE("Missing certificate information");
|
|
return false;
|
|
}
|
|
if (!private_key.IsValid()) {
|
|
LOGE("Private key is invalid");
|
|
return false;
|
|
}
|
|
|
|
// Fill in file information
|
|
video_widevine_client::sdk::File file;
|
|
|
|
file.set_type(video_widevine_client::sdk::File::DEVICE_CERTIFICATE);
|
|
file.set_version(video_widevine_client::sdk::File::VERSION_1);
|
|
|
|
DeviceCertificate* device_certificate = file.mutable_device_certificate();
|
|
device_certificate->set_certificate(certificate);
|
|
device_certificate->set_wrapped_private_key(private_key.key());
|
|
switch (private_key.type()) {
|
|
case CryptoWrappedKey::kRsa:
|
|
device_certificate->set_key_type(DeviceCertificate::RSA);
|
|
break;
|
|
case CryptoWrappedKey::kEcc:
|
|
device_certificate->set_key_type(DeviceCertificate::ECC);
|
|
break;
|
|
case CryptoWrappedKey::kUninitialized: // Suppress compiler warnings.
|
|
default:
|
|
LOGE("Unexpected key type");
|
|
return false;
|
|
}
|
|
|
|
std::string serialized_file;
|
|
file.SerializeToString(&serialized_file);
|
|
|
|
return StoreFileWithHash(GetCertificateFileName(false), serialized_file) ==
|
|
kNoError;
|
|
}
|
|
|
|
bool DeviceFiles::RetrieveCertificate(bool atsc_mode_enabled,
|
|
std::string* certificate,
|
|
CryptoWrappedKey* private_key,
|
|
std::string* serial_number,
|
|
uint32_t* system_id) {
|
|
RETURN_FALSE_IF_UNINITIALIZED();
|
|
RETURN_FALSE_IF_NULL(certificate);
|
|
RETURN_FALSE_IF_NULL(private_key);
|
|
|
|
if (!HasCertificate(atsc_mode_enabled)) {
|
|
return false;
|
|
}
|
|
|
|
video_widevine_client::sdk::File file;
|
|
if (RetrieveHashedFile(GetCertificateFileName(atsc_mode_enabled), &file) !=
|
|
kNoError) {
|
|
LOGW("Unable to retrieve certificate file");
|
|
return false;
|
|
}
|
|
|
|
if (file.type() != video_widevine_client::sdk::File::DEVICE_CERTIFICATE) {
|
|
LOGE("Certificate file is of incorrect file type: type = %d",
|
|
static_cast<int>(file.type()));
|
|
return false;
|
|
}
|
|
|
|
if (file.version() != video_widevine_client::sdk::File::VERSION_1) {
|
|
LOGE("Certificate file is of incorrect file version: version = %d",
|
|
static_cast<int>(file.version()));
|
|
return false;
|
|
}
|
|
|
|
if (!file.has_device_certificate()) {
|
|
LOGE("Certificate not present");
|
|
return false;
|
|
}
|
|
|
|
DeviceCertificate device_certificate = file.device_certificate();
|
|
*certificate = device_certificate.certificate();
|
|
private_key->Clear();
|
|
private_key->set_key(device_certificate.wrapped_private_key());
|
|
if (device_certificate.has_key_type()) {
|
|
const DeviceCertificate::PrivateKeyType key_type =
|
|
device_certificate.key_type();
|
|
switch (key_type) {
|
|
case DeviceCertificate::RSA:
|
|
private_key->set_type(CryptoWrappedKey::kRsa);
|
|
break;
|
|
case DeviceCertificate::ECC:
|
|
private_key->set_type(CryptoWrappedKey::kEcc);
|
|
break;
|
|
default:
|
|
LOGW("Unknown DRM key type, defaulting to RSA: type = %d", key_type);
|
|
private_key->set_type(CryptoWrappedKey::kRsa);
|
|
break;
|
|
}
|
|
} else {
|
|
// Possible that device certificate is from V15, in this case, the
|
|
// only supported key of at that time was RSA.
|
|
LOGD("No key type info, assuming RSA");
|
|
private_key->set_type(CryptoWrappedKey::kRsa);
|
|
}
|
|
|
|
return CertificateProvisioning::ExtractDeviceInfo(
|
|
device_certificate.certificate(), serial_number, system_id);
|
|
}
|
|
|
|
bool DeviceFiles::HasCertificate(bool atsc_mode_enabled) {
|
|
RETURN_FALSE_IF_UNINITIALIZED();
|
|
|
|
return FileExists(GetCertificateFileName(atsc_mode_enabled));
|
|
}
|
|
|
|
bool DeviceFiles::RemoveCertificate() {
|
|
RETURN_FALSE_IF_UNINITIALIZED()
|
|
|
|
return RemoveFile(GetCertificateFileName(false));
|
|
}
|
|
|
|
bool DeviceFiles::StoreLicense(const CdmLicenseData& license_data,
|
|
ResponseType* result) {
|
|
RETURN_FALSE_IF_NULL(result);
|
|
|
|
*result = kNoError;
|
|
RETURN_FALSE_WITH_RESULT_IF_UNINITIALIZED(result);
|
|
|
|
// Fill in file information
|
|
video_widevine_client::sdk::File file;
|
|
|
|
file.set_type(video_widevine_client::sdk::File::LICENSE);
|
|
file.set_version(video_widevine_client::sdk::File::VERSION_1);
|
|
|
|
License* license = file.mutable_license();
|
|
switch (license_data.state) {
|
|
case kLicenseStateActive:
|
|
license->set_state(License_LicenseState_ACTIVE);
|
|
break;
|
|
case kLicenseStateReleasing:
|
|
license->set_state(License_LicenseState_RELEASING);
|
|
break;
|
|
default:
|
|
LOGE("Unknown license state: %d", static_cast<int>(license_data.state));
|
|
*result = kUnknownLicenseState;
|
|
return false;
|
|
break;
|
|
}
|
|
license->set_pssh_data(license_data.pssh_data);
|
|
license->set_license_request(license_data.license_request);
|
|
license->set_license(license_data.license);
|
|
license->set_renewal_request(license_data.license_renewal_request);
|
|
license->set_renewal(license_data.license_renewal);
|
|
license->set_release_server_url(license_data.release_server_url);
|
|
license->set_playback_start_time(license_data.playback_start_time);
|
|
license->set_last_playback_time(license_data.last_playback_time);
|
|
license->set_grace_period_end_time(license_data.grace_period_end_time);
|
|
for (CdmAppParameterMap::const_iterator iter =
|
|
license_data.app_parameters.cbegin();
|
|
iter != license_data.app_parameters.cend(); ++iter) {
|
|
NameValue* app_params = license->add_app_parameters();
|
|
app_params->set_name(iter->first);
|
|
app_params->set_value(iter->second);
|
|
}
|
|
license->set_usage_entry(license_data.usage_entry);
|
|
license->set_usage_entry_number(license_data.usage_entry_number);
|
|
|
|
std::string serialized_file;
|
|
file.SerializeToString(&serialized_file);
|
|
|
|
reserved_license_ids_.erase(license_data.key_set_id);
|
|
*result = StoreFileWithHash(license_data.key_set_id + kLicenseFileNameExt,
|
|
serialized_file);
|
|
return *result == kNoError;
|
|
}
|
|
|
|
bool DeviceFiles::RetrieveLicense(const std::string& key_set_id,
|
|
CdmLicenseData* license_data,
|
|
ResponseType* result) {
|
|
// This check must be made first as the RETURN_FALSE_IF_NULL() macro
|
|
// will assign kParameterNull to |result|.
|
|
if (result == nullptr) {
|
|
LOGE("Output parameter |result| not provided");
|
|
return false;
|
|
}
|
|
RETURN_FALSE_WITH_RESULT_IF_UNINITIALIZED(result);
|
|
RETURN_FALSE_WITH_RESULT_IF_NULL(license_data, result);
|
|
|
|
video_widevine_client::sdk::File file;
|
|
*result = RetrieveHashedFile(key_set_id + kLicenseFileNameExt, &file);
|
|
if (*result != kNoError) {
|
|
LOGE("Unable to retrieve key set license file: result = %d",
|
|
static_cast<int>(*result));
|
|
return false;
|
|
}
|
|
|
|
if (file.type() != video_widevine_client::sdk::File::LICENSE) {
|
|
LOGE("Incorrect file type: type = %d, expected_type = %d",
|
|
static_cast<int>(file.type()),
|
|
static_cast<int>(video_widevine_client::sdk::File::LICENSE));
|
|
*result = kIncorrectFileType;
|
|
return false;
|
|
}
|
|
|
|
if (file.version() != video_widevine_client::sdk::File::VERSION_1) {
|
|
LOGE("Incorrect file version: version = %d, expected_version = %d",
|
|
static_cast<int>(file.version()),
|
|
static_cast<int>(video_widevine_client::sdk::File::VERSION_1));
|
|
*result = kIncorrectFileVersion;
|
|
return false;
|
|
}
|
|
|
|
if (!file.has_license()) {
|
|
LOGE("License not present");
|
|
*result = kLicenseNotPresent;
|
|
return false;
|
|
}
|
|
|
|
license_data->key_set_id = key_set_id;
|
|
|
|
License license = file.license();
|
|
|
|
switch (license.state()) {
|
|
case License_LicenseState_ACTIVE:
|
|
license_data->state = kLicenseStateActive;
|
|
break;
|
|
case License_LicenseState_RELEASING:
|
|
license_data->state = kLicenseStateReleasing;
|
|
break;
|
|
default:
|
|
LOGW("Unrecognized license state: %d", static_cast<int>(license.state()));
|
|
license_data->state = kLicenseStateUnknown;
|
|
break;
|
|
}
|
|
license_data->pssh_data = license.pssh_data();
|
|
license_data->license_request = license.license_request();
|
|
license_data->license = license.license();
|
|
license_data->license_renewal_request = license.renewal_request();
|
|
license_data->license_renewal = license.renewal();
|
|
license_data->release_server_url = license.release_server_url();
|
|
license_data->playback_start_time = license.playback_start_time();
|
|
license_data->last_playback_time = license.last_playback_time();
|
|
license_data->grace_period_end_time = license.grace_period_end_time();
|
|
for (int i = 0; i < license.app_parameters_size(); ++i) {
|
|
license_data->app_parameters[license.app_parameters(i).name()] =
|
|
license.app_parameters(i).value();
|
|
}
|
|
license_data->usage_entry = license.usage_entry();
|
|
license_data->usage_entry_number = license.usage_entry_number();
|
|
return true;
|
|
}
|
|
|
|
bool DeviceFiles::DeleteLicense(const std::string& key_set_id) {
|
|
RETURN_FALSE_IF_UNINITIALIZED();
|
|
return RemoveFile(key_set_id + kLicenseFileNameExt);
|
|
}
|
|
|
|
bool DeviceFiles::ListLicenses(std::vector<std::string>* key_set_ids) {
|
|
RETURN_FALSE_IF_UNINITIALIZED();
|
|
RETURN_FALSE_IF_NULL(key_set_ids);
|
|
|
|
// Get list of filenames
|
|
std::vector<std::string> filenames;
|
|
if (!ListFiles(&filenames)) {
|
|
return false;
|
|
}
|
|
|
|
// Scan list of returned filenames, remove extension, and return
|
|
// as a list of key_set_ids.
|
|
key_set_ids->clear();
|
|
for (size_t i = 0; i < filenames.size(); i++) {
|
|
std::string* name = &filenames[i];
|
|
std::size_t pos = name->find(kLicenseFileNameExt);
|
|
if (pos == std::string::npos) {
|
|
// Skip this file - extension does not match
|
|
continue;
|
|
}
|
|
// Store filename (minus extension). This should be a key set ID.
|
|
key_set_ids->push_back(name->substr(0, pos));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool DeviceFiles::DeleteAllLicenses() {
|
|
RETURN_FALSE_IF_UNINITIALIZED();
|
|
return RemoveFile(std::string(kWildcard) + kLicenseFileNameExt);
|
|
}
|
|
|
|
bool DeviceFiles::DeleteAllFiles() {
|
|
RETURN_FALSE_IF_UNINITIALIZED();
|
|
// We pass an empty string to RemoveFile to delete the device files base
|
|
// directory itself.
|
|
return RemoveFile(kEmptyFileName);
|
|
}
|
|
|
|
bool DeviceFiles::LicenseExists(const std::string& key_set_id) {
|
|
RETURN_FALSE_IF_UNINITIALIZED();
|
|
return reserved_license_ids_.count(key_set_id) ||
|
|
FileExists(key_set_id + kLicenseFileNameExt);
|
|
}
|
|
|
|
bool DeviceFiles::ReserveLicenseId(const std::string& key_set_id) {
|
|
RETURN_FALSE_IF_UNINITIALIZED();
|
|
reserved_license_ids_.insert(key_set_id);
|
|
return true;
|
|
}
|
|
|
|
bool DeviceFiles::UnreserveLicenseId(const std::string& key_set_id) {
|
|
RETURN_FALSE_IF_UNINITIALIZED();
|
|
reserved_license_ids_.erase(key_set_id);
|
|
return true;
|
|
}
|
|
|
|
bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token,
|
|
const CdmKeyMessage& key_request,
|
|
const CdmKeyResponse& key_response,
|
|
const std::string& usage_info_file_name,
|
|
const std::string& key_set_id,
|
|
const std::string& usage_entry,
|
|
uint32_t usage_entry_number) {
|
|
RETURN_FALSE_IF_UNINITIALIZED();
|
|
video_widevine_client::sdk::File file;
|
|
if (!FileExists(usage_info_file_name)) {
|
|
file.set_type(video_widevine_client::sdk::File::USAGE_INFO);
|
|
file.set_version(video_widevine_client::sdk::File::VERSION_1);
|
|
} else {
|
|
if (RetrieveHashedFile(usage_info_file_name, &file) != kNoError) {
|
|
LOGE("Unable to retrieve usage info 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());
|
|
provider_session->set_key_set_id(key_set_id.data(), key_set_id.size());
|
|
provider_session->set_usage_entry(usage_entry);
|
|
provider_session->set_usage_entry_number(usage_entry_number);
|
|
|
|
std::string serialized_file;
|
|
file.SerializeToString(&serialized_file);
|
|
return StoreFileWithHash(usage_info_file_name, serialized_file) == kNoError;
|
|
}
|
|
|
|
bool DeviceFiles::ListUsageIds(
|
|
const std::string& app_id, std::vector<std::string>* ksids,
|
|
std::vector<std::string>* provider_session_tokens) {
|
|
RETURN_FALSE_IF_UNINITIALIZED();
|
|
|
|
if (ksids == nullptr && provider_session_tokens == nullptr) {
|
|
LOGE(
|
|
"Both output parameters |ksids| and |provider_session_tokens| are "
|
|
"not provided");
|
|
return false;
|
|
}
|
|
|
|
// Empty or non-existent file == no usage records.
|
|
std::string file_name = GetUsageInfoFileName(app_id);
|
|
if (!FileExists(file_name) || GetFileSize(file_name) == 0) {
|
|
if (ksids != nullptr) ksids->clear();
|
|
if (provider_session_tokens != nullptr) provider_session_tokens->clear();
|
|
return true;
|
|
}
|
|
|
|
video_widevine_client::sdk::File file;
|
|
if (RetrieveHashedFile(file_name, &file) != kNoError) {
|
|
LOGE("Unable to retrieve usage info file");
|
|
return false;
|
|
}
|
|
|
|
if (ksids != nullptr) ksids->clear();
|
|
if (provider_session_tokens != nullptr) provider_session_tokens->clear();
|
|
|
|
size_t num_records = file.usage_info().sessions_size();
|
|
for (size_t i = 0; i < num_records; ++i) {
|
|
if ((ksids != nullptr) &&
|
|
!file.usage_info().sessions(i).key_set_id().empty()) {
|
|
ksids->push_back(file.usage_info().sessions(i).key_set_id());
|
|
}
|
|
if ((provider_session_tokens != nullptr) &&
|
|
!file.usage_info().sessions(i).token().empty()) {
|
|
provider_session_tokens->push_back(file.usage_info().sessions(i).token());
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool DeviceFiles::GetProviderSessionToken(const std::string& app_id,
|
|
const std::string& key_set_id,
|
|
std::string* provider_session_token) {
|
|
RETURN_FALSE_IF_UNINITIALIZED();
|
|
RETURN_FALSE_IF_NULL(provider_session_token);
|
|
|
|
std::string file_name = GetUsageInfoFileName(app_id);
|
|
if (!FileExists(file_name) || GetFileSize(file_name) == 0) {
|
|
LOGE("Usage info file does not exists or is an empty file");
|
|
return false;
|
|
}
|
|
|
|
video_widevine_client::sdk::File file;
|
|
if (RetrieveHashedFile(file_name, &file) != kNoError) {
|
|
LOGE("Unable to retrieve usage info file");
|
|
return false;
|
|
}
|
|
|
|
size_t num_records = file.usage_info().sessions_size();
|
|
for (size_t i = 0; i < num_records; ++i) {
|
|
if (file.usage_info().sessions(i).key_set_id() == key_set_id) {
|
|
*provider_session_token = file.usage_info().sessions(i).token();
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool DeviceFiles::DeleteUsageInfo(const std::string& usage_info_file_name,
|
|
const std::string& provider_session_token) {
|
|
RETURN_FALSE_IF_UNINITIALIZED();
|
|
video_widevine_client::sdk::File file;
|
|
if (RetrieveHashedFile(usage_info_file_name, &file) != kNoError) {
|
|
LOGE("Unable to retrieve usage info file");
|
|
return false;
|
|
}
|
|
|
|
UsageInfo* usage_info = file.mutable_usage_info();
|
|
int index = 0;
|
|
bool found = false;
|
|
for (; index < usage_info->sessions_size(); ++index) {
|
|
if (usage_info->sessions(index).token() == provider_session_token) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
LOGE("Unable to find provider session token: pst = %s",
|
|
b2a_hex(provider_session_token).c_str());
|
|
return false;
|
|
}
|
|
|
|
google::protobuf::RepeatedPtrField<UsageInfo_ProviderSession>* sessions =
|
|
usage_info->mutable_sessions();
|
|
if (index < usage_info->sessions_size() - 1) {
|
|
sessions->SwapElements(index, usage_info->sessions_size() - 1);
|
|
}
|
|
sessions->RemoveLast();
|
|
|
|
std::string serialized_file;
|
|
file.SerializeToString(&serialized_file);
|
|
return StoreFileWithHash(usage_info_file_name, serialized_file) == kNoError;
|
|
}
|
|
|
|
bool DeviceFiles::DeleteAllUsageInfoForApp(
|
|
const std::string& usage_info_file_name,
|
|
std::vector<std::string>* provider_session_tokens) {
|
|
RETURN_FALSE_IF_UNINITIALIZED();
|
|
RETURN_FALSE_IF_NULL(provider_session_tokens);
|
|
|
|
provider_session_tokens->clear();
|
|
|
|
if (!FileExists(usage_info_file_name)) return true;
|
|
|
|
video_widevine_client::sdk::File file;
|
|
if (RetrieveHashedFile(usage_info_file_name, &file) == kNoError) {
|
|
for (int i = 0; i < file.usage_info().sessions_size(); ++i) {
|
|
provider_session_tokens->push_back(file.usage_info().sessions(i).token());
|
|
}
|
|
} else {
|
|
LOGW("Unable to retrieve usage info file");
|
|
}
|
|
return RemoveFile(usage_info_file_name);
|
|
}
|
|
|
|
bool DeviceFiles::DeleteMultipleUsageInfoByKeySetIds(
|
|
const std::string& usage_info_file_name,
|
|
const std::vector<std::string>& key_set_ids) {
|
|
if (!FileExists(usage_info_file_name)) return false;
|
|
if (key_set_ids.empty()) {
|
|
LOGW("No key set IDs provided");
|
|
return true;
|
|
}
|
|
|
|
video_widevine_client::sdk::File file;
|
|
if (RetrieveHashedFile(usage_info_file_name, &file) != kNoError) {
|
|
LOGW("Unable to retrieve usage info file");
|
|
return false;
|
|
}
|
|
|
|
const auto is_deletable =
|
|
[&key_set_ids](
|
|
const video_widevine_client::sdk::UsageInfo::ProviderSession& session)
|
|
-> bool {
|
|
return std::find(key_set_ids.cbegin(), key_set_ids.cend(),
|
|
session.key_set_id()) != key_set_ids.cend();
|
|
};
|
|
|
|
auto sessions = file.mutable_usage_info()->mutable_sessions();
|
|
const int initial_size = sessions->size();
|
|
sessions->erase(
|
|
std::remove_if(sessions->begin(), sessions->end(), is_deletable),
|
|
sessions->end());
|
|
|
|
if (sessions->size() == initial_size) {
|
|
// Nothing deleted.
|
|
return true;
|
|
}
|
|
|
|
if (sessions->size() > 0) {
|
|
std::string serialized_file;
|
|
file.SerializeToString(&serialized_file);
|
|
return StoreFileWithHash(usage_info_file_name, serialized_file) == kNoError;
|
|
}
|
|
|
|
return RemoveFile(usage_info_file_name);
|
|
}
|
|
|
|
bool DeviceFiles::DeleteAllUsageInfo() {
|
|
RETURN_FALSE_IF_UNINITIALIZED();
|
|
return RemoveFile(kUsageInfoFileNamePrefix + std::string(kWildcard) +
|
|
kUsageInfoFileNameExt);
|
|
}
|
|
|
|
bool DeviceFiles::RetrieveUsageInfo(
|
|
const std::string& usage_info_file_name,
|
|
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> >* usage_info) {
|
|
RETURN_FALSE_IF_UNINITIALIZED();
|
|
RETURN_FALSE_IF_NULL(usage_info);
|
|
|
|
if (!FileExists(usage_info_file_name) ||
|
|
GetFileSize(usage_info_file_name) == 0) {
|
|
usage_info->resize(0);
|
|
return true;
|
|
}
|
|
|
|
video_widevine_client::sdk::File file;
|
|
if (RetrieveHashedFile(usage_info_file_name, &file) != kNoError) {
|
|
LOGE("Unable to retrieve usage info 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::RetrieveUsageInfo(const std::string& usage_info_file_name,
|
|
const std::string& provider_session_token,
|
|
CdmKeyMessage* license_request,
|
|
CdmKeyResponse* license,
|
|
std::string* usage_entry,
|
|
uint32_t* usage_entry_number) {
|
|
RETURN_FALSE_IF_UNINITIALIZED();
|
|
|
|
video_widevine_client::sdk::File file;
|
|
if (RetrieveHashedFile(usage_info_file_name, &file) != kNoError) {
|
|
LOGE("Unable to retrieve usage info file");
|
|
return false;
|
|
}
|
|
|
|
int index = 0;
|
|
for (; index < file.usage_info().sessions_size(); ++index) {
|
|
if (file.usage_info().sessions(index).token() == provider_session_token) {
|
|
*license_request = file.usage_info().sessions(index).license_request();
|
|
*license = file.usage_info().sessions(index).license();
|
|
*usage_entry = file.usage_info().sessions(index).usage_entry();
|
|
*usage_entry_number =
|
|
file.usage_info().sessions(index).usage_entry_number();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool DeviceFiles::RetrieveUsageInfoByKeySetId(
|
|
const std::string& usage_info_file_name, const std::string& key_set_id,
|
|
std::string* provider_session_token, CdmKeyMessage* license_request,
|
|
CdmKeyResponse* license, std::string* usage_entry,
|
|
uint32_t* usage_entry_number) {
|
|
RETURN_FALSE_IF_UNINITIALIZED();
|
|
|
|
video_widevine_client::sdk::File file;
|
|
if (RetrieveHashedFile(usage_info_file_name, &file) != kNoError) {
|
|
LOGE("Unable to retrieve usage info file");
|
|
return false;
|
|
}
|
|
|
|
int index = 0;
|
|
for (; index < file.usage_info().sessions_size(); ++index) {
|
|
if (file.usage_info().sessions(index).key_set_id() == key_set_id) {
|
|
*provider_session_token = file.usage_info().sessions(index).token();
|
|
*license_request = file.usage_info().sessions(index).license_request();
|
|
*license = file.usage_info().sessions(index).license();
|
|
*usage_entry = file.usage_info().sessions(index).usage_entry();
|
|
*usage_entry_number =
|
|
file.usage_info().sessions(index).usage_entry_number();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool DeviceFiles::StoreUsageInfo(const std::string& usage_info_file_name,
|
|
const std::vector<CdmUsageData>& usage_data) {
|
|
RETURN_FALSE_IF_UNINITIALIZED();
|
|
|
|
video_widevine_client::sdk::File file;
|
|
file.set_type(video_widevine_client::sdk::File::USAGE_INFO);
|
|
file.set_version(video_widevine_client::sdk::File::VERSION_1);
|
|
|
|
UsageInfo* usage_info = file.mutable_usage_info();
|
|
for (size_t i = 0; i < usage_data.size(); ++i) {
|
|
UsageInfo_ProviderSession* provider_session = usage_info->add_sessions();
|
|
|
|
provider_session->set_token(usage_data[i].provider_session_token.data(),
|
|
usage_data[i].provider_session_token.size());
|
|
provider_session->set_license_request(usage_data[i].license_request.data(),
|
|
usage_data[i].license_request.size());
|
|
provider_session->set_license(usage_data[i].license.data(),
|
|
usage_data[i].license.size());
|
|
provider_session->set_key_set_id(usage_data[i].key_set_id.data(),
|
|
usage_data[i].key_set_id.size());
|
|
provider_session->set_usage_entry(usage_data[i].usage_entry);
|
|
provider_session->set_usage_entry_number(usage_data[i].usage_entry_number);
|
|
}
|
|
|
|
std::string serialized_file;
|
|
file.SerializeToString(&serialized_file);
|
|
return StoreFileWithHash(usage_info_file_name, serialized_file) == kNoError;
|
|
}
|
|
|
|
bool DeviceFiles::UpdateUsageInfo(const std::string& usage_info_file_name,
|
|
const std::string& provider_session_token,
|
|
const CdmUsageData& usage_data) {
|
|
RETURN_FALSE_IF_UNINITIALIZED();
|
|
|
|
video_widevine_client::sdk::File file;
|
|
if (!FileExists(usage_info_file_name)) {
|
|
LOGE("Usage info file does not exist");
|
|
return false;
|
|
}
|
|
|
|
if (RetrieveHashedFile(usage_info_file_name, &file) != kNoError) {
|
|
LOGE("Unable to retrieve usage info file");
|
|
return false;
|
|
}
|
|
|
|
int index = 0;
|
|
for (; index < file.usage_info().sessions_size(); ++index) {
|
|
if (file.usage_info().sessions(index).token() == provider_session_token) {
|
|
UsageInfo* usage_info = file.mutable_usage_info();
|
|
UsageInfo_ProviderSession* provider_session =
|
|
usage_info->mutable_sessions(index);
|
|
provider_session->set_license_request(usage_data.license_request);
|
|
provider_session->set_license(usage_data.license);
|
|
provider_session->set_key_set_id(usage_data.key_set_id);
|
|
provider_session->set_usage_entry(usage_data.usage_entry);
|
|
provider_session->set_usage_entry_number(usage_data.usage_entry_number);
|
|
|
|
std::string serialized_file;
|
|
file.SerializeToString(&serialized_file);
|
|
return StoreFileWithHash(usage_info_file_name, serialized_file) ==
|
|
kNoError;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool DeviceFiles::RetrieveUsageInfo(const std::string& usage_info_file_name,
|
|
std::vector<CdmUsageData>* usage_data) {
|
|
RETURN_FALSE_IF_UNINITIALIZED();
|
|
RETURN_FALSE_IF_NULL(usage_data);
|
|
|
|
if (!FileExists(usage_info_file_name) ||
|
|
GetFileSize(usage_info_file_name) == 0) {
|
|
usage_data->resize(0);
|
|
return true;
|
|
}
|
|
|
|
video_widevine_client::sdk::File file;
|
|
if (RetrieveHashedFile(usage_info_file_name, &file) != kNoError) {
|
|
LOGE("Unable to retrieve usage info file");
|
|
return false;
|
|
}
|
|
|
|
usage_data->resize(file.usage_info().sessions_size());
|
|
for (int i = 0; i < file.usage_info().sessions_size(); ++i) {
|
|
(*usage_data)[i].provider_session_token =
|
|
file.usage_info().sessions(i).token();
|
|
(*usage_data)[i].license_request =
|
|
file.usage_info().sessions(i).license_request();
|
|
(*usage_data)[i].license = file.usage_info().sessions(i).license();
|
|
(*usage_data)[i].key_set_id = file.usage_info().sessions(i).key_set_id();
|
|
(*usage_data)[i].usage_entry = file.usage_info().sessions(i).usage_entry();
|
|
(*usage_data)[i].usage_entry_number =
|
|
file.usage_info().sessions(i).usage_entry_number();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool DeviceFiles::RetrieveUsageInfo(const std::string& usage_info_file_name,
|
|
const std::string& provider_session_token,
|
|
CdmUsageData* usage_data) {
|
|
RETURN_FALSE_IF_UNINITIALIZED();
|
|
RETURN_FALSE_IF_NULL(usage_data);
|
|
|
|
video_widevine_client::sdk::File file;
|
|
if (RetrieveHashedFile(usage_info_file_name, &file) != kNoError) {
|
|
LOGE("Unable to retrieve usage info file");
|
|
return false;
|
|
}
|
|
|
|
int index = 0;
|
|
for (; index < file.usage_info().sessions_size(); ++index) {
|
|
if (file.usage_info().sessions(index).token() == provider_session_token) {
|
|
usage_data->provider_session_token =
|
|
file.usage_info().sessions(index).token();
|
|
usage_data->license_request =
|
|
file.usage_info().sessions(index).license_request();
|
|
usage_data->license = file.usage_info().sessions(index).license();
|
|
usage_data->key_set_id = file.usage_info().sessions(index).key_set_id();
|
|
usage_data->usage_entry = file.usage_info().sessions(index).usage_entry();
|
|
usage_data->usage_entry_number =
|
|
file.usage_info().sessions(index).usage_entry_number();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool DeviceFiles::ListUsageInfoFiles(
|
|
std::vector<std::string>* usage_info_file_names) {
|
|
RETURN_FALSE_IF_UNINITIALIZED();
|
|
RETURN_FALSE_IF_NULL(usage_info_file_names);
|
|
|
|
// Get list of filenames
|
|
std::vector<std::string> filenames;
|
|
if (!ListFiles(&filenames)) {
|
|
return false;
|
|
}
|
|
|
|
// Scan list of all filenames and return only usage info filenames
|
|
usage_info_file_names->clear();
|
|
for (size_t i = 0; i < filenames.size(); i++) {
|
|
std::string* name = &filenames[i];
|
|
std::size_t pos_prefix = name->find(kUsageInfoFileNamePrefix);
|
|
std::size_t pos_suffix = name->find(kUsageInfoFileNameExt);
|
|
if (pos_prefix == std::string::npos || pos_suffix == std::string::npos) {
|
|
// Skip this file - extension does not match
|
|
continue;
|
|
}
|
|
|
|
usage_info_file_names->push_back(*name);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool DeviceFiles::StoreHlsAttributes(
|
|
const std::string& key_set_id, const CdmHlsMethod method,
|
|
const std::vector<uint8_t>& media_segment_iv) {
|
|
RETURN_FALSE_IF_UNINITIALIZED();
|
|
|
|
// Fill in file information
|
|
video_widevine_client::sdk::File file;
|
|
|
|
file.set_type(video_widevine_client::sdk::File::HLS_ATTRIBUTES);
|
|
file.set_version(video_widevine_client::sdk::File::VERSION_1);
|
|
|
|
HlsAttributes* hls_attributes = file.mutable_hls_attributes();
|
|
switch (method) {
|
|
case kHlsMethodAes128:
|
|
hls_attributes->set_method(HlsAttributes_Method_AES_128);
|
|
break;
|
|
case kHlsMethodSampleAes:
|
|
hls_attributes->set_method(HlsAttributes_Method_SAMPLE_AES);
|
|
break;
|
|
case kHlsMethodNone:
|
|
default:
|
|
LOGE("Unknown HLS method: %d", method);
|
|
return false;
|
|
break;
|
|
}
|
|
hls_attributes->set_media_segment_iv(&media_segment_iv[0],
|
|
media_segment_iv.size());
|
|
|
|
std::string serialized_file;
|
|
file.SerializeToString(&serialized_file);
|
|
|
|
return StoreFileWithHash(key_set_id + kHlsAttributesFileNameExt,
|
|
serialized_file) == kNoError;
|
|
}
|
|
|
|
bool DeviceFiles::RetrieveHlsAttributes(
|
|
const std::string& key_set_id, CdmHlsMethod* method,
|
|
std::vector<uint8_t>* media_segment_iv) {
|
|
RETURN_FALSE_IF_UNINITIALIZED();
|
|
|
|
video_widevine_client::sdk::File file;
|
|
if (RetrieveHashedFile(key_set_id + kHlsAttributesFileNameExt, &file) !=
|
|
kNoError) {
|
|
LOGE("Unable to retrieve key set HLS attributes file");
|
|
return false;
|
|
}
|
|
|
|
if (file.type() != video_widevine_client::sdk::File::HLS_ATTRIBUTES) {
|
|
LOGE("Incorrect file type: type = %d", static_cast<int>(file.type()));
|
|
return false;
|
|
}
|
|
|
|
if (file.version() != video_widevine_client::sdk::File::VERSION_1) {
|
|
LOGE("Incorrect file version: version = %d",
|
|
static_cast<int>(file.version()));
|
|
return false;
|
|
}
|
|
|
|
if (!file.has_hls_attributes()) {
|
|
LOGE("HLS attributes not present");
|
|
return false;
|
|
}
|
|
|
|
HlsAttributes attributes = file.hls_attributes();
|
|
|
|
switch (attributes.method()) {
|
|
case HlsAttributes_Method_AES_128:
|
|
*method = kHlsMethodAes128;
|
|
break;
|
|
case HlsAttributes_Method_SAMPLE_AES:
|
|
*method = kHlsMethodSampleAes;
|
|
break;
|
|
default:
|
|
LOGW("Unrecognized HLS method: %d",
|
|
static_cast<int>(attributes.method()));
|
|
*method = kHlsMethodNone;
|
|
break;
|
|
}
|
|
media_segment_iv->assign(attributes.media_segment_iv().begin(),
|
|
attributes.media_segment_iv().end());
|
|
return true;
|
|
}
|
|
|
|
bool DeviceFiles::DeleteHlsAttributes(const std::string& key_set_id) {
|
|
RETURN_FALSE_IF_UNINITIALIZED();
|
|
return RemoveFile(key_set_id + kHlsAttributesFileNameExt);
|
|
}
|
|
|
|
bool DeviceFiles::StoreUsageTableInfo(
|
|
const CdmUsageTableHeader& usage_table_header,
|
|
const std::vector<CdmUsageEntryInfo>& usage_entry_info) {
|
|
RETURN_FALSE_IF_UNINITIALIZED();
|
|
|
|
// Fill in file information
|
|
video_widevine_client::sdk::File file;
|
|
|
|
file.set_type(video_widevine_client::sdk::File::USAGE_TABLE_INFO);
|
|
file.set_version(video_widevine_client::sdk::File::VERSION_1);
|
|
|
|
UsageTableInfo* usage_table_info = file.mutable_usage_table_info();
|
|
usage_table_info->set_usage_table_header(usage_table_header);
|
|
for (size_t i = 0; i < usage_entry_info.size(); ++i) {
|
|
UsageTableInfo_UsageEntryInfo* info =
|
|
usage_table_info->add_usage_entry_info();
|
|
info->set_key_set_id(usage_entry_info[i].key_set_id);
|
|
switch (usage_entry_info[i].storage_type) {
|
|
case kStorageLicense:
|
|
info->set_storage(
|
|
UsageTableInfo_UsageEntryInfo_UsageEntryStorage_LICENSE);
|
|
info->set_last_use_time(usage_entry_info[i].last_use_time);
|
|
info->set_offline_license_expiry_time(
|
|
usage_entry_info[i].offline_license_expiry_time);
|
|
break;
|
|
case kStorageUsageInfo:
|
|
info->set_storage(
|
|
UsageTableInfo_UsageEntryInfo_UsageEntryStorage_USAGE_INFO);
|
|
info->set_usage_info_file_name(
|
|
usage_entry_info[i].usage_info_file_name);
|
|
info->set_last_use_time(usage_entry_info[i].last_use_time);
|
|
break;
|
|
case kStorageTypeUnknown:
|
|
default:
|
|
info->set_storage(
|
|
UsageTableInfo_UsageEntryInfo_UsageEntryStorage_UNKNOWN);
|
|
break;
|
|
}
|
|
}
|
|
usage_table_info->set_use_lru(true);
|
|
|
|
std::string serialized_file;
|
|
file.SerializeToString(&serialized_file);
|
|
|
|
return StoreFileWithHash(GetUsageTableFileName(), serialized_file) ==
|
|
kNoError;
|
|
}
|
|
|
|
bool DeviceFiles::RetrieveUsageTableInfo(
|
|
CdmUsageTableHeader* usage_table_header,
|
|
std::vector<CdmUsageEntryInfo>* usage_entry_info, bool* lru_upgrade) {
|
|
RETURN_FALSE_IF_UNINITIALIZED();
|
|
RETURN_FALSE_IF_NULL(usage_table_header);
|
|
RETURN_FALSE_IF_NULL(usage_entry_info);
|
|
RETURN_FALSE_IF_NULL(lru_upgrade);
|
|
|
|
video_widevine_client::sdk::File file;
|
|
if (RetrieveHashedFile(GetUsageTableFileName(), &file) != kNoError) {
|
|
LOGE("Unable to retrieve usage table file");
|
|
return false;
|
|
}
|
|
|
|
if (file.type() != video_widevine_client::sdk::File::USAGE_TABLE_INFO) {
|
|
LOGE("Incorrect file type: type = %d, expected_type = %d",
|
|
static_cast<int>(file.type()),
|
|
static_cast<int>(video_widevine_client::sdk::File::USAGE_TABLE_INFO));
|
|
return false;
|
|
}
|
|
|
|
if (file.version() != video_widevine_client::sdk::File::VERSION_1) {
|
|
LOGE("Incorrect file version: version = %d, expected_version = %d",
|
|
static_cast<int>(file.version()),
|
|
static_cast<int>(video_widevine_client::sdk::File::VERSION_1));
|
|
return false;
|
|
}
|
|
|
|
if (!file.has_usage_table_info()) {
|
|
LOGE("Usage table info not present in file");
|
|
return false;
|
|
}
|
|
|
|
const UsageTableInfo& usage_table_info = file.usage_table_info();
|
|
|
|
*lru_upgrade = !usage_table_info.use_lru();
|
|
|
|
*usage_table_header = usage_table_info.usage_table_header();
|
|
usage_entry_info->resize(usage_table_info.usage_entry_info_size());
|
|
for (int i = 0; i < usage_table_info.usage_entry_info_size(); ++i) {
|
|
const UsageTableInfo_UsageEntryInfo& info =
|
|
usage_table_info.usage_entry_info(i);
|
|
(*usage_entry_info)[i].key_set_id = info.key_set_id();
|
|
switch (info.storage()) {
|
|
case UsageTableInfo_UsageEntryInfo_UsageEntryStorage_LICENSE:
|
|
(*usage_entry_info)[i].storage_type = kStorageLicense;
|
|
(*usage_entry_info)[i].last_use_time = info.last_use_time();
|
|
(*usage_entry_info)[i].offline_license_expiry_time =
|
|
info.offline_license_expiry_time();
|
|
break;
|
|
case UsageTableInfo_UsageEntryInfo_UsageEntryStorage_USAGE_INFO:
|
|
(*usage_entry_info)[i].storage_type = kStorageUsageInfo;
|
|
(*usage_entry_info)[i].usage_info_file_name =
|
|
info.usage_info_file_name();
|
|
(*usage_entry_info)[i].last_use_time = info.last_use_time();
|
|
break;
|
|
case UsageTableInfo_UsageEntryInfo_UsageEntryStorage_UNKNOWN:
|
|
default:
|
|
(*usage_entry_info)[i].storage_type = kStorageTypeUnknown;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool DeviceFiles::DeleteUsageTableInfo() {
|
|
RETURN_FALSE_IF_UNINITIALIZED();
|
|
return RemoveFile(GetUsageTableFileName());
|
|
}
|
|
|
|
DeviceFiles::ResponseType DeviceFiles::StoreFileWithHash(
|
|
const std::string& name, const std::string& serialized_file) {
|
|
std::string hash = Sha256Hash(serialized_file);
|
|
|
|
// 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);
|
|
|
|
return StoreFileRaw(name, serialized_hash_file);
|
|
}
|
|
|
|
DeviceFiles::ResponseType DeviceFiles::StoreFileRaw(
|
|
const std::string& name, const std::string& serialized_file) {
|
|
std::string path;
|
|
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
|
LOGE("Unable to get base path");
|
|
return kBasePathUnavailable;
|
|
}
|
|
|
|
path += name;
|
|
|
|
auto file =
|
|
file_system_->Open(path, FileSystem::kCreate | FileSystem::kTruncate);
|
|
if (!file) {
|
|
LOGE("Failed to open file: path = %s", path.c_str());
|
|
return kFileOpenFailed;
|
|
}
|
|
|
|
const ssize_t bytes_written =
|
|
file->Write(serialized_file.data(), serialized_file.size());
|
|
|
|
if (bytes_written < 0) {
|
|
LOGE("Failed to write to file: path = %s", path.c_str());
|
|
return kFileWriteError;
|
|
}
|
|
if (bytes_written != static_cast<ssize_t>(serialized_file.size())) {
|
|
LOGE(
|
|
"Failed to fully write to file: path = %s, "
|
|
"bytes_written = %zd, bytes_attempted = %zu",
|
|
path.c_str(), bytes_written, serialized_file.size());
|
|
return kFileWriteError;
|
|
}
|
|
|
|
LOGV("Successfully stored raw file: path = %s, size = %zu", path.c_str(),
|
|
serialized_file.size());
|
|
return kNoError;
|
|
}
|
|
|
|
DeviceFiles::ResponseType DeviceFiles::RetrieveHashedFile(
|
|
const std::string& name,
|
|
video_widevine_client::sdk::File* deserialized_file) {
|
|
std::string serialized_file;
|
|
|
|
if (deserialized_file == nullptr) {
|
|
LOGE("File handle parameter |deserialized_file| not provided");
|
|
return kParameterNull;
|
|
}
|
|
|
|
std::string path;
|
|
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
|
LOGE("Unable to get base path");
|
|
return kBasePathUnavailable;
|
|
}
|
|
|
|
path += name;
|
|
|
|
if (!file_system_->Exists(path)) {
|
|
LOGW("File does not exist: path = %s", path.c_str());
|
|
return kFileNotFound;
|
|
}
|
|
|
|
const ssize_t file_size = file_system_->FileSize(path);
|
|
if (file_size <= 0) {
|
|
LOGE("File size is invalid: %s", path.c_str());
|
|
// Remove the corrupted file so the caller will not get the same error
|
|
// when trying to access the file repeatedly, causing the system to stall.
|
|
file_system_->Remove(path);
|
|
return kInvalidFileSize;
|
|
}
|
|
|
|
auto file = file_system_->Open(path, FileSystem::kReadOnly);
|
|
if (!file) {
|
|
return kFileOpenFailed;
|
|
}
|
|
|
|
std::string serialized_hash_file;
|
|
serialized_hash_file.resize(file_size);
|
|
const ssize_t bytes_read =
|
|
file->Read(&serialized_hash_file[0], serialized_hash_file.size());
|
|
|
|
if (bytes_read != file_size) {
|
|
if (bytes_read < 0) {
|
|
LOGE("Failed to read from file: path = %s", path.c_str());
|
|
} else {
|
|
LOGE(
|
|
"Failed to fully read from file: "
|
|
"path = %s, bytes_read = %zd, bytes_attempted = %zd",
|
|
path.c_str(), bytes_read, file_size);
|
|
}
|
|
// Remove the corrupted file so the caller will not get the same error
|
|
// when trying to access the file repeatedly, causing the system to stall.
|
|
file_system_->Remove(path);
|
|
return kFileReadError;
|
|
}
|
|
|
|
LOGV("Successfully read file: path = %s, size = %zu", path.c_str(),
|
|
serialized_hash_file.size());
|
|
|
|
HashedFile hash_file;
|
|
if (!hash_file.ParseFromString(serialized_hash_file)) {
|
|
LOGE("Unable to parse hash file");
|
|
// Remove the corrupted file so the caller will not get the same error
|
|
// when trying to access the file repeatedly, causing the system to stall.
|
|
file_system_->Remove(path);
|
|
return kFileParseError1;
|
|
}
|
|
|
|
std::string hash = Sha256Hash(hash_file.file());
|
|
if (hash != hash_file.hash()) {
|
|
LOGE("File hash mismatch: path = %s", path.c_str());
|
|
// Remove the corrupted file so the caller will not get the same error
|
|
// when trying to access the file repeatedly, causing the system to stall.
|
|
file_system_->Remove(path);
|
|
return kFileHashMismatch;
|
|
}
|
|
|
|
if (!deserialized_file->ParseFromString(hash_file.file())) {
|
|
LOGE("Unable to parse hashed file");
|
|
// Remove the corrupted file so the caller will not get the same error
|
|
// when trying to access the file repeatedly, causing the system to stall.
|
|
file_system_->Remove(path);
|
|
return kFileParseError2;
|
|
}
|
|
return kNoError;
|
|
}
|
|
|
|
bool DeviceFiles::FileExists(const std::string& name) {
|
|
std::string path;
|
|
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
|
LOGE("Unable to get base path");
|
|
return false;
|
|
}
|
|
path += name;
|
|
|
|
return file_system_->Exists(path);
|
|
}
|
|
|
|
bool DeviceFiles::ListFiles(std::vector<std::string>* names) {
|
|
std::string path;
|
|
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
|
LOGE("Unable to get base path");
|
|
return false;
|
|
}
|
|
return file_system_->List(path, names);
|
|
}
|
|
|
|
bool DeviceFiles::RemoveFile(const std::string& name) {
|
|
std::string path;
|
|
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
|
LOGE("Unable to get base path");
|
|
return false;
|
|
}
|
|
path += name;
|
|
|
|
return file_system_->Remove(path);
|
|
}
|
|
|
|
ssize_t DeviceFiles::GetFileSize(const std::string& name) {
|
|
std::string path;
|
|
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
|
LOGE("Unable to get base path");
|
|
return -1;
|
|
}
|
|
path += name;
|
|
|
|
return file_system_->FileSize(path);
|
|
}
|
|
|
|
std::string DeviceFiles::GetCertificateFileName(bool atsc_mode_enabled) {
|
|
return atsc_mode_enabled ? kAtscCertificateFileName : kCertificateFileName;
|
|
}
|
|
|
|
std::string DeviceFiles::GetUsageTableFileName() { return kUsageTableFileName; }
|
|
|
|
std::string DeviceFiles::GetHlsAttributesFileNameExtension() {
|
|
return kHlsAttributesFileNameExt;
|
|
}
|
|
|
|
std::string DeviceFiles::GetLicenseFileNameExtension() {
|
|
return kLicenseFileNameExt;
|
|
}
|
|
|
|
std::string DeviceFiles::GetUsageInfoFileName(const std::string& app_id) {
|
|
std::string hash;
|
|
if (app_id != "") {
|
|
hash = GetFileNameSafeHash(app_id);
|
|
}
|
|
return kUsageInfoFileNamePrefix + hash + kUsageInfoFileNameExt;
|
|
}
|
|
|
|
std::string DeviceFiles::GetFileNameSafeHash(const std::string& input) {
|
|
std::string hash = Md5Hash(input);
|
|
return wvcdm::Base64SafeEncode(
|
|
std::vector<uint8_t>(hash.begin(), hash.end()));
|
|
}
|
|
|
|
} // namespace wvcdm
|