blob: 4675353a2578e68dc3b97b89aebfbd6459b09805 [file] [log] [blame]
// Copyright 2019 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/developer/feedback/feedback_agent/annotations/channel_provider.h"
#include <lib/async/cpp/task.h>
#include <lib/async/default.h>
#include <zircon/errors.h>
#include "src/developer/feedback/feedback_agent/constants.h"
#include "src/developer/feedback/utils/promise.h"
#include "src/lib/fxl/logging.h"
#include "src/lib/syslog/cpp/logger.h"
namespace feedback {
namespace {
using fuchsia::feedback::Annotation;
using internal::ChannelProviderPtr;
} // namespace
ChannelProvider::ChannelProvider(async_dispatcher_t* dispatcher,
std::shared_ptr<sys::ServiceDirectory> services,
zx::duration timeout, Cobalt* cobalt)
: dispatcher_(dispatcher), services_(services), timeout_(timeout), cobalt_(cobalt) {}
std::set<std::string> ChannelProvider::GetSupportedAnnotations() {
return {
kAnnotationChannel,
};
}
fit::promise<std::vector<Annotation>> ChannelProvider::GetAnnotations() {
auto channel_ptr = std::make_unique<ChannelProviderPtr>(dispatcher_, services_, cobalt_);
// We must store the promise in a variable due to the fact that the order of evaluation of
// function parameters is undefined.
auto channel = channel_ptr->GetCurrent(timeout_);
return ExtendArgsLifetimeBeyondPromise(/*promise=*/std::move(channel),
/*args=*/std::move(channel_ptr))
.and_then([](const std::string& channel) {
std::vector<Annotation> annotations;
Annotation annotation;
annotation.key = kAnnotationChannel;
annotation.value = channel;
annotations.push_back(std::move(annotation));
return fit::ok(std::move(annotations));
})
.or_else([] {
FX_LOGS(WARNING) << "Failed to build annotation " << kAnnotationChannel;
return fit::error();
});
}
namespace internal {
ChannelProviderPtr::ChannelProviderPtr(async_dispatcher_t* dispatcher,
std::shared_ptr<sys::ServiceDirectory> services,
Cobalt* cobalt)
: dispatcher_(dispatcher), services_(services), cobalt_(cobalt) {}
fit::promise<std::string> ChannelProviderPtr::GetCurrent(zx::duration timeout) {
FXL_CHECK(!has_called_get_current_) << "GetCurrent() is not intended to be called twice";
has_called_get_current_ = true;
update_info_ = services_->Connect<fuchsia::update::channel::Provider>();
// fit::promise does not have the notion of a timeout. So we post a delayed task that will call
// the completer after the timeout and return an error.
//
// We wrap the delayed task in a CancelableClosure so we can cancel it when the fit::bridge is
// completed another way.
//
// It is safe to pass "this" to the fit::function as the callback won't be callable when the
// CancelableClosure goes out of scope, which is before "this".
done_after_timeout_.Reset([this] {
if (!done_.completer) {
return;
}
FX_LOGS(ERROR) << "Current OTA channel retrieval timed out";
cobalt_->LogOccurrence(TimedOutData::kChannel);
done_.completer.complete_error();
});
const zx_status_t post_status = async::PostDelayedTask(
dispatcher_, [cb = done_after_timeout_.callback()] { cb(); }, timeout);
if (post_status != ZX_OK) {
FX_PLOGS(ERROR, post_status) << "Failed to post delayed task";
FX_LOGS(ERROR) << "Skipping current OTA channel retrieval as it is not safe without a timeout";
return fit::make_result_promise<std::string>(fit::error());
}
update_info_.set_error_handler([this](zx_status_t status) {
if (!done_.completer) {
return;
}
FX_PLOGS(ERROR, status) << "Lost connection to fuchsia.update.channel.Provider";
done_.completer.complete_error();
});
update_info_->GetCurrent([this](std::string channel) {
if (!done_.completer) {
return;
}
done_.completer.complete_ok(std::move(channel));
});
return done_.consumer.promise_or(fit::error()).then([this](fit::result<std::string>& result) {
done_after_timeout_.Cancel();
return std::move(result);
});
}
} // namespace internal
} // namespace feedback