blob: 2faeeba799e82a063863821c77f0950974a891e0 [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/modular/bin/sessionmgr/element_manager_impl.h"
#include <lib/syslog/cpp/macros.h>
#include "src/lib/uuid/uuid.h"
#include "src/modular/bin/sessionmgr/annotations.h"
#include "src/modular/bin/sessionmgr/element_controller_impl.h"
namespace modular {
namespace {
// Prefix for all story names created for each proposed element.
constexpr auto kElementStoryNamePrefix = "element-";
// Name of the single module in an element story, which represents the element.
constexpr auto kElementModuleName = "element";
// Returns a unique story name for a newly proposed element.
std::string GenerateStoryName() {
return kElementStoryNamePrefix + uuid::Uuid::Generate().ToString();
};
} // namespace
ElementManagerImpl::ElementManagerImpl(SessionStorage* const session_storage)
: session_storage_(session_storage), weak_factory_(this) {
FX_DCHECK(session_storage_ != nullptr);
session_storage_->SubscribeStoryDeleted(
[weak_ptr = weak_factory_.GetWeakPtr()](std::string story_id) {
if (!weak_ptr) {
return modular::WatchInterest::kStop;
}
weak_ptr->OnStoryStorageDeleted(std::move(story_id));
return modular::WatchInterest::kContinue;
});
}
void ElementManagerImpl::Connect(fidl::InterfaceRequest<fuchsia::element::Manager> request) {
bindings_.AddBinding(this, std::move(request));
}
void ElementManagerImpl::ProposeElement(
fuchsia::element::Spec spec,
fidl::InterfaceRequest<fuchsia::element::Controller> element_controller,
ProposeElementCallback callback) {
// Component URL is required.
if (!spec.has_component_url()) {
fuchsia::element::Manager_ProposeElement_Result result;
result.set_err(fuchsia::element::ProposeElementError::NOT_FOUND);
callback(std::move(result));
return;
}
// When |spec.additional_services| is provided, it must have a valid |host_directory| channel,
// and must not have a valid |provider|.
if (spec.has_additional_services() && (!spec.additional_services().host_directory.is_valid() ||
spec.additional_services().provider.is_valid())) {
fuchsia::element::Manager_ProposeElement_Result result;
result.set_err(fuchsia::element::ProposeElementError::INVALID_ARGS);
callback(std::move(result));
return;
}
std::vector<fuchsia::modular::Annotation> annotations;
if (spec.has_annotations()) {
annotations = element::annotations::ToModularAnnotations(spec.annotations());
}
auto story_id = session_storage_->CreateStory(GenerateStoryName(), std::move(annotations));
// The story should not exist because it was created with a unique name.
FX_DCHECK(element_controllers_.find(story_id) == element_controllers_.end());
// Create an |ElementControllerImpl| for this element, even if the proposer did not request
// an ElementController. The map entry is used to keep track of element stories.
auto element_controller_impl =
std::make_unique<ElementControllerImpl>(story_id, session_storage_);
if (element_controller.is_valid()) {
element_controller_impl->Connect(std::move(element_controller));
}
element_controllers_[story_id] = std::move(element_controller_impl);
auto story_storage = session_storage_->GetStoryStorage(story_id);
// Add the element to the story as a module.
auto module_data = CreateElementModuleData(std::move(spec));
story_storage->WriteModuleData(std::move(module_data));
fuchsia::element::Manager_ProposeElement_Result result;
result.set_response({});
callback(std::move(result));
}
void ElementManagerImpl::OnStoryStorageDeleted(std::string story_id) {
// Do nothing if this is not an element story.
if (element_controllers_.find(story_id) == element_controllers_.end()) {
return;
}
element_controllers_.erase(story_id);
}
fuchsia::modular::ModuleData ElementManagerImpl::CreateElementModuleData(
fuchsia::element::Spec spec) {
fuchsia::modular::ModuleData module_data;
module_data.set_module_url(spec.component_url());
module_data.mutable_module_path()->push_back(kElementModuleName);
module_data.set_module_source(fuchsia::modular::ModuleSource::EXTERNAL);
module_data.set_module_deleted(false);
module_data.set_is_embedded(false);
if (spec.has_additional_services()) {
module_data.set_additional_services(std::move(*spec.mutable_additional_services()));
}
fuchsia::modular::Intent intent;
intent.handler = spec.component_url();
module_data.set_intent(std::move(intent));
return module_data;
}
} // namespace modular