blob: bfeaa4e13aaa95441aeb1939799fabb5753a07b2 [file] [log] [blame]
// Copyright 2020 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/forensics/feedback_data/device_id_provider.h"
#include <lib/syslog/cpp/macros.h>
#include <memory>
#include <optional>
#include <tuple>
#include <utility>
#include "src/developer/forensics/utils/errors.h"
#include "src/lib/files/directory.h"
#include "src/lib/files/file.h"
#include "src/lib/fxl/strings/string_printf.h"
#include "src/lib/uuid/uuid.h"
namespace forensics {
namespace feedback_data {
namespace {
// Server for |fuchsia.feedback.DeviceIdProvider| that only responds to the first call to GetId as
// the ID never changes and the method is a hanging get.
class DeviceIdProvider : public fuchsia::feedback::DeviceIdProvider {
public:
DeviceIdProvider(async_dispatcher_t* dispatcher,
::fidl::InterfaceRequest<fuchsia::feedback::DeviceIdProvider> request,
::fit::function<void(zx_status_t)> on_channel_close, std::string* device_id)
: connection_(this, std::move(request), dispatcher),
has_been_called_(false),
device_id_(device_id) {
connection_.set_error_handler(std::move(on_channel_close));
}
// |fuchsia.feedback.DeviceIdProvider|
void GetId(GetIdCallback callback) override {
// This is the second call on this connection and we want to leave it hanging forever.
if (has_been_called_) {
return;
}
callback(*device_id_);
has_been_called_ = true;
}
private:
::fidl::Binding<fuchsia::feedback::DeviceIdProvider> connection_;
bool has_been_called_;
std::string* device_id_;
};
// Reads a device id from the file at |path|. If the device id doesn't exist or is invalid, return
// a nullopt.
std::optional<std::string> ReadDeviceId(const std::string& path) {
std::string id;
if (!files::ReadFileToString(path, &id)) {
return std::nullopt;
}
return id;
}
// Creates a new device id and stores it at |path|.
//
// The id is a 128-bit (pseudo) random UUID in the form of version 4 as described in RFC 4122,
// section 4.4.
std::string InitializeDeviceId(const std::string& path) {
if (const auto read_id = ReadDeviceId(path);
read_id.has_value() && uuid::IsValid(read_id.value())) {
return read_id.value();
}
std::string new_id = uuid::Generate();
if (!files::WriteFile(path, new_id.c_str(), new_id.size())) {
FX_LOGS(ERROR) << fxl::StringPrintf("Cannot write device id '%s' to '%s'", new_id.c_str(),
path.c_str());
}
FX_LOGS(INFO) << "Created new feedback device id";
return new_id;
}
} // namespace
DeviceIdManager::DeviceIdManager(async_dispatcher_t* dispatcher, const std::string& path)
: dispatcher_(dispatcher), device_id_(InitializeDeviceId(path)), next_provider_idx_(0u) {}
void DeviceIdManager::AddBinding(
::fidl::InterfaceRequest<fuchsia::feedback::DeviceIdProvider> request,
::fit::function<void(zx_status_t)> on_channel_close) {
const size_t idx = next_provider_idx_++;
providers_.emplace(
idx,
std::make_unique<DeviceIdProvider>(
dispatcher_, std::move(request),
[this, idx, on_channel_close = std::move(on_channel_close)](const zx_status_t status) {
// Execute |on_channel_close| before removing the created DeviceIdProvider from
// |providers_|.
on_channel_close(status);
providers_.erase(idx);
},
&device_id_));
}
} // namespace feedback_data
} // namespace forensics