| // Copyright 2017 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 "peridot/bin/sessionmgr/sessionmgr_impl.h" |
| |
| #include <fcntl.h> |
| #include <memory> |
| #include <string> |
| |
| #include <fuchsia/ledger/cloud/firestore/cpp/fidl.h> |
| #include <fuchsia/ledger/cpp/fidl.h> |
| #include <fuchsia/ledger/internal/cpp/fidl.h> |
| #include <fuchsia/modular/cpp/fidl.h> |
| #include <fuchsia/ui/viewsv1/cpp/fidl.h> |
| #include <lib/component/cpp/connect.h> |
| #include <lib/fsl/io/fd.h> |
| #include <lib/fsl/types/type_converters.h> |
| #include <lib/fxl/files/directory.h> |
| #include <lib/fxl/files/unique_fd.h> |
| #include <lib/fxl/functional/make_copyable.h> |
| #include <lib/fxl/logging.h> |
| #include <lib/fxl/macros.h> |
| |
| #include "peridot/bin/basemgr/cobalt/cobalt.h" |
| #include "peridot/bin/sessionmgr/component_context_impl.h" |
| #include "peridot/bin/sessionmgr/device_map_impl.h" |
| #include "peridot/bin/sessionmgr/focus.h" |
| #include "peridot/bin/sessionmgr/message_queue/message_queue_manager.h" |
| #include "peridot/bin/sessionmgr/presentation_provider.h" |
| #include "peridot/bin/sessionmgr/puppet_master/make_production_impl.h" |
| #include "peridot/bin/sessionmgr/puppet_master/puppet_master_impl.h" |
| #include "peridot/bin/sessionmgr/puppet_master/story_command_executor.h" |
| #include "peridot/bin/sessionmgr/session_ctl.h" |
| #include "peridot/bin/sessionmgr/storage/constants_and_utils.h" |
| #include "peridot/bin/sessionmgr/storage/session_storage.h" |
| #include "peridot/bin/sessionmgr/story_runner/link_impl.h" |
| #include "peridot/bin/sessionmgr/story_runner/story_provider_impl.h" |
| #include "peridot/lib/common/names.h" |
| #include "peridot/lib/common/teardown.h" |
| #include "peridot/lib/common/xdr.h" |
| #include "peridot/lib/device_info/device_info.h" |
| #include "peridot/lib/fidl/array_to_string.h" |
| #include "peridot/lib/fidl/environment.h" |
| #include "peridot/lib/fidl/json_xdr.h" |
| #include "peridot/lib/ledger_client/constants.h" |
| #include "peridot/lib/ledger_client/ledger_client.h" |
| #include "peridot/lib/ledger_client/page_id.h" |
| #include "peridot/lib/ledger_client/status.h" |
| #include "peridot/lib/module_manifest/module_facet_reader_impl.h" |
| |
| namespace modular { |
| |
| namespace { |
| |
| constexpr char kAppId[] = "modular_sessionmgr"; |
| |
| constexpr char kMaxwellComponentNamespace[] = "maxwell"; |
| constexpr char kMaxwellUrl[] = "maxwell"; |
| |
| constexpr char kContextEngineUrl[] = |
| "fuchsia-pkg://fuchsia.com/context_engine#meta/context_engine.cmx"; |
| constexpr char kContextEngineComponentNamespace[] = "context_engine"; |
| |
| constexpr char kModuleResolverUrl[] = |
| "fuchsia-pkg://fuchsia.com/module_resolver#meta/module_resolver.cmx"; |
| |
| constexpr char kUserEnvironmentLabelPrefix[] = "user-"; |
| |
| constexpr char kMessageQueuePath[] = "/data/MESSAGE_QUEUES/v1/"; |
| |
| constexpr char kSessionShellComponentNamespace[] = "user-shell-namespace"; |
| constexpr char kSessionShellLinkName[] = "user-shell-link"; |
| |
| constexpr char kClipboardAgentUrl[] = |
| "fuchsia-pkg://fuchsia.com/clipboard_agent#meta/clipboard_agent.cmx"; |
| |
| constexpr char kLedgerRepositoryDirectory[] = "/data/LEDGER"; |
| |
| // The name in the outgoing debug directory (hub) for developer session control |
| // services. |
| constexpr char kSessionCtlDir[] = "sessionctl"; |
| |
| fuchsia::ledger::cloud::firestore::Config GetLedgerFirestoreConfig() { |
| fuchsia::ledger::cloud::firestore::Config config; |
| config.server_id = kFirebaseProjectId; |
| config.api_key = kFirebaseApiKey; |
| return config; |
| } |
| |
| std::string GetAccountId(const fuchsia::modular::auth::AccountPtr& account) { |
| return !account ? "GUEST" : account->id; |
| } |
| |
| // Creates a function that can be used as termination action passed to AtEnd(), |
| // which when called invokes the reset() method on the object pointed to by the |
| // argument. Used to reset() fidl pointers and std::unique_ptr<>s fields. |
| template <typename X> |
| std::function<void(std::function<void()>)> Reset( |
| std::unique_ptr<X>* const field) { |
| return [field](std::function<void()> cont) { |
| field->reset(); |
| cont(); |
| }; |
| } |
| |
| template <typename X> |
| std::function<void(std::function<void()>)> Reset( |
| fidl::InterfacePtr<X>* const field) { |
| return [field](std::function<void()> cont) { |
| field->Unbind(); |
| cont(); |
| }; |
| } |
| |
| // Creates a function that can be used as termination action passed to AtEnd(), |
| // which when called asynchronously invokes the Teardown() method on the object |
| // pointed to by the argument. Used to teardown AppClient and AsyncHolder |
| // members. |
| template <typename X> |
| std::function<void(std::function<void()>)> Teardown(const zx::duration timeout, |
| const char* const message, |
| X* const field) { |
| return [timeout, message, field](std::function<void()> cont) { |
| field->Teardown(timeout, [message, cont] { |
| if (message) { |
| FXL_DLOG(INFO) << "- " << message << " down."; |
| } |
| cont(); |
| }); |
| }; |
| } |
| |
| } // namespace |
| |
| class SessionmgrImpl::PresentationProviderImpl : public PresentationProvider { |
| public: |
| PresentationProviderImpl(SessionmgrImpl* const impl) : impl_(impl) {} |
| ~PresentationProviderImpl() override = default; |
| |
| private: |
| // |PresentationProvider| |
| void GetPresentation(fidl::StringPtr story_id, |
| fidl::InterfaceRequest<fuchsia::ui::policy::Presentation> |
| request) override { |
| if (impl_->session_shell_app_) { |
| fuchsia::modular::SessionShellPresentationProviderPtr provider; |
| impl_->session_shell_app_->services().ConnectToService( |
| provider.NewRequest()); |
| provider->GetPresentation(std::move(story_id), std::move(request)); |
| } |
| } |
| |
| void WatchVisualState( |
| fidl::StringPtr story_id, |
| fidl::InterfaceHandle<fuchsia::modular::StoryVisualStateWatcher> watcher) |
| override { |
| if (impl_->session_shell_app_) { |
| fuchsia::modular::SessionShellPresentationProviderPtr provider; |
| impl_->session_shell_app_->services().ConnectToService( |
| provider.NewRequest()); |
| provider->WatchVisualState(std::move(story_id), std::move(watcher)); |
| } |
| } |
| |
| SessionmgrImpl* const impl_; |
| }; |
| |
| SessionmgrImpl::SessionmgrImpl(component::StartupContext* const startup_context, |
| const Options& options) |
| : startup_context_(startup_context), |
| options_(options), |
| story_provider_impl_("StoryProviderImpl"), |
| agent_runner_("AgentRunner") { |
| startup_context_->outgoing() |
| .AddPublicService<fuchsia::modular::internal::Sessionmgr>( |
| [this](fidl::InterfaceRequest<fuchsia::modular::internal::Sessionmgr> |
| request) { |
| bindings_.AddBinding(this, std::move(request)); |
| }); |
| } |
| |
| SessionmgrImpl::~SessionmgrImpl() = default; |
| |
| void SessionmgrImpl::Initialize( |
| fuchsia::modular::auth::AccountPtr account, |
| fuchsia::modular::AppConfig session_shell, |
| fuchsia::modular::AppConfig story_shell, |
| fidl::InterfaceHandle<fuchsia::auth::TokenManager> ledger_token_manager, |
| fidl::InterfaceHandle<fuchsia::auth::TokenManager> agent_token_manager, |
| fidl::InterfaceHandle<fuchsia::modular::internal::UserContext> user_context, |
| fidl::InterfaceRequest<fuchsia::ui::viewsv1token::ViewOwner> |
| view_owner_request) { |
| // This is called in the service connection factory callbacks for session |
| // shell (see how RunSessionShell() initializes session_shell_services_) to |
| // lazily initialize the following services only once they are requested |
| // for the first time. |
| finish_initialization_ = fit::defer<fit::closure>([this, |
| session_shell_url = session_shell.url, |
| ledger_token_manager = std::move(ledger_token_manager), |
| story_shell = std::move(story_shell)] () mutable { |
| InitializeLedger(std::move(ledger_token_manager)); |
| InitializeDeviceMap(); |
| InitializeMessageQueueManager(); |
| InitializeMaxwellAndModular(session_shell_url, std::move(story_shell)); |
| ConnectSessionShellToStoryProvider(); |
| AtEnd([this](std::function<void()> cont) { TerminateSessionShell(cont); }); |
| InitializeClipboard(); |
| ReportEvent(ModularEvent::BOOTED_TO_SESSIONMGR); |
| }); |
| |
| InitializeUser(std::move(account), std::move(agent_token_manager), |
| std::move(user_context)); |
| InitializeSessionShell( |
| std::move(session_shell), |
| zx::eventpair(view_owner_request.TakeChannel().release())); |
| } |
| |
| void SessionmgrImpl::ConnectSessionShellToStoryProvider() { |
| fuchsia::modular::SessionShellPtr session_shell; |
| session_shell_app_->services().ConnectToService(session_shell.NewRequest()); |
| story_provider_impl_->SetSessionShell(std::move(session_shell)); |
| } |
| |
| void SessionmgrImpl::InitializeUser( |
| fuchsia::modular::auth::AccountPtr account, |
| fidl::InterfaceHandle<fuchsia::auth::TokenManager> agent_token_manager, |
| fidl::InterfaceHandle<fuchsia::modular::internal::UserContext> |
| user_context) { |
| agent_token_manager_ = agent_token_manager.Bind(); |
| AtEnd(Reset(&agent_token_manager_)); |
| |
| user_context_ = user_context.Bind(); |
| AtEnd(Reset(&user_context_)); |
| |
| account_ = std::move(account); |
| AtEnd(Reset(&account_)); |
| |
| static const auto* const kEnvServices = new std::vector<std::string>{ |
| fuchsia::modular::DeviceMap::Name_, fuchsia::modular::Clipboard::Name_}; |
| user_environment_ = std::make_unique<Environment>( |
| startup_context_->environment(), |
| std::string(kUserEnvironmentLabelPrefix) + GetAccountId(account_), |
| *kEnvServices, |
| /* kill_on_oom = */ true); |
| AtEnd(Reset(&user_environment_)); |
| } |
| |
| zx::channel SessionmgrImpl::GetLedgerRepositoryDirectory() { |
| if (options_.use_memfs_for_ledger) { |
| FXL_DCHECK(!memfs_for_ledger_) |
| << "An existing memfs for the Ledger has already been initialized."; |
| FXL_LOG(INFO) << "Using memfs-backed storage for the ledger."; |
| memfs_for_ledger_ = std::make_unique<scoped_tmpfs::ScopedTmpFS>(); |
| AtEnd(Reset(&memfs_for_ledger_)); |
| |
| return fsl::CloneChannelFromFileDescriptor(memfs_for_ledger_->root_fd()); |
| } |
| if (!files::CreateDirectory(kLedgerRepositoryDirectory)) { |
| FXL_LOG(ERROR) << "Unable to create directory at " |
| << kLedgerRepositoryDirectory; |
| return zx::channel(); |
| } |
| fxl::UniqueFD dir(open(kLedgerRepositoryDirectory, O_PATH)); |
| if (!dir.is_valid()) { |
| FXL_LOG(ERROR) << "Unable to open directory at " |
| << kLedgerRepositoryDirectory << ". errno: " << errno; |
| return zx::channel(); |
| } |
| |
| return fsl::CloneChannelFromFileDescriptor(dir.get()); |
| } |
| |
| void SessionmgrImpl::InitializeLedger( |
| fidl::InterfaceHandle<fuchsia::auth::TokenManager> ledger_token_manager) { |
| fuchsia::modular::AppConfig ledger_config; |
| ledger_config.url = kLedgerAppUrl; |
| |
| ledger_app_ = |
| std::make_unique<AppClient<fuchsia::ledger::internal::LedgerController>>( |
| user_environment_->GetLauncher(), std::move(ledger_config), "", |
| nullptr); |
| ledger_app_->SetAppErrorHandler([this] { |
| FXL_LOG(ERROR) << "Ledger seems to have crashed unexpectedly." << std::endl |
| << "CALLING Logout() DUE TO UNRECOVERABLE LEDGER ERROR."; |
| Logout(); |
| }); |
| AtEnd(Teardown(kBasicTimeout, "Ledger", ledger_app_.get())); |
| |
| fuchsia::ledger::cloud::CloudProviderPtr cloud_provider; |
| std::string ledger_user_id; |
| if (account_ && !options_.no_cloud_provider_for_ledger) { |
| // If not running in Guest mode, spin up a cloud provider for Ledger to use |
| // for syncing. |
| fuchsia::modular::AppConfig cloud_provider_config; |
| cloud_provider_config.url = kCloudProviderFirestoreAppUrl; |
| cloud_provider_app_ = |
| std::make_unique<AppClient<fuchsia::modular::Lifecycle>>( |
| user_environment_->GetLauncher(), std::move(cloud_provider_config)); |
| cloud_provider_app_->services().ConnectToService( |
| cloud_provider_factory_.NewRequest()); |
| |
| cloud_provider = GetCloudProvider(std::move(ledger_token_manager)); |
| ledger_user_id = account_->profile_id; |
| |
| // TODO(mesch): Teardown cloud_provider_app_ ? |
| } |
| |
| ledger_repository_factory_.set_error_handler([this](zx_status_t status) { |
| FXL_LOG(ERROR) << "LedgerRepositoryFactory.GetRepository() failed: " |
| << LedgerEpitaphToString(status) << std::endl |
| << "CALLING Logout() DUE TO UNRECOVERABLE LEDGER ERROR."; |
| Logout(); |
| }); |
| ledger_app_->services().ConnectToService( |
| ledger_repository_factory_.NewRequest()); |
| AtEnd(Reset(&ledger_repository_factory_)); |
| |
| // The directory "/data" is the data root "/data/LEDGER" that the ledger app |
| // client is configured to. |
| ledger_repository_factory_->GetRepository( |
| GetLedgerRepositoryDirectory(), std::move(cloud_provider), ledger_user_id, |
| ledger_repository_.NewRequest()); |
| |
| // If ledger state is erased from underneath us (happens when the cloud store |
| // is cleared), ledger will close the connection to |ledger_repository_|. |
| ledger_repository_.set_error_handler([this](zx_status_t status) { |
| FXL_LOG(ERROR) << "LedgerRepository disconnected with epitaph: " |
| << LedgerEpitaphToString(status) << std::endl |
| << "CALLING Logout() DUE TO UNRECOVERABLE LEDGER ERROR."; |
| Logout(); |
| }); |
| AtEnd(Reset(&ledger_repository_)); |
| |
| ledger_client_ = |
| std::make_unique<LedgerClient>(ledger_repository_.get(), kAppId, [this] { |
| FXL_LOG(ERROR) << "CALLING Logout() DUE TO UNRECOVERABLE LEDGER ERROR."; |
| Logout(); |
| }); |
| AtEnd(Reset(&ledger_client_)); |
| } |
| |
| void SessionmgrImpl::InitializeDeviceMap() { |
| // fuchsia::modular::DeviceMap service |
| const std::string device_id = LoadDeviceID(GetAccountId(account_)); |
| device_name_ = LoadDeviceName(GetAccountId(account_)); |
| const std::string device_profile = LoadDeviceProfile(); |
| |
| device_map_impl_ = std::make_unique<DeviceMapImpl>( |
| device_name_, device_id, device_profile, ledger_client_.get(), |
| fuchsia::ledger::PageId()); |
| user_environment_->AddService<fuchsia::modular::DeviceMap>( |
| [this](fidl::InterfaceRequest<fuchsia::modular::DeviceMap> request) { |
| // device_map_impl_ may be reset before user_environment_. |
| if (device_map_impl_) { |
| device_map_impl_->Connect(std::move(request)); |
| } |
| }); |
| AtEnd(Reset(&device_map_impl_)); |
| } |
| |
| void SessionmgrImpl::InitializeClipboard() { |
| agent_runner_->ConnectToAgent(kAppId, kClipboardAgentUrl, |
| services_from_clipboard_agent_.NewRequest(), |
| clipboard_agent_controller_.NewRequest()); |
| user_environment_->AddService<fuchsia::modular::Clipboard>( |
| [this](fidl::InterfaceRequest<fuchsia::modular::Clipboard> request) { |
| services_from_clipboard_agent_->ConnectToService( |
| fuchsia::modular::Clipboard::Name_, request.TakeChannel()); |
| }); |
| } |
| |
| void SessionmgrImpl::InitializeMessageQueueManager() { |
| std::string message_queue_path = kMessageQueuePath; |
| message_queue_path.append(GetAccountId(account_)); |
| if (!files::CreateDirectory(message_queue_path)) { |
| FXL_LOG(FATAL) << "Failed to create message queue directory: " |
| << message_queue_path; |
| } |
| |
| message_queue_manager_ = std::make_unique<MessageQueueManager>( |
| ledger_client_.get(), MakePageId(kMessageQueuePageId), |
| message_queue_path); |
| AtEnd(Reset(&message_queue_manager_)); |
| } |
| |
| void SessionmgrImpl::InitializeMaxwellAndModular( |
| const fidl::StringPtr& session_shell_url, |
| fuchsia::modular::AppConfig story_shell) { |
| // NOTE: There is an awkward service exchange here between |
| // AgentRunner, StoryProviderImpl, FocusHandler, VisibleStoriesHandler. |
| // |
| // AgentRunner needs a UserIntelligenceProvider. Initializing the |
| // Maxwell process UserIntelligenceProvider requires a ComponentContext. |
| // ComponentContext requires an AgentRunner, which creates a circular |
| // dependency. |
| // |
| // Because of FIDL late bindings, we can get around this by creating a new |
| // InterfaceRequest here (|intelligence_provider_request|), making the |
| // InterfacePtr a valid proxy to be passed to AgentRunner and |
| // StoryProviderImpl, even though it won't be bound to a real implementation |
| // (provided by Maxwell) until later. It works, but it's not a good pattern. |
| |
| fidl::InterfaceHandle<fuchsia::modular::ContextEngine> context_engine; |
| auto context_engine_request = context_engine.NewRequest(); |
| |
| fidl::InterfaceHandle<fuchsia::modular::StoryProvider> story_provider; |
| auto story_provider_request = story_provider.NewRequest(); |
| |
| fidl::InterfaceHandle<fuchsia::modular::FocusProvider> focus_provider_maxwell; |
| auto focus_provider_request_maxwell = focus_provider_maxwell.NewRequest(); |
| |
| fidl::InterfaceHandle<fuchsia::modular::PuppetMaster> puppet_master; |
| auto puppet_master_request = puppet_master.NewRequest(); |
| |
| fidl::InterfaceHandle<fuchsia::modular::VisibleStoriesProvider> |
| visible_stories_provider; |
| auto visible_stories_provider_request = visible_stories_provider.NewRequest(); |
| |
| user_intelligence_provider_impl_.reset(new UserIntelligenceProviderImpl( |
| startup_context_, std::move(context_engine), |
| [this](fidl::InterfaceRequest<fuchsia::modular::VisibleStoriesProvider> |
| request) { |
| visible_stories_handler_->AddProviderBinding(std::move(request)); |
| }, |
| [this](fidl::InterfaceRequest<fuchsia::modular::StoryProvider> request) { |
| story_provider_impl_->Connect(std::move(request)); |
| }, |
| [this](fidl::InterfaceRequest<fuchsia::modular::FocusProvider> request) { |
| focus_handler_->AddProviderBinding(std::move(request)); |
| }, |
| [this](fidl::InterfaceRequest<fuchsia::modular::PuppetMaster> request) { |
| puppet_master_impl_->Connect(std::move(request)); |
| })); |
| AtEnd(Reset(&user_intelligence_provider_impl_)); |
| |
| entity_provider_runner_ = std::make_unique<EntityProviderRunner>( |
| static_cast<EntityProviderLauncher*>(this)); |
| AtEnd(Reset(&entity_provider_runner_)); |
| |
| agent_runner_storage_ = std::make_unique<AgentRunnerStorageImpl>( |
| ledger_client_.get(), MakePageId(kAgentRunnerPageId)); |
| AtEnd(Reset(&agent_runner_storage_)); |
| |
| agent_runner_.reset(new AgentRunner( |
| user_environment_->GetLauncher(), message_queue_manager_.get(), |
| ledger_repository_.get(), agent_runner_storage_.get(), |
| agent_token_manager_.get(), user_intelligence_provider_impl_.get(), |
| entity_provider_runner_.get())); |
| AtEnd(Teardown(kAgentRunnerTimeout, "AgentRunner", &agent_runner_)); |
| |
| maxwell_component_context_bindings_ = std::make_unique< |
| fidl::BindingSet<fuchsia::modular::ComponentContext, |
| std::unique_ptr<ComponentContextImpl>>>(); |
| AtEnd(Reset(&maxwell_component_context_bindings_)); |
| |
| ComponentContextInfo component_context_info{ |
| message_queue_manager_.get(), agent_runner_.get(), |
| ledger_repository_.get(), entity_provider_runner_.get()}; |
| // Start kContextEngineUrl. |
| { |
| context_engine_ns_services_.AddService<fuchsia::modular::ComponentContext>( |
| [this, component_context_info]( |
| fidl::InterfaceRequest<fuchsia::modular::ComponentContext> |
| request) { |
| maxwell_component_context_bindings_->AddBinding( |
| std::make_unique<ComponentContextImpl>( |
| component_context_info, kContextEngineComponentNamespace, |
| kContextEngineUrl, kContextEngineUrl), |
| std::move(request)); |
| }); |
| auto service_list = fuchsia::sys::ServiceList::New(); |
| service_list->names.push_back(fuchsia::modular::ComponentContext::Name_); |
| context_engine_ns_services_.AddBinding(service_list->provider.NewRequest()); |
| |
| fuchsia::modular::AppConfig context_engine_config; |
| context_engine_config.url = kContextEngineUrl; |
| |
| context_engine_app_ = |
| std::make_unique<AppClient<fuchsia::modular::Lifecycle>>( |
| user_environment_->GetLauncher(), std::move(context_engine_config), |
| "" /* data_origin */, std::move(service_list)); |
| context_engine_app_->services().ConnectToService( |
| std::move(context_engine_request)); |
| AtEnd(Reset(&context_engine_app_)); |
| AtEnd(Teardown(kBasicTimeout, "ContextEngine", context_engine_app_.get())); |
| } |
| |
| auto maxwell_app_component_context = |
| maxwell_component_context_bindings_->AddBinding( |
| std::make_unique<ComponentContextImpl>(component_context_info, |
| kMaxwellComponentNamespace, |
| kMaxwellUrl, kMaxwellUrl)); |
| |
| user_intelligence_provider_impl_->StartAgents( |
| std::move(maxwell_app_component_context), |
| options_.session_agents, |
| options_.startup_agents); |
| |
| // Setup for kModuleResolverUrl |
| { |
| module_resolver_ns_services_.AddService< |
| fuchsia::modular::IntelligenceServices>( |
| [this](fidl::InterfaceRequest<fuchsia::modular::IntelligenceServices> |
| request) { |
| fuchsia::modular::ComponentScope component_scope; |
| component_scope.set_global_scope(fuchsia::modular::GlobalScope()); |
| fidl::InterfaceHandle<fuchsia::modular::IntelligenceServices> |
| intelligence_services; |
| if (user_intelligence_provider_impl_) { |
| user_intelligence_provider_impl_->GetComponentIntelligenceServices( |
| std::move(component_scope), std::move(request)); |
| } |
| }); |
| module_resolver_ns_services_.AddService<fuchsia::modular::ComponentContext>( |
| [this, component_context_info]( |
| fidl::InterfaceRequest<fuchsia::modular::ComponentContext> |
| request) { |
| maxwell_component_context_bindings_->AddBinding( |
| std::make_unique<ComponentContextImpl>( |
| component_context_info, kMaxwellComponentNamespace, |
| kModuleResolverUrl, kModuleResolverUrl), |
| std::move(request)); |
| }); |
| auto service_list = fuchsia::sys::ServiceList::New(); |
| service_list->names.push_back( |
| fuchsia::modular::IntelligenceServices::Name_); |
| service_list->names.push_back(fuchsia::modular::ComponentContext::Name_); |
| module_resolver_ns_services_.AddBinding( |
| service_list->provider.NewRequest()); |
| |
| fuchsia::modular::AppConfig module_resolver_config; |
| module_resolver_config.url = kModuleResolverUrl; |
| if (options_.test) { |
| module_resolver_config.args.push_back("--test"); |
| } |
| // For now, we want data_origin to be "", which uses our (parent process's) |
| // /data. This is appropriate for the module_resolver. We can in the future |
| // isolate the data it reads to a subdir of /data and map that in here. |
| module_resolver_app_ = |
| std::make_unique<AppClient<fuchsia::modular::Lifecycle>>( |
| user_environment_->GetLauncher(), std::move(module_resolver_config), |
| "" /* data_origin */, std::move(service_list)); |
| AtEnd(Reset(&module_resolver_app_)); |
| AtEnd(Teardown(kBasicTimeout, "Resolver", module_resolver_app_.get())); |
| } |
| |
| module_resolver_app_->services().ConnectToService( |
| module_resolver_service_.NewRequest()); |
| AtEnd(Reset(&module_resolver_service_)); |
| // End kModuleResolverUrl |
| |
| session_shell_component_context_impl_ = |
| std::make_unique<ComponentContextImpl>( |
| component_context_info, kSessionShellComponentNamespace, |
| session_shell_url, session_shell_url); |
| |
| AtEnd(Reset(&session_shell_component_context_impl_)); |
| |
| fidl::InterfacePtr<fuchsia::modular::FocusProvider> |
| focus_provider_story_provider; |
| auto focus_provider_request_story_provider = |
| focus_provider_story_provider.NewRequest(); |
| |
| presentation_provider_impl_ = |
| std::make_unique<PresentationProviderImpl>(this); |
| AtEnd(Reset(&presentation_provider_impl_)); |
| |
| // We create |story_provider_impl_| after |agent_runner_| so |
| // story_provider_impl_ is terminated before agent_runner_, which will cause |
| // all modules to be terminated before agents are terminated. Agents must |
| // outlive the stories which contain modules that are connected to those |
| // agents. |
| session_storage_ = std::make_unique<SessionStorage>( |
| ledger_client_.get(), fuchsia::ledger::PageId()); |
| |
| module_facet_reader_.reset(new ModuleFacetReaderImpl( |
| startup_context_->ConnectToEnvironmentService<fuchsia::sys::Loader>())); |
| |
| story_provider_impl_.reset(new StoryProviderImpl( |
| user_environment_.get(), device_map_impl_->current_device_id(), |
| session_storage_.get(), std::move(story_shell), component_context_info, |
| std::move(focus_provider_story_provider), |
| user_intelligence_provider_impl_.get(), module_resolver_service_.get(), |
| entity_provider_runner_.get(), module_facet_reader_.get(), |
| presentation_provider_impl_.get(), |
| startup_context_ |
| ->ConnectToEnvironmentService<fuchsia::ui::viewsv1::ViewSnapshot>(), |
| options_.test)); |
| story_provider_impl_->Connect(std::move(story_provider_request)); |
| |
| AtEnd( |
| Teardown(kStoryProviderTimeout, "StoryProvider", &story_provider_impl_)); |
| |
| fuchsia::modular::FocusProviderPtr focus_provider_puppet_master; |
| auto focus_provider_request_puppet_master = |
| focus_provider_puppet_master.NewRequest(); |
| fuchsia::modular::StoryProviderPtr story_provider_puppet_master; |
| auto story_provider_puppet_master_request = |
| story_provider_puppet_master.NewRequest(); |
| |
| // Initialize the PuppetMaster. |
| // TODO(miguelfrde): there's no clean runtime interface we can inject to |
| // puppet master. Hence, for now we inject this function to be able to focus |
| // mods. Eventually we want to have a StoryRuntime and SessionRuntime classes |
| // similar to Story/SessionStorage but for runtime management. |
| auto module_focuser = |
| [story_provider = std::move(story_provider_puppet_master)]( |
| std::string story_id, std::vector<std::string> mod_name) { |
| fuchsia::modular::StoryControllerPtr story_controller; |
| story_provider->GetController(story_id, story_controller.NewRequest()); |
| |
| fuchsia::modular::ModuleControllerPtr module_controller; |
| story_controller->GetModuleController(std::move(mod_name), |
| module_controller.NewRequest()); |
| module_controller->Focus(); |
| }; |
| AtEnd(Reset(&session_storage_)); |
| story_command_executor_ = MakeProductionStoryCommandExecutor( |
| session_storage_.get(), std::move(focus_provider_puppet_master), |
| module_resolver_service_.get(), entity_provider_runner_.get(), |
| std::move(module_focuser)); |
| story_provider_impl_->Connect( |
| std::move(story_provider_puppet_master_request)); |
| puppet_master_impl_ = std::make_unique<PuppetMasterImpl>( |
| session_storage_.get(), story_command_executor_.get()); |
| puppet_master_impl_->Connect(std::move(puppet_master_request)); |
| |
| session_ctl_ = |
| std::make_unique<SessionCtl>(startup_context_->outgoing().debug_dir(), |
| kSessionCtlDir, puppet_master_impl_.get()); |
| |
| AtEnd(Reset(&story_command_executor_)); |
| AtEnd(Reset(&puppet_master_impl_)); |
| AtEnd(Reset(&session_ctl_)); |
| |
| focus_handler_ = std::make_unique<FocusHandler>( |
| device_map_impl_->current_device_id(), ledger_client_.get(), |
| fuchsia::ledger::PageId()); |
| focus_handler_->AddProviderBinding(std::move(focus_provider_request_maxwell)); |
| focus_handler_->AddProviderBinding( |
| std::move(focus_provider_request_story_provider)); |
| focus_handler_->AddProviderBinding( |
| std::move(focus_provider_request_puppet_master)); |
| |
| visible_stories_handler_ = std::make_unique<VisibleStoriesHandler>(); |
| visible_stories_handler_->AddProviderBinding( |
| std::move(visible_stories_provider_request)); |
| |
| AtEnd(Reset(&focus_handler_)); |
| AtEnd(Reset(&visible_stories_handler_)); |
| } |
| |
| void SessionmgrImpl::InitializeSessionShell( |
| fuchsia::modular::AppConfig session_shell, zx::eventpair view_token) { |
| // We setup our own view and make the fuchsia::modular::SessionShell a child |
| // of it. |
| auto scenic = |
| startup_context_ |
| ->ConnectToEnvironmentService<fuchsia::ui::scenic::Scenic>(); |
| scenic::ViewContext view_context = { |
| .session_and_listener_request = |
| scenic::CreateScenicSessionPtrAndListenerRequest(scenic.get()), |
| .view_token = std::move(view_token), |
| .startup_context = startup_context_, |
| }; |
| session_shell_view_host_ = |
| std::make_unique<ViewHost>(std::move(view_context)); |
| RunSessionShell(std::move(session_shell)); |
| } |
| |
| void SessionmgrImpl::RunSessionShell( |
| fuchsia::modular::AppConfig session_shell_config) { |
| // |session_shell_services_| is a ServiceProvider (aka a Directory) that will |
| // be used to augment the session shell's namespace. |
| session_shell_services_.AddService<fuchsia::modular::SessionShellContext>( |
| [this](fidl::InterfaceRequest<fuchsia::modular::SessionShellContext> |
| request) { |
| finish_initialization_.call(); |
| session_shell_context_bindings_.AddBinding(this, std::move(request)); |
| }); |
| session_shell_services_.AddService<fuchsia::modular::ComponentContext>( |
| [this]( |
| fidl::InterfaceRequest<fuchsia::modular::ComponentContext> request) { |
| finish_initialization_.call(); |
| session_shell_component_context_impl_->Connect(std::move(request)); |
| }); |
| session_shell_services_.AddService<fuchsia::modular::PuppetMaster>( |
| [this](fidl::InterfaceRequest<fuchsia::modular::PuppetMaster> request) { |
| finish_initialization_.call(); |
| puppet_master_impl_->Connect(std::move(request)); |
| }); |
| session_shell_services_.AddService<fuchsia::modular::IntelligenceServices>( |
| [this](fidl::InterfaceRequest<fuchsia::modular::IntelligenceServices> |
| request) { |
| finish_initialization_.call(); |
| fuchsia::modular::ComponentScope component_scope; |
| component_scope.set_global_scope(fuchsia::modular::GlobalScope()); |
| user_intelligence_provider_impl_->GetComponentIntelligenceServices( |
| std::move(component_scope), std::move(request)); |
| }); |
| // |session_shell_service_provider_| is an InterfacePtr impl for |
| // ServiceProvider that binds to |session_shell_services_|. |
| fuchsia::sys::ServiceProviderPtr session_shell_service_provider_ptr_; |
| session_shell_services_.AddBinding( |
| session_shell_service_provider_ptr_.NewRequest()); |
| |
| // |service_list| specifies which services are available to the child |
| // component from which ServiceProvider. There is a lot of indirection here. |
| auto service_list = fuchsia::sys::ServiceList::New(); |
| service_list->names.push_back(fuchsia::modular::ComponentContext::Name_); |
| service_list->names.push_back(fuchsia::modular::IntelligenceServices::Name_); |
| service_list->names.push_back(fuchsia::modular::PuppetMaster::Name_); |
| service_list->names.push_back(fuchsia::modular::SessionShellContext::Name_); |
| service_list->provider = std::move(session_shell_service_provider_ptr_); |
| |
| session_shell_app_ = std::make_unique<AppClient<fuchsia::modular::Lifecycle>>( |
| user_environment_->GetLauncher(), std::move(session_shell_config), |
| /* data_origin = */ "", std::move(service_list)); |
| |
| session_shell_app_->SetAppErrorHandler([this] { |
| FXL_LOG(ERROR) << "Session Shell seems to have crashed unexpectedly." |
| << " Logging out."; |
| Logout(); |
| }); |
| |
| fuchsia::ui::viewsv1token::ViewOwnerPtr view_owner; |
| fuchsia::ui::viewsv1::ViewProviderPtr view_provider; |
| session_shell_app_->services().ConnectToService(view_provider.NewRequest()); |
| view_provider->CreateView(view_owner.NewRequest(), nullptr); |
| session_shell_view_host_->ConnectView(std::move(view_owner)); |
| } |
| |
| void SessionmgrImpl::TerminateSessionShell(const std::function<void()>& done) { |
| session_shell_app_->Teardown(kBasicTimeout, [this, done] { |
| session_shell_app_.reset(); |
| done(); |
| }); |
| } |
| |
| class SessionmgrImpl::SwapSessionShellOperation : public Operation<> { |
| public: |
| SwapSessionShellOperation(SessionmgrImpl* const sessionmgr_impl, |
| fuchsia::modular::AppConfig session_shell_config, |
| ResultCall result_call) |
| : Operation("SessionmgrImpl::SwapSessionShellOperation", |
| std::move(result_call)), |
| sessionmgr_impl_(sessionmgr_impl), |
| session_shell_config_(std::move(session_shell_config)) {} |
| |
| private: |
| void Run() override { |
| FlowToken flow{this}; |
| sessionmgr_impl_->story_provider_impl_->StopAllStories([this, flow] { |
| sessionmgr_impl_->TerminateSessionShell([this, flow] { |
| sessionmgr_impl_->RunSessionShell(std::move(session_shell_config_)); |
| sessionmgr_impl_->ConnectSessionShellToStoryProvider(); |
| }); |
| }); |
| } |
| |
| SessionmgrImpl* const sessionmgr_impl_; |
| fuchsia::modular::AppConfig session_shell_config_; |
| |
| FXL_DISALLOW_COPY_AND_ASSIGN(SwapSessionShellOperation); |
| }; |
| |
| void SessionmgrImpl::SwapSessionShell( |
| fuchsia::modular::AppConfig session_shell_config, |
| SwapSessionShellCallback callback) { |
| operation_queue_.Add(new SwapSessionShellOperation( |
| this, std::move(session_shell_config), callback)); |
| } |
| |
| void SessionmgrImpl::Terminate(std::function<void()> done) { |
| FXL_LOG(INFO) << "Sessionmgr::Terminate()"; |
| at_end_done_ = std::move(done); |
| |
| TerminateRecurse(at_end_.size() - 1); |
| } |
| |
| void SessionmgrImpl::GetAccount( |
| std::function<void(::std::unique_ptr<::fuchsia::modular::auth::Account>)> |
| callback) { |
| callback(fidl::Clone(account_)); |
| } |
| |
| void SessionmgrImpl::GetAgentProvider( |
| fidl::InterfaceRequest<fuchsia::modular::AgentProvider> request) { |
| agent_runner_->Connect(std::move(request)); |
| } |
| |
| void SessionmgrImpl::GetComponentContext( |
| fidl::InterfaceRequest<fuchsia::modular::ComponentContext> request) { |
| session_shell_component_context_impl_->Connect(std::move(request)); |
| } |
| |
| void SessionmgrImpl::GetDeviceName( |
| std::function<void(::std::string)> callback) { |
| callback(device_name_); |
| } |
| |
| void SessionmgrImpl::GetFocusController( |
| fidl::InterfaceRequest<fuchsia::modular::FocusController> request) { |
| focus_handler_->AddControllerBinding(std::move(request)); |
| } |
| |
| void SessionmgrImpl::GetFocusProvider( |
| fidl::InterfaceRequest<fuchsia::modular::FocusProvider> request) { |
| focus_handler_->AddProviderBinding(std::move(request)); |
| } |
| |
| void SessionmgrImpl::GetLink( |
| fidl::InterfaceRequest<fuchsia::modular::Link> request) { |
| if (!session_shell_storage_) { |
| session_shell_storage_ = std::make_unique<StoryStorage>( |
| ledger_client_.get(), fuchsia::ledger::PageId()); |
| } |
| |
| fuchsia::modular::LinkPath link_path; |
| link_path.module_path.resize(0); |
| link_path.link_name = kSessionShellLinkName; |
| auto impl = std::make_unique<LinkImpl>(session_shell_storage_.get(), |
| std::move(link_path)); |
| session_shell_link_bindings_.AddBinding(std::move(impl), std::move(request)); |
| } |
| |
| void SessionmgrImpl::GetPresentation( |
| fidl::InterfaceRequest<fuchsia::ui::policy::Presentation> request) { |
| user_context_->GetPresentation(std::move(request)); |
| } |
| |
| void SessionmgrImpl::GetSpeechToText( |
| fidl::InterfaceRequest<fuchsia::speech::SpeechToText> request) { |
| user_intelligence_provider_impl_->GetSpeechToText(std::move(request)); |
| } |
| |
| void SessionmgrImpl::GetStoryProvider( |
| fidl::InterfaceRequest<fuchsia::modular::StoryProvider> request) { |
| story_provider_impl_->Connect(std::move(request)); |
| } |
| |
| void SessionmgrImpl::GetSuggestionProvider( |
| fidl::InterfaceRequest<fuchsia::modular::SuggestionProvider> request) { |
| user_intelligence_provider_impl_->GetSuggestionProvider(std::move(request)); |
| } |
| |
| void SessionmgrImpl::GetVisibleStoriesController( |
| fidl::InterfaceRequest<fuchsia::modular::VisibleStoriesController> |
| request) { |
| visible_stories_handler_->AddControllerBinding(std::move(request)); |
| } |
| |
| void SessionmgrImpl::Logout() { user_context_->Logout(); } |
| |
| // |EntityProviderLauncher| |
| void SessionmgrImpl::ConnectToEntityProvider( |
| const std::string& agent_url, |
| fidl::InterfaceRequest<fuchsia::modular::EntityProvider> |
| entity_provider_request, |
| fidl::InterfaceRequest<fuchsia::modular::AgentController> |
| agent_controller_request) { |
| FXL_DCHECK(agent_runner_.get()); |
| agent_runner_->ConnectToEntityProvider(agent_url, |
| std::move(entity_provider_request), |
| std::move(agent_controller_request)); |
| } |
| |
| void SessionmgrImpl::ConnectToStoryEntityProvider( |
| const std::string& story_id, |
| fidl::InterfaceRequest<fuchsia::modular::EntityProvider> |
| entity_provider_request) { |
| story_provider_impl_->ConnectToStoryEntityProvider( |
| story_id, std::move(entity_provider_request)); |
| } |
| |
| fuchsia::ledger::cloud::CloudProviderPtr SessionmgrImpl::GetCloudProvider( |
| fidl::InterfaceHandle<fuchsia::auth::TokenManager> ledger_token_manager) { |
| FXL_CHECK(ledger_token_manager); |
| |
| fuchsia::ledger::cloud::CloudProviderPtr cloud_provider; |
| auto cloud_provider_config = GetLedgerFirestoreConfig(); |
| |
| cloud_provider_factory_->GetCloudProvider( |
| std::move(cloud_provider_config), std::move(ledger_token_manager), |
| cloud_provider.NewRequest(), [](fuchsia::ledger::cloud::Status status) { |
| if (status != fuchsia::ledger::cloud::Status::OK) { |
| FXL_LOG(ERROR) << "Failed to create a cloud provider: " |
| << fidl::ToUnderlying(status); |
| } |
| }); |
| return cloud_provider; |
| } |
| |
| void SessionmgrImpl::AtEnd(std::function<void(std::function<void()>)> action) { |
| at_end_.emplace_back(std::move(action)); |
| } |
| |
| void SessionmgrImpl::TerminateRecurse(const int i) { |
| if (i >= 0) { |
| at_end_[i]([this, i] { TerminateRecurse(i - 1); }); |
| } else { |
| FXL_LOG(INFO) << "Sessionmgr::Terminate(): done"; |
| at_end_done_(); |
| } |
| } |
| |
| } // namespace modular |