blob: b77223710a9c6da60811fa1fd670389bb6cd86fa [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_agent/data_provider.h"
#include <fuchsia/feedback/cpp/fidl.h>
#include <fuchsia/ui/scenic/cpp/fidl.h>
#include <lib/fit/promise.h>
#include <lib/syslog/cpp/logger.h>
#include <zircon/errors.h>
#include <zircon/status.h>
#include "src/developer/feedback_agent/annotations.h"
#include "src/developer/feedback_agent/attachments.h"
#include "src/developer/feedback_agent/config.h"
#include "src/developer/feedback_agent/image_conversion.h"
#include "src/lib/fxl/strings/string_printf.h"
namespace fuchsia {
namespace feedback {
namespace {
const char kDefaultConfigPath[] = "/pkg/data/default_config.json";
} // namespace
std::unique_ptr<DataProviderImpl> DataProviderImpl::TryCreate(
async_dispatcher_t* dispatcher,
std::shared_ptr<::sys::ServiceDirectory> services) {
Config config;
const zx_status_t parse_status = ParseConfig(kDefaultConfigPath, &config);
if (parse_status != ZX_OK) {
FX_LOGS(ERROR) << "Failed to read default config file at "
<< kDefaultConfigPath << ": " << parse_status << " ("
<< zx_status_get_string(parse_status) << ")";
FX_LOGS(FATAL) << "Failed to set up data provider";
return nullptr;
}
return std::make_unique<DataProviderImpl>(dispatcher, std::move(services),
config);
}
DataProviderImpl::DataProviderImpl(
async_dispatcher_t* dispatcher,
std::shared_ptr<::sys::ServiceDirectory> services, const Config& config)
: executor_(dispatcher), services_(services), config_(config) {}
void DataProviderImpl::GetData(GetDataCallback callback) {
// Today attachments are fetched asynchronously, but annotations are not.
// In the future, we can use fit::join_promises() if annotations need to be
// fetched asynchronously as well.
auto promise =
fit::join_promise_vector(
GetAttachments(services_, config_.attachment_whitelist))
.then([callback = std::move(callback)](
fit::result<std::vector<fit::result<Attachment>>>&
attachments) {
DataProvider_GetData_Response response;
response.data.set_annotations(GetAnnotations());
if (attachments.is_ok()) {
std::vector<Attachment> ok_attachments;
for (auto& attachment : attachments.take_value()) {
if (attachment.is_ok()) {
ok_attachments.emplace_back(attachment.take_value());
}
}
if (!ok_attachments.empty()) {
response.data.set_attachments(std::move(ok_attachments));
}
} else {
FX_LOGS(WARNING) << "Failed to retrieve any attachments";
}
DataProvider_GetData_Result result;
result.set_response(std::move(response));
callback(std::move(result));
});
executor_.schedule_task(std::move(promise));
}
void DataProviderImpl::GetScreenshot(ImageEncoding encoding,
GetScreenshotCallback callback) {
// We wrap the callback in a shared_ptr to share it between the error handler
// of the FIDL connection and the Scenic::TakeScreenshot() callback.
std::shared_ptr<GetScreenshotCallback> shared_callback =
std::make_shared<GetScreenshotCallback>(std::move(callback));
const uint64_t id = next_scenic_id_++;
scenics_[id] = services_->Connect<fuchsia::ui::scenic::Scenic>();
scenics_[id].set_error_handler(
[this, id, shared_callback](zx_status_t status) {
CloseScenic(id);
FX_LOGS(ERROR) << fxl::StringPrintf(
"Lost connection to Scenic service: %d (%s)", status,
zx_status_get_string(status));
(*shared_callback)(/*screenshot=*/nullptr);
});
scenics_[id]->TakeScreenshot(
// We pass |scenic| to the lambda to keep it alive.
[this, id, encoding, shared_callback](
fuchsia::ui::scenic::ScreenshotData raw_screenshot, bool success) {
CloseScenic(id);
if (!success) {
FX_LOGS(ERROR) << "Scenic failed to take screenshot";
(*shared_callback)(/*screenshot=*/nullptr);
return;
}
std::unique_ptr<Screenshot> screenshot = std::make_unique<Screenshot>();
screenshot->dimensions_in_px.height = raw_screenshot.info.height;
screenshot->dimensions_in_px.width = raw_screenshot.info.width;
switch (encoding) {
case ImageEncoding::PNG:
if (!RawToPng(raw_screenshot.data, raw_screenshot.info.height,
raw_screenshot.info.width, raw_screenshot.info.stride,
raw_screenshot.info.pixel_format,
&screenshot->image)) {
FX_LOGS(ERROR) << "Failed to convert raw screenshot to PNG";
(*shared_callback)(/*screenshot=*/nullptr);
return;
}
break;
}
(*shared_callback)(std::move(screenshot));
});
}
void DataProviderImpl::CloseScenic(const uint64_t id) {
if (scenics_.erase(id) == 0) {
FX_LOGS(ERROR) << "No fuchsia.ui.scenic.Scenic connection to close with id "
<< id;
}
}
} // namespace feedback
} // namespace fuchsia