| // Copyright 2018 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/basemgr/basemgr_impl.h" |
| |
| #include <fuchsia/ui/app/cpp/fidl.h> |
| #include <lib/fidl/cpp/type_converter.h> |
| #include <lib/fsl/types/type_converters.h> |
| #include <lib/ui/scenic/cpp/view_token_pair.h> |
| #include <src/lib/fxl/logging.h> |
| |
| #include "peridot/bin/basemgr/basemgr_settings.h" |
| #include "peridot/bin/basemgr/session_provider.h" |
| #include "peridot/bin/basemgr/wait_for_minfs.h" |
| #include "peridot/lib/common/async_holder.h" |
| #include "peridot/lib/common/teardown.h" |
| #include "peridot/lib/fidl/app_client.h" |
| #include "peridot/lib/fidl/clone.h" |
| |
| namespace fidl { |
| template <> |
| // fidl::TypeConverter specialization for fuchsia::modular::session::AppConfig |
| // TODO(MF-277) Convert all usages of fuchsia::modular::AppConfig to |
| // fuchsia::modular::session::AppConfig and remove this converter. |
| struct TypeConverter<fuchsia::modular::AppConfig, |
| fuchsia::modular::session::AppConfig> { |
| // Converts fuchsia::modular::session::AppConfig to |
| // fuchsia::modular::AppConfig |
| static fuchsia::modular::AppConfig Convert( |
| const fuchsia::modular::session::AppConfig& config) { |
| fuchsia::modular::AppConfig app_config; |
| app_config.url = config.url().c_str(); |
| |
| if (config.has_args()) { |
| app_config.args = fidl::To<fidl::VectorPtr<std::string>>(config.args()); |
| } |
| |
| return app_config; |
| } |
| }; |
| } // namespace fidl |
| |
| namespace modular { |
| |
| namespace { |
| |
| // TODO(MF-134): This key is duplicated in |
| // topaz/lib/settings/lib/device_info.dart. Remove this key once factory reset |
| // is provided to topaz as a service. |
| // The key for factory reset toggles. |
| constexpr char kFactoryResetKey[] = "FactoryReset"; |
| |
| constexpr char kTokenManagerFactoryUrl[] = |
| "fuchsia-pkg://fuchsia.com/token_manager_factory#meta/" |
| "token_manager_factory.cmx"; |
| |
| } // namespace |
| |
| BasemgrImpl::BasemgrImpl( |
| fuchsia::modular::session::BasemgrConfig config, |
| fuchsia::sys::Launcher* const launcher, |
| fuchsia::ui::policy::PresenterPtr presenter, |
| fuchsia::devicesettings::DeviceSettingsManagerPtr device_settings_manager, |
| fuchsia::wlan::service::WlanPtr wlan, |
| fuchsia::auth::account::AccountManagerPtr account_manager, |
| fit::function<void()> on_shutdown) |
| : config_(std::move(config)), |
| launcher_(launcher), |
| presenter_(std::move(presenter)), |
| device_settings_manager_(std::move(device_settings_manager)), |
| wlan_(std::move(wlan)), |
| account_manager_(std::move(account_manager)), |
| on_shutdown_(std::move(on_shutdown)), |
| base_shell_context_binding_(this), |
| authentication_context_provider_binding_(this), |
| session_provider_("SessionProvider") { |
| UpdateSessionShellConfig(); |
| |
| Start(); |
| } |
| |
| BasemgrImpl::~BasemgrImpl() = default; |
| |
| void BasemgrImpl::Connect( |
| fidl::InterfaceRequest<fuchsia::modular::internal::BasemgrDebug> request) { |
| basemgr_bindings_.AddBinding(this, std::move(request)); |
| } |
| |
| void BasemgrImpl::StartBaseShell() { |
| if (base_shell_running_) { |
| FXL_DLOG(INFO) << "StartBaseShell() called when already running"; |
| |
| return; |
| } |
| |
| auto base_shell_config = |
| fidl::To<fuchsia::modular::AppConfig>(config_.base_shell().app_config()); |
| base_shell_app_ = std::make_unique<AppClient<fuchsia::modular::Lifecycle>>( |
| launcher_, std::move(base_shell_config)); |
| |
| auto [view_token, view_holder_token] = scenic::ViewTokenPair::New(); |
| |
| fuchsia::ui::app::ViewProviderPtr base_shell_view_provider; |
| base_shell_app_->services().ConnectToService( |
| base_shell_view_provider.NewRequest()); |
| base_shell_view_provider->CreateView(std::move(view_token.value), nullptr, |
| nullptr); |
| |
| presentation_container_ = std::make_unique<PresentationContainer>( |
| presenter_.get(), std::move(view_holder_token), |
| /* shell_config= */ GetActiveSessionShellConfig(), |
| /* on_swap_session_shell= */ [this] { |
| SelectNextSessionShell(/* callback= */ [] {}); |
| }); |
| |
| // TODO(alexmin): Remove BaseShellParams. |
| fuchsia::modular::BaseShellParams params; |
| base_shell_app_->services().ConnectToService(base_shell_.NewRequest()); |
| base_shell_->Initialize(base_shell_context_binding_.NewBinding(), |
| /* base_shell_params= */ std::move(params)); |
| |
| base_shell_running_ = true; |
| } |
| |
| FuturePtr<> BasemgrImpl::StopBaseShell() { |
| if (!base_shell_running_) { |
| FXL_DLOG(INFO) << "StopBaseShell() called when already stopped"; |
| |
| return Future<>::CreateCompleted("StopBaseShell::Completed"); |
| } |
| |
| auto did_stop = Future<>::Create("StopBaseShell"); |
| |
| base_shell_app_->Teardown(kBasicTimeout, [did_stop, this] { |
| FXL_DLOG(INFO) << "- fuchsia::modular::BaseShell down"; |
| |
| base_shell_running_ = false; |
| did_stop->Complete(); |
| }); |
| |
| return did_stop; |
| } |
| |
| FuturePtr<> BasemgrImpl::StopTokenManagerFactoryApp() { |
| if (!token_manager_factory_app_) { |
| FXL_DLOG(INFO) |
| << "StopTokenManagerFactoryApp() called when already stopped"; |
| |
| return Future<>::CreateCompleted("StopTokenManagerFactoryApp::Completed"); |
| } |
| |
| auto did_stop = Future<>::Create("StopTokenManagerFactoryApp"); |
| |
| token_manager_factory_app_->Teardown(kBasicTimeout, [did_stop, this] { |
| FXL_DLOG(INFO) << "- fuchsia::auth::TokenManagerFactory down"; |
| |
| token_manager_factory_app_.release(); |
| did_stop->Complete(); |
| }); |
| |
| return did_stop; |
| } |
| |
| void BasemgrImpl::Start() { |
| if (config_.test()) { |
| // Print test banner. |
| FXL_LOG(INFO) |
| << std::endl |
| << std::endl |
| << "======================== Starting Test [" << config_.test_name() |
| << "]" << std::endl |
| << "============================================================" |
| << std::endl; |
| } |
| |
| // Wait for persistent data to come up. |
| if (config_.use_minfs()) { |
| WaitForMinfs(); |
| } |
| |
| auto sessionmgr_config = |
| fidl::To<fuchsia::modular::AppConfig>(config_.sessionmgr()); |
| auto story_shell_config = |
| fidl::To<fuchsia::modular::AppConfig>(config_.story_shell().app_config()); |
| session_provider_.reset(new SessionProvider( |
| /* delegate= */ this, launcher_, std::move(sessionmgr_config), |
| CloneStruct(session_shell_config_), std::move(story_shell_config), |
| config_.use_session_shell_for_story_shell_factory(), |
| /* on_zero_sessions= */ |
| [this] { |
| if (config_.base_shell().keep_alive_after_login()) { |
| // TODO(MI4-1117): Integration tests currently |
| // expect base shell to always be running. So, if |
| // we're running under a test, DidLogin() will not |
| // shut down the base shell after login; thus this |
| // method doesn't need to re-start the base shell |
| // after a logout. |
| return; |
| } |
| |
| FXL_DLOG(INFO) << "Re-starting due to logout"; |
| ShowSetupOrLogin(); |
| })); |
| |
| InitializeUserProvider(); |
| |
| ReportEvent(ModularEvent::BOOTED_TO_BASEMGR); |
| } |
| |
| void BasemgrImpl::InitializeUserProvider() { |
| token_manager_factory_app_.release(); |
| fuchsia::modular::AppConfig token_manager_config; |
| token_manager_config.url = kTokenManagerFactoryUrl; |
| token_manager_factory_app_ = |
| std::make_unique<AppClient<fuchsia::modular::Lifecycle>>( |
| launcher_, CloneStruct(token_manager_config)); |
| token_manager_factory_app_->services().ConnectToService( |
| token_manager_factory_.NewRequest()); |
| |
| session_user_provider_impl_ = std::make_unique<SessionUserProviderImpl>( |
| account_manager_.get(), token_manager_factory_.get(), |
| authentication_context_provider_binding_.NewBinding().Bind(), |
| /* on_initialize= */ |
| [this] { ShowSetupOrLogin(); }, |
| /* on_login= */ |
| [this](fuchsia::modular::auth::AccountPtr account, |
| fuchsia::auth::TokenManagerPtr ledger_token_manager, |
| fuchsia::auth::TokenManagerPtr agent_token_manager) { |
| OnLogin(std::move(account), std::move(ledger_token_manager), |
| std::move(agent_token_manager)); |
| }); |
| } |
| |
| void BasemgrImpl::GetUserProvider( |
| fidl::InterfaceRequest<fuchsia::modular::UserProvider> request) { |
| session_user_provider_impl_->Connect(std::move(request)); |
| } |
| |
| void BasemgrImpl::Shutdown() { |
| // Prevent the shutdown sequence from running twice. |
| if (state_ == State::SHUTTING_DOWN) { |
| return; |
| } |
| |
| state_ = State::SHUTTING_DOWN; |
| |
| FXL_DLOG(INFO) << "fuchsia::modular::BaseShellContext::Shutdown()"; |
| |
| if (config_.test()) { |
| FXL_LOG(INFO) |
| << std::endl |
| << "============================================================" |
| << std::endl |
| << "======================== [" << config_.test_name() << "] Done"; |
| } |
| |
| // |session_provider_| teardown is asynchronous because it holds the |
| // sessionmgr processes. |
| session_provider_.Teardown(kSessionProviderTimeout, [this] { |
| StopBaseShell()->Then([this] { |
| FXL_DLOG(INFO) << "- fuchsia::modular::BaseShell down"; |
| session_user_provider_impl_.reset(); |
| FXL_DLOG(INFO) << "- fuchsia::modular::UserProvider down"; |
| |
| StopTokenManagerFactoryApp()->Then([this] { |
| FXL_DLOG(INFO) << "- fuchsia::auth::TokenManagerFactory down"; |
| FXL_LOG(INFO) << "Clean shutdown"; |
| on_shutdown_(); |
| }); |
| }); |
| }); |
| } |
| |
| void BasemgrImpl::GetAuthenticationUIContext( |
| fidl::InterfaceRequest<fuchsia::auth::AuthenticationUIContext> request) { |
| // TODO(MI4-1107): Basemgr needs to implement AuthenticationUIContext |
| // itself, and proxy calls for StartOverlay & StopOverlay to BaseShell, |
| // starting it if it's not running yet. |
| base_shell_->GetAuthenticationUIContext(std::move(request)); |
| } |
| |
| void BasemgrImpl::OnLogin(fuchsia::modular::auth::AccountPtr account, |
| fuchsia::auth::TokenManagerPtr ledger_token_manager, |
| fuchsia::auth::TokenManagerPtr agent_token_manager) { |
| auto [view_token, view_holder_token] = scenic::ViewTokenPair::New(); |
| |
| auto did_start_session = session_provider_->StartSession( |
| std::move(view_token), std::move(account), |
| std::move(ledger_token_manager), std::move(agent_token_manager)); |
| if (!did_start_session) { |
| FXL_LOG(WARNING) << "Session was already started and the logged in user " |
| "could not join the session."; |
| return; |
| } |
| |
| // TODO(MI4-1117): Integration tests currently expect base shell to always be |
| // running. So, if we're running under a test, do not shut down the base shell |
| // after login. |
| if (!config_.base_shell().keep_alive_after_login()) { |
| FXL_DLOG(INFO) << "Stopping base shell due to login"; |
| StopBaseShell(); |
| } |
| |
| // Ownership of the Presenter should be moved to the session shell for tests |
| // that enable presenter, and production code. |
| if (!config_.test() || config_.enable_presenter()) { |
| presentation_container_ = std::make_unique<PresentationContainer>( |
| presenter_.get(), std::move(view_holder_token), |
| /* shell_config= */ GetActiveSessionShellConfig(), |
| /* on_swap_session_shell= */ [this] { |
| SelectNextSessionShell(/* callback= */ [] {}); |
| }); |
| } |
| } |
| |
| void BasemgrImpl::SelectNextSessionShell( |
| SelectNextSessionShellCallback callback) { |
| if (state_ == State::SHUTTING_DOWN) { |
| FXL_DLOG(INFO) |
| << "SelectNextSessionShell() not supported while shutting down"; |
| callback(); |
| return; |
| } |
| |
| if (config_.session_shell_map().empty()) { |
| FXL_DLOG(INFO) << "No session shells has been defined"; |
| callback(); |
| return; |
| } |
| auto shell_count = config_.session_shell_map().size(); |
| if (shell_count <= 1) { |
| FXL_DLOG(INFO) |
| << "Only one session shell has been defined so switch is disabled"; |
| callback(); |
| return; |
| } |
| |
| active_session_shell_configs_index_ = |
| (active_session_shell_configs_index_ + 1) % shell_count; |
| |
| UpdateSessionShellConfig(); |
| |
| session_provider_->SwapSessionShell(CloneStruct(session_shell_config_)) |
| ->Then([callback = std::move(callback)] { |
| FXL_LOG(INFO) << "Swapped session shell"; |
| callback(); |
| }); |
| } |
| |
| fuchsia::modular::session::SessionShellConfig |
| BasemgrImpl::GetActiveSessionShellConfig() { |
| return CloneStruct(config_.session_shell_map() |
| .at(active_session_shell_configs_index_) |
| .config()); |
| } |
| |
| void BasemgrImpl::UpdateSessionShellConfig() { |
| session_shell_config_ = CloneStruct(fidl::To<fuchsia::modular::AppConfig>( |
| config_.session_shell_map() |
| .at(active_session_shell_configs_index_) |
| .config() |
| .app_config())); |
| } |
| |
| void BasemgrImpl::ShowSetupOrLogin() { |
| auto show_setup_or_login = [this] { |
| // If there are no session shell settings specified, default to showing |
| // setup. |
| if (!config_.test() && active_session_shell_configs_index_ >= |
| config_.session_shell_map().size()) { |
| StartBaseShell(); |
| return; |
| } |
| |
| // Login as the first user, or show setup. This asssumes that: |
| // 1) Basemgr has exclusive access to AccountManager. |
| // 2) There are only 0 or 1 authenticated accounts ever. |
| account_manager_->GetAccountIds( |
| [this]( |
| std::vector<fuchsia::auth::account::LocalAccountId> account_ids) { |
| if (account_ids.empty()) { |
| StartBaseShell(); |
| } else { |
| fuchsia::modular::UserLoginParams2 params; |
| params.account_id = std::to_string(account_ids.at(0).id); |
| session_user_provider_impl_->Login2(std::move(params)); |
| } |
| }); |
| }; |
| |
| // TODO(MF-347): Handle scenario where device settings manager channel is |
| // dropped before error handler is set. |
| // TODO(MF-134): Modular should not be handling factory reset. |
| // If the device needs factory reset, remove all the users before proceeding |
| // with setup. |
| device_settings_manager_.set_error_handler( |
| [show_setup_or_login](zx_status_t status) { show_setup_or_login(); }); |
| device_settings_manager_->GetInteger( |
| kFactoryResetKey, |
| [this, show_setup_or_login](int factory_reset_value, |
| fuchsia::devicesettings::Status status) { |
| if (status == fuchsia::devicesettings::Status::ok && |
| factory_reset_value > 0) { |
| FXL_LOG(INFO) << "Factory reset initiated"; |
| // Unset the factory reset flag. |
| device_settings_manager_->SetInteger( |
| kFactoryResetKey, 0, [](bool result) { |
| if (!result) { |
| FXL_LOG(WARNING) << "Factory reset flag was not updated."; |
| } |
| }); |
| |
| session_user_provider_impl_->RemoveAllUsers([this] { |
| wlan_->ClearSavedNetworks([this] { StartBaseShell(); }); |
| }); |
| } else { |
| show_setup_or_login(); |
| } |
| }); |
| } |
| |
| void BasemgrImpl::RestartSession(RestartSessionCallback on_restart_complete) { |
| session_provider_->RestartSession(std::move(on_restart_complete)); |
| } |
| |
| void BasemgrImpl::LoginAsGuest() { |
| fuchsia::modular::UserLoginParams2 params; |
| session_user_provider_impl_->Login2(std::move(params)); |
| } |
| |
| void BasemgrImpl::LogoutUsers(fit::function<void()> callback) { |
| session_user_provider_impl_->RemoveAllUsers(std::move(callback)); |
| } |
| |
| void BasemgrImpl::GetPresentation( |
| fidl::InterfaceRequest<fuchsia::ui::policy::Presentation> request) { |
| presentation_container_->GetPresentation(std::move(request)); |
| } |
| |
| } // namespace modular |