blob: 9c9d7d6bcc0451fd789f8c3e8dc68af7086f25e0 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/media/cdm_storage_manager.h"
#include "base/metrics/histogram_functions.h"
#include "base/notreached.h"
#include "base/task/thread_pool.h"
#include "base/types/pass_key.h"
#include "content/browser/media/cdm_storage_common.h"
#include "content/public/common/content_features.h"
#include "media/cdm/cdm_type.h"
#include "media/mojo/mojom/cdm_storage.mojom.h"
namespace content {
namespace {
const char kUmaPrefix[] = "Media.EME.CdmStorageManager.";
const char kGetSizeForFileError[] = "GetSizeForFileError.";
const char kGetSizeForStorageKeyError[] = "GetSizeForStorageKeyError.";
const char kGetSizeForTimeFrameError[] = "GetSizeForTimeFrameError.";
const char kWriteFileError[] = "WriteFileError.";
const char kReadFileError[] = "ReadFileError.";
const char kDatabaseOpenErrorNoPeriod[] = "DatabaseOpenError";
const char kDatabaseOpenError[] = "DatabaseOpenError.";
const char kDatabaseSizeName[] = "CurrentDatabaseUsageKB.";
constexpr uint64_t kBytesPerKB = 1024;
constexpr int kMinDatabaseSizeKB = 0;
// Used for histogram reporting, the max size of the database we expect in KB.
constexpr uint64_t kMaxDatabaseSizeKB = 512000 * 10;
constexpr int kSizeKBBuckets = 1000;
// Creates a task runner suitable for running SQLite database operations.
scoped_refptr<base::SequencedTaskRunner> CreateDatabaseTaskRunner() {
// We use a SequencedTaskRunner so that there is a global ordering to a
// storage key's directory operations.
return base::ThreadPool::CreateSequencedTaskRunner({
// Needed for file I/O.
base::MayBlock(),
// Reasonable compromise, given that a few database operations are
// blocking, while most operations are not. We should be able to do better
// when we get scheduling APIs on the Web Platform.
base::TaskPriority::USER_VISIBLE,
// Needed to allow for clearing site data on shutdown.
base::TaskShutdownBehavior::BLOCK_SHUTDOWN,
});
}
} // namespace
CdmStorageManager::CdmStorageManager(const base::FilePath& path)
: path_(path), db_(CreateDatabaseTaskRunner(), path_) {}
CdmStorageManager::~CdmStorageManager() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void CdmStorageManager::Open(const std::string& file_name,
OpenCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (file_name.empty()) {
DVLOG(1) << "No file specified.";
ReportDatabaseOpenError(CdmStorageOpenError::kNoFileSpecified);
std::move(callback).Run(Status::kFailure, mojo::NullAssociatedRemote());
return;
}
if (!CdmFileImpl::IsValidName(file_name)) {
DVLOG(1) << "Invalid name of file.";
ReportDatabaseOpenError(CdmStorageOpenError::kInvalidFileName);
std::move(callback).Run(Status::kFailure, mojo::NullAssociatedRemote());
return;
}
db_.AsyncCall(&CdmStorageDatabase::EnsureOpen)
.Then(base::BindOnce(&CdmStorageManager::DidOpenFile,
weak_factory_.GetWeakPtr(),
receivers_.current_context().storage_key,
receivers_.current_context().cdm_type, file_name,
std::move(callback)));
}
void CdmStorageManager::GetUsagePerAllStorageKeys(
base::OnceCallback<void(const CdmStorageKeyUsageSize&)> callback,
base::Time begin,
base::Time end) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
db_.AsyncCall(&CdmStorageDatabase::GetUsagePerAllStorageKeys)
.WithArgs(begin, end)
.Then(std::move(callback));
}
void CdmStorageManager::DeleteDataForStorageKey(
const blink::StorageKey& storage_key,
base::OnceCallback<void(bool)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DeleteData(base::NullCallback(), storage_key, base::Time::Min(),
base::Time::Max(), std::move(callback));
}
void CdmStorageManager::OpenCdmStorage(
const CdmStorageBindingContext& binding_context,
mojo::PendingReceiver<media::mojom::CdmStorage> receiver) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
receivers_.Add(this, std::move(receiver), binding_context);
}
void CdmStorageManager::ReadFile(
const blink::StorageKey& storage_key,
const media::CdmType& cdm_type,
const std::string& file_name,
base::OnceCallback<void(std::optional<std::vector<uint8_t>>)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
db_.AsyncCall(&CdmStorageDatabase::ReadFile)
.WithArgs(storage_key, cdm_type, file_name)
.Then(base::BindOnce(&CdmStorageManager::DidReadFile,
weak_factory_.GetWeakPtr(), std::move(callback)));
}
void CdmStorageManager::WriteFile(const blink::StorageKey& storage_key,
const media::CdmType& cdm_type,
const std::string& file_name,
const std::vector<uint8_t>& data,
base::OnceCallback<void(bool)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
db_.AsyncCall(&CdmStorageDatabase::WriteFile)
.WithArgs(storage_key, cdm_type, file_name, data)
.Then(base::BindOnce(&CdmStorageManager::DidWriteFile,
weak_factory_.GetWeakPtr(), std::move(callback)));
}
void CdmStorageManager::GetSizeForFile(
const blink::StorageKey& storage_key,
const media::CdmType& cdm_type,
const std::string& file_name,
base::OnceCallback<void(uint64_t)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
db_.AsyncCall(&CdmStorageDatabase::GetSizeForFile)
.WithArgs(storage_key, cdm_type, file_name)
.Then(base::BindOnce(&CdmStorageManager::DidGetSize,
weak_factory_.GetWeakPtr(), std::move(callback),
kGetSizeForFileError));
}
void CdmStorageManager::GetSizeForStorageKey(
const blink::StorageKey& storage_key,
const base::Time begin,
const base::Time end,
base::OnceCallback<void(uint64_t)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
db_.AsyncCall(&CdmStorageDatabase::GetSizeForStorageKey)
.WithArgs(storage_key, begin, end)
.Then(base::BindOnce(&CdmStorageManager::DidGetSize,
weak_factory_.GetWeakPtr(), std::move(callback),
kGetSizeForStorageKeyError));
}
void CdmStorageManager::GetSizeForTimeFrame(
const base::Time begin,
const base::Time end,
base::OnceCallback<void(uint64_t)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
db_.AsyncCall(&CdmStorageDatabase::GetSizeForTimeFrame)
.WithArgs(begin, end)
.Then(base::BindOnce(&CdmStorageManager::DidGetSize,
weak_factory_.GetWeakPtr(), std::move(callback),
kGetSizeForTimeFrameError));
}
void CdmStorageManager::DeleteFile(const blink::StorageKey& storage_key,
const media::CdmType& cdm_type,
const std::string& file_name,
base::OnceCallback<void(bool)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
db_.AsyncCall(&CdmStorageDatabase::DeleteFile)
.WithArgs(storage_key, cdm_type, file_name)
.Then(std::move(callback));
}
void CdmStorageManager::DeleteData(
const StoragePartition::StorageKeyMatcherFunction& storage_key_matcher,
const blink::StorageKey& storage_key,
const base::Time begin,
const base::Time end,
base::OnceCallback<void(bool)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
db_.AsyncCall(&CdmStorageDatabase::DeleteData)
.WithArgs(storage_key_matcher, storage_key, begin, end)
.Then(std::move(callback));
}
void CdmStorageManager::OnFileReceiverDisconnect(
const std::string& name,
const media::CdmType& cdm_type,
const blink::StorageKey& storage_key,
base::PassKey<CdmFileImpl> pass_key) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto count = cdm_files_.erase(CdmFileId(name, cdm_type, storage_key));
DCHECK_GT(count, 0u);
}
void CdmStorageManager::DidOpenFile(const blink::StorageKey& storage_key,
const media::CdmType& cdm_type,
const std::string& file_name,
OpenCallback callback,
CdmStorageOpenError error) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (error != CdmStorageOpenError::kOk) {
ReportDatabaseOpenError(error);
std::move(callback).Run(Status::kFailure, mojo::NullAssociatedRemote());
return;
}
// Check whether this CDM file is in-use.
CdmFileId id(file_name, cdm_type, storage_key);
if (base::Contains(cdm_files_, id)) {
std::move(callback).Run(Status::kInUse, mojo::NullAssociatedRemote());
return;
}
// File was opened successfully, so create the binding and return success.
mojo::PendingAssociatedRemote<media::mojom::CdmFile> cdm_file;
cdm_files_.emplace(id, std::make_unique<CdmFileImpl>(
this, storage_key, cdm_type, file_name,
cdm_file.InitWithNewEndpointAndPassReceiver()));
std::move(callback).Run(Status::kSuccess, std::move(cdm_file));
}
void CdmStorageManager::DidReadFile(
base::OnceCallback<void(std::optional<std::vector<uint8_t>>)> callback,
std::optional<std::vector<uint8_t>> data) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::UmaHistogramBoolean(
GetCdmStorageManagerHistogramName(kReadFileError, in_memory()),
!data.has_value());
if (!database_size_reported_) {
db_.AsyncCall(&CdmStorageDatabase::GetDatabaseSize)
.Then(base::BindOnce(&CdmStorageManager::DidGetDatabaseSize,
weak_factory_.GetWeakPtr()));
database_size_reported_ = true;
}
std::move(callback).Run(data);
}
void CdmStorageManager::DidWriteFile(base::OnceCallback<void(bool)> callback,
bool success) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::UmaHistogramBoolean(
GetCdmStorageManagerHistogramName(kWriteFileError, in_memory()),
!success);
std::move(callback).Run(success);
}
void CdmStorageManager::DidGetSize(base::OnceCallback<void(uint64_t)> callback,
const std::string& operation,
std::optional<uint64_t> size) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::UmaHistogramBoolean(
GetCdmStorageManagerHistogramName(operation, in_memory()),
!size.has_value());
std::move(callback).Run(size.value_or(0));
}
void CdmStorageManager::DidGetDatabaseSize(const uint64_t size) {
// One time report DatabaseSize.
base::UmaHistogramCustomCounts(
GetCdmStorageManagerHistogramName(kDatabaseSizeName, in_memory()),
size / kBytesPerKB, kMinDatabaseSizeKB, kMaxDatabaseSizeKB,
kSizeKBBuckets);
}
// TODO(crbug.com/40272342) Investigate if we can propagate the SQL errors.
// Investigate adding delete functionality to 'MojoCdmHelper::CloseCdmFileIO' to
// close database on CdmFileIO closure.
void CdmStorageManager::ReportDatabaseOpenError(CdmStorageOpenError error) {
// General Errors without distinguishing incognito or not.
base::UmaHistogramEnumeration(
std::string{kUmaPrefix} + std::string{kDatabaseOpenErrorNoPeriod}, error);
// Histogram split by incognito and non-incognito.
base::UmaHistogramEnumeration(
GetCdmStorageManagerHistogramName(kDatabaseOpenError, in_memory()),
error);
}
} // namespace content