blob: 7b499d20d6076cf2fabeca34a98003f3f7f665c1 [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/feedback/feedback_data/device_id_provider.h"
#include <optional>
#include "src/developer/feedback/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/syslog/cpp/logger.h"
#include "src/lib/uuid/uuid.h"
namespace feedback {
namespace {
using Response = fuchsia::feedback::DeviceIdProvider_GetId_Response;
using Result = fuchsia::feedback::DeviceIdProvider_GetId_Result;
using DeviceIdError = fuchsia::feedback::DeviceIdError;
// Reads a device id from the file at |path|. If the device id doesn't exist or is invalid, return
// a nullopt.
AnnotationOr ReadDeviceId(const std::string& path) {
std::string id;
if (!files::ReadFileToString(path, &id)) {
return AnnotationOr(Error::kFileReadFailure);
}
return (uuid::IsValid(id)) ? AnnotationOr(id) : AnnotationOr(Error::kBadValue);
}
// Creates a new device id and stores it at |path| if the file doesn't exist or contains an
// invalid id.
//
// The id is a 128-bit (pseudo) random UUID in the form of version 4 as described in RFC 4122,
// section 4.4.
AnnotationOr InitializeDeviceId(const std::string& path) {
if (files::IsDirectory(path)) {
FX_LOGS(ERROR) << fxl::StringPrintf("Unable to initialize feedback id, '%s' is a directory",
path.c_str());
return AnnotationOr(Error::kFileReadFailure);
}
if (const AnnotationOr read_id = ReadDeviceId(path); read_id.HasValue()) {
return AnnotationOr(read_id);
}
std::string new_id = uuid::Generate();
if (!uuid::IsValid(new_id)) {
FX_LOGS(ERROR) << fxl::StringPrintf("%s is not a valid feedback id", new_id.c_str());
return AnnotationOr(Error::kBadValue);
}
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());
return AnnotationOr(Error::kFileWriteFailure);
}
return AnnotationOr(new_id);
}
} // namespace
DeviceIdProvider::DeviceIdProvider(const std::string& path)
: device_id_(InitializeDeviceId(path)) {}
AnnotationOr DeviceIdProvider::GetId() { return device_id_; }
void DeviceIdProvider::GetId(GetIdCallback callback) {
Result result;
if (device_id_.HasValue()) {
// We need to copy |device_id_| since Response::Response() requires a rvalue reference to
// std::string in its constructor.
Response response(std::string(device_id_.Value()));
result = Result::WithResponse(std::move(response));
} else {
result = Result::WithErr(DeviceIdError::NOT_FOUND);
}
callback(std::move(result));
}
} // namespace feedback