[ Merge of http://go/wvgerrit/100905 and http://go/ag/10708438 ] Add support for ATSC certificate and licenses handling. ATSC files are distinguished from the apps DRM certificate and licenses by file naming conventions. Bug: 139730600 Test: WV unit/integration test, GtsMediaTestCases Change-Id: I295f66f92fe01d7716978deac9dc360d74addedd
1251 lines
42 KiB
C++
1251 lines
42 KiB
C++
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
|
// source code may only be used and distributed under the Widevine Master
|
|
// License Agreement.
|
|
|
|
#include "device_files.h"
|
|
|
|
#include <string.h>
|
|
|
|
#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 std::string& wrapped_private_key) {
|
|
RETURN_FALSE_IF_UNINITIALIZED();
|
|
|
|
// 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(wrapped_private_key);
|
|
|
|
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,
|
|
std::string* wrapped_private_key,
|
|
std::string* serial_number,
|
|
uint32_t* system_id) {
|
|
RETURN_FALSE_IF_UNINITIALIZED();
|
|
|
|
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();
|
|
*wrapped_private_key = device_certificate.wrapped_private_key();
|
|
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)) {
|
|
LOGE("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
|