blob: bed4adceed91ed239c2778b882161a643d89689c [file] [log] [blame]
// Copyright 2017 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/cloud_sync/impl/user_sync_impl.h"
#include <lib/fit/function.h>
#include <zircon/syscalls.h>
#include <utility>
#include "src/ledger/bin/clocks/public/device_fingerprint_manager.h"
#include "src/ledger/bin/cloud_sync/impl/ledger_sync_impl.h"
#include "src/ledger/bin/public/status.h"
#include "src/ledger/lib/convert/convert.h"
#include "src/ledger/lib/coroutine/coroutine.h"
#include "src/ledger/lib/coroutine/coroutine_manager.h"
#include "src/ledger/lib/logging/logging.h"
#include "third_party/abseil-cpp/absl/strings/string_view.h"
namespace cloud_sync {
UserSyncImpl::UserSyncImpl(ledger::Environment* environment, UserConfig user_config,
std::unique_ptr<ledger::Backoff> backoff,
fit::closure on_version_mismatch,
clocks::DeviceFingerprintManager* fingerprint_manager)
: environment_(environment),
user_config_(std::move(user_config)),
backoff_(std::move(backoff)),
on_version_mismatch_(std::move(on_version_mismatch)),
watcher_binding_(this),
fingerprint_manager_(fingerprint_manager),
coroutine_manager_(environment_->coroutine_service()),
task_runner_(environment_->dispatcher()) {
LEDGER_DCHECK(on_version_mismatch_);
}
UserSyncImpl::~UserSyncImpl() { LEDGER_DCHECK(active_ledger_syncs_.empty()); }
void UserSyncImpl::SetSyncWatcher(SyncStateWatcher* watcher) {
aggregator_.SetBaseWatcher(watcher);
}
std::unique_ptr<LedgerSync> UserSyncImpl::CreateLedgerSync(
absl::string_view app_id, encryption::EncryptionService* encryption_service) {
LEDGER_DCHECK(started_);
auto result = std::make_unique<LedgerSyncImpl>(environment_, &user_config_, encryption_service,
app_id, aggregator_.GetNewStateWatcher());
result->set_on_delete(
[this, ledger_sync = result.get()]() { active_ledger_syncs_.erase(ledger_sync); });
active_ledger_syncs_.insert(result.get());
if (upload_enabled_) {
result->EnableUpload();
}
return result;
}
ledger::DetachedPath UserSyncImpl::GetFingerprintPath() {
return user_config_.user_directory.SubPath("fingerprint");
}
void UserSyncImpl::OnCloudErased() {
// |this| can be deleted within on_version_mismatch_() - don't
// access member variables afterwards.
on_version_mismatch_();
}
void UserSyncImpl::OnError(cloud_provider::Status status) {
task_runner_.PostDelayedTask([this] { SetCloudErasedWatcher(); }, backoff_->GetNext());
}
void UserSyncImpl::Start() {
LEDGER_DCHECK(!started_);
if (!user_config_.cloud_provider) {
// TODO(ppi): handle recovery from cloud provider disconnection, LE-567.
LEDGER_LOG(WARNING) << "Cloud provider is disconnected, will not verify "
<< "the cloud fingerprint";
return;
}
user_config_.cloud_provider->GetDeviceSet(
device_set_.NewRequest(),
task_runner_.MakeScoped([this](fuchsia::ledger::cloud::Status status) {
if (status != cloud_provider::Status::OK) {
LEDGER_LOG(ERROR) << "Failed to retrieve the device map: " << fidl::ToUnderlying(status)
<< ", sync upload will not work.";
return;
}
CheckCloudNotErased();
}));
started_ = true;
}
void UserSyncImpl::CheckCloudNotErased() {
if (!device_set_) {
// TODO(ppi): handle recovery from cloud provider disconnection, LE-567.
LEDGER_LOG(WARNING) << "Cloud provider is disconnected, will not verify "
<< "the cloud fingerprint";
return;
}
coroutine_manager_.StartCoroutine([this](coroutine::CoroutineHandler* handler) {
cloud_provider::Status status;
clocks::DeviceFingerprintManager::CloudUploadStatus upload_status;
if (fingerprint_manager_->GetDeviceFingerprint(handler, &fingerprint_, &upload_status) !=
ledger::Status::OK) {
return;
}
switch (upload_status) {
case clocks::DeviceFingerprintManager::CloudUploadStatus::NOT_UPLOADED: {
if (coroutine::SyncCall(
handler,
[this](fit::function<void(cloud_provider::Status)> callback) {
device_set_->SetFingerprint(convert::ToArray(fingerprint_), std::move(callback));
},
&status) == coroutine::ContinuationStatus::INTERRUPTED) {
return;
}
if (status == cloud_provider::Status::OK) {
ledger::Status s = fingerprint_manager_->SetDeviceFingerprintSynced(handler);
if (s != ledger::Status::OK) {
return;
}
}
} break;
case clocks::DeviceFingerprintManager::CloudUploadStatus::UPLOADED: {
if (coroutine::SyncCall(
handler,
[this](fit::function<void(cloud_provider::Status)> callback) {
device_set_->CheckFingerprint(convert::ToArray(fingerprint_),
std::move(callback));
},
&status) == coroutine::ContinuationStatus::INTERRUPTED) {
return;
}
}
}
HandleDeviceSetResult(status);
});
}
void UserSyncImpl::HandleDeviceSetResult(cloud_provider::Status status) {
switch (status) {
case cloud_provider::Status::OK:
backoff_->Reset();
SetCloudErasedWatcher();
EnableUpload();
return;
case cloud_provider::Status::NETWORK_ERROR:
// Retry after some backoff time.
task_runner_.PostDelayedTask([this] { CheckCloudNotErased(); }, backoff_->GetNext());
return;
case cloud_provider::Status::NOT_FOUND:
// |this| can be deleted within on_version_mismatch_() - don't
// access member variables afterwards. Also, make sure we are not executing within a
// coroutine.
task_runner_.PostTask(std::move(on_version_mismatch_));
return;
default:
LEDGER_LOG(ERROR) << "Unexpected status returned from device set: "
<< fidl::ToUnderlying(status) << ", sync upload will not work.";
return;
}
}
void UserSyncImpl::SetCloudErasedWatcher() {
if (!device_set_) {
// TODO(ppi): handle recovery from cloud provider disconnection, LE-567.
LEDGER_LOG(WARNING) << "Cloud provider is disconnected, will not verify "
<< "the cloud fingerprint";
return;
}
cloud_provider::DeviceSetWatcherPtr watcher;
if (watcher_binding_.is_bound()) {
watcher_binding_.Unbind();
}
watcher_binding_.Bind(watcher.NewRequest());
device_set_->SetWatcher(convert::ToArray(fingerprint_), std::move(watcher),
[this](cloud_provider::Status status) {
if (status == cloud_provider::Status::OK) {
backoff_->Reset();
}
// Don't handle errors - in case of error, the
// corresponding call is made on the watcher
// itself and handled there (OnCloudErased(),
// OnError()).
});
}
void UserSyncImpl::EnableUpload() {
upload_enabled_ = true;
for (auto ledger_sync : active_ledger_syncs_) {
ledger_sync->EnableUpload();
}
}
} // namespace cloud_sync