blob: 69e0cff705a6d7c1bf7771f3f7df9baeed5d44b1 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/ledger/bin/app/ledger_repository_impl.h"
#include <lib/inspect/deprecated/expose.h>
#include <lib/inspect/deprecated/object_dir.h>
#include <trace/event.h>
#include "peridot/lib/base64url/base64url.h"
#include "peridot/lib/convert/convert.h"
#include "src/ledger/bin/app/constants.h"
#include "src/ledger/bin/app/page_utils.h"
#include "src/ledger/bin/cloud_sync/impl/ledger_sync_impl.h"
#include "src/ledger/bin/p2p_sync/public/ledger_communicator.h"
#include "src/ledger/bin/storage/impl/ledger_storage_impl.h"
#include "src/ledger/bin/sync_coordinator/public/ledger_sync.h"
namespace ledger {
namespace {
// Encodes opaque bytes in a way that is usable as a directory name.
std::string GetDirectoryName(fxl::StringView bytes) {
return base64url::Base64UrlEncode(bytes);
}
} // namespace
LedgerRepositoryImpl::LedgerRepositoryImpl(
DetachedPath content_path, Environment* environment,
std::unique_ptr<storage::DbFactory> db_factory,
std::unique_ptr<SyncWatcherSet> watchers,
std::unique_ptr<sync_coordinator::UserSync> user_sync,
std::unique_ptr<DiskCleanupManager> disk_cleanup_manager,
PageUsageListener* page_usage_listener, inspect::Node inspect_node)
: content_path_(std::move(content_path)),
environment_(environment),
db_factory_(std::move(db_factory)),
encryption_service_factory_(environment),
watchers_(std::move(watchers)),
user_sync_(std::move(user_sync)),
page_usage_listener_(page_usage_listener),
disk_cleanup_manager_(std::move(disk_cleanup_manager)),
inspect_node_(std::move(inspect_node)),
requests_metric_(
inspect_node_.CreateUIntMetric(kRequestsInspectPathComponent, 0UL)),
ledgers_inspect_node_(
inspect_node_.CreateChild(kLedgersInspectPathComponent)) {
bindings_.set_on_empty([this] { CheckEmpty(); });
ledger_managers_.set_on_empty([this] { CheckEmpty(); });
disk_cleanup_manager_->set_on_empty([this] { CheckEmpty(); });
}
LedgerRepositoryImpl::~LedgerRepositoryImpl() {}
void LedgerRepositoryImpl::BindRepository(
fidl::InterfaceRequest<ledger_internal::LedgerRepository>
repository_request) {
bindings_.emplace(this, std::move(repository_request));
requests_metric_.Add(1);
}
void LedgerRepositoryImpl::PageIsClosedAndSynced(
fxl::StringView ledger_name, storage::PageIdView page_id,
fit::function<void(storage::Status, PagePredicateResult)> callback) {
LedgerManager* ledger_manager;
storage::Status status = GetLedgerManager(ledger_name, &ledger_manager);
if (status != storage::Status::OK) {
callback(status, PagePredicateResult::PAGE_OPENED);
return;
}
FXL_DCHECK(ledger_manager);
// |ledger_manager| can be destructed if empty, or if the
// |LedgerRepositoryImpl| is destructed. In the second case, the callback
// should not be called. The first case will not happen before the callback
// has been called, because the manager is non-empty while a page is tracked.
ledger_manager->PageIsClosedAndSynced(page_id, std::move(callback));
}
void LedgerRepositoryImpl::PageIsClosedOfflineAndEmpty(
fxl::StringView ledger_name, storage::PageIdView page_id,
fit::function<void(storage::Status, PagePredicateResult)> callback) {
LedgerManager* ledger_manager;
storage::Status status = GetLedgerManager(ledger_name, &ledger_manager);
if (status != storage::Status::OK) {
callback(status, PagePredicateResult::PAGE_OPENED);
return;
}
FXL_DCHECK(ledger_manager);
// |ledger_manager| can be destructed if empty, or if the
// |LedgerRepositoryImpl| is destructed. In the second case, the callback
// should not be called. The first case will not happen before the callback
// has been called, because the manager is non-empty while a page is tracked.
ledger_manager->PageIsClosedOfflineAndEmpty(page_id, std::move(callback));
}
void LedgerRepositoryImpl::DeletePageStorage(
fxl::StringView ledger_name, storage::PageIdView page_id,
fit::function<void(storage::Status)> callback) {
LedgerManager* ledger_manager;
storage::Status status = GetLedgerManager(ledger_name, &ledger_manager);
if (status != storage::Status::OK) {
callback(status);
return;
}
FXL_DCHECK(ledger_manager);
return ledger_manager->DeletePageStorage(page_id, std::move(callback));
}
std::vector<fidl::InterfaceRequest<ledger_internal::LedgerRepository>>
LedgerRepositoryImpl::Unbind() {
std::vector<fidl::InterfaceRequest<ledger_internal::LedgerRepository>>
handles;
for (auto& binding : bindings_) {
handles.push_back(binding.Unbind());
}
bindings_.clear();
return handles;
}
storage::Status LedgerRepositoryImpl::GetLedgerManager(
convert::ExtendedStringView ledger_name, LedgerManager** ledger_manager) {
FXL_DCHECK(!ledger_name.empty());
// If the Ledger instance is already open return it directly.
auto it = ledger_managers_.find(ledger_name);
if (it != ledger_managers_.end()) {
*ledger_manager = &(it->second);
return storage::Status::OK;
}
std::string name_as_string = convert::ToString(ledger_name);
std::unique_ptr<encryption::EncryptionService> encryption_service =
encryption_service_factory_.MakeEncryptionService(name_as_string);
auto ledger_storage = std::make_unique<storage::LedgerStorageImpl>(
environment_, encryption_service.get(), db_factory_.get(),
GetPathFor(name_as_string));
storage::Status status = ledger_storage->Init();
if (status != storage::Status::OK) {
return status;
}
std::unique_ptr<sync_coordinator::LedgerSync> ledger_sync;
if (user_sync_) {
ledger_sync =
user_sync_->CreateLedgerSync(name_as_string, encryption_service.get());
}
auto result = ledger_managers_.emplace(
std::piecewise_construct, std::forward_as_tuple(name_as_string),
std::forward_as_tuple(environment_, name_as_string,
ledgers_inspect_node_.CreateChild(name_as_string),
std::move(encryption_service),
std::move(ledger_storage), std::move(ledger_sync),
page_usage_listener_));
FXL_DCHECK(result.second);
*ledger_manager = &(result.first->second);
return storage::Status::OK;
}
void LedgerRepositoryImpl::GetLedger(
std::vector<uint8_t> ledger_name,
fidl::InterfaceRequest<Ledger> ledger_request,
fit::function<void(Status)> callback) {
TRACE_DURATION("ledger", "repository_get_ledger");
if (ledger_name.empty()) {
callback(Status::INVALID_ARGUMENT);
return;
}
LedgerManager* ledger_manager;
storage::Status status = GetLedgerManager(ledger_name, &ledger_manager);
if (status != storage::Status::OK) {
callback(PageUtils::ConvertStatus(status));
return;
}
FXL_DCHECK(ledger_manager);
ledger_manager->BindLedger(std::move(ledger_request));
callback(Status::OK);
}
void LedgerRepositoryImpl::Duplicate(
fidl::InterfaceRequest<ledger_internal::LedgerRepository> request,
fit::function<void(Status)> callback) {
BindRepository(std::move(request));
callback(Status::OK);
}
void LedgerRepositoryImpl::SetSyncStateWatcher(
fidl::InterfaceHandle<SyncWatcher> watcher,
fit::function<void(Status)> callback) {
watchers_->AddSyncWatcher(std::move(watcher));
callback(Status::OK);
}
void LedgerRepositoryImpl::CheckEmpty() {
if (!on_empty_callback_)
return;
if (ledger_managers_.empty() && bindings_.empty() &&
disk_cleanup_manager_->IsEmpty()) {
on_empty_callback_();
}
}
void LedgerRepositoryImpl::DiskCleanUp(fit::function<void(Status)> callback) {
cleanup_callbacks_.push_back(std::move(callback));
if (cleanup_callbacks_.size() > 1) {
return;
}
disk_cleanup_manager_->TryCleanUp([this](storage::Status status) {
FXL_DCHECK(!cleanup_callbacks_.empty());
auto callbacks = std::move(cleanup_callbacks_);
cleanup_callbacks_.clear();
Status ledger_status = PageUtils::ConvertStatus(status);
for (auto& callback : callbacks) {
callback(ledger_status);
}
});
}
DetachedPath LedgerRepositoryImpl::GetPathFor(fxl::StringView ledger_name) {
FXL_DCHECK(!ledger_name.empty());
return content_path_.SubPath(GetDirectoryName(ledger_name));
}
} // namespace ledger