blob: 71ddd2ea64c1866e9753ae235b5c46979e57c125 [file] [log] [blame]
// 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 <memory>
#include <fuchsia/auth/cpp/fidl.h>
#include <fuchsia/modular/auth/cpp/fidl.h>
#include <fuchsia/modular/cpp/fidl.h>
#include <fuchsia/sys/cpp/fidl.h>
#include <fuchsia/ui/app/cpp/fidl.h>
#include <fuchsia/ui/policy/cpp/fidl.h>
#include <lib/async/cpp/future.h>
#include <lib/component/cpp/startup_context.h>
#include <lib/fidl/cpp/interface_handle.h>
#include <lib/fidl/cpp/interface_request.h>
#include <lib/fidl/cpp/string.h>
#include <lib/fxl/logging.h>
#include <lib/fxl/macros.h>
#include "peridot/bin/basemgr/basemgr_settings.h"
#include "peridot/bin/basemgr/user_provider_impl.h"
#include "peridot/lib/common/async_holder.h"
#include "peridot/lib/common/names.h"
#include "peridot/lib/common/teardown.h"
#include "peridot/lib/fidl/app_client.h"
#include "peridot/lib/fidl/clone.h"
#include "peridot/lib/session_shell_settings/session_shell_settings.h"
#include "peridot/lib/util/filesystem.h"
namespace modular {
BasemgrImpl::BasemgrImpl(
const modular::BasemgrSettings& settings,
const std::vector<SessionShellSettings>& session_shell_settings,
fuchsia::sys::Launcher* const launcher,
fuchsia::ui::policy::PresenterPtr presenter,
fuchsia::devicesettings::DeviceSettingsManagerPtr device_settings_manager,
std::function<void()> on_shutdown)
: settings_(settings),
session_shell_settings_(session_shell_settings),
launcher_(launcher),
presenter_(std::move(presenter)),
device_settings_manager_(std::move(device_settings_manager)),
on_shutdown_(std::move(on_shutdown)),
user_provider_impl_("UserProviderImpl"),
base_shell_context_binding_(this),
authentication_context_provider_binding_(this) {
UpdateSessionShellConfig();
// TODO(SCN-595): Presentation is now discoverable, so we don't need
// kPresentationService anymore.
service_namespace_.AddService(presentation_state_.bindings.GetHandler(
presentation_state_.presentation.get()),
kPresentationService);
Start();
}
BasemgrImpl::~BasemgrImpl() = default;
void BasemgrImpl::Connect(
fidl::InterfaceRequest<fuchsia::modular::internal::BasemgrDebug> request) {
basemgr_bindings_.AddBinding(this, std::move(request));
}
void BasemgrImpl::InitializePresentation(zx::eventpair view_holder_token) {
if (settings_.test && !settings_.enable_presenter) {
return;
}
auto presentation_request =
presentation_state_.presentation.is_bound()
? presentation_state_.presentation.Unbind().NewRequest()
: presentation_state_.presentation.NewRequest();
presenter_->Present2(std::move(view_holder_token),
std::move(presentation_request));
AddGlobalKeyboardShortcuts(presentation_state_.presentation);
SetShadowTechnique(presentation_state_.shadow_technique);
// Set the presentation of the given view to the settings of the active
// session shell.
if (active_session_shell_settings_index_ >= session_shell_settings_.size()) {
FXL_LOG(ERROR) << "Active session shell index is "
<< active_session_shell_settings_index_ << ", but only "
<< session_shell_settings_.size()
<< " session shell settings exist.";
return;
}
auto active_session_shell_settings =
session_shell_settings_[active_session_shell_settings_index_];
if (active_session_shell_settings.display_usage !=
fuchsia::ui::policy::DisplayUsage::kUnknown) {
FXL_DLOG(INFO) << "Setting display usage: "
<< fidl::ToUnderlying(
active_session_shell_settings.display_usage);
presentation_state_.presentation->SetDisplayUsage(
active_session_shell_settings.display_usage);
}
if (!std::isnan(active_session_shell_settings.screen_width) &&
!std::isnan(active_session_shell_settings.screen_height)) {
FXL_DLOG(INFO) << "Setting display size: "
<< active_session_shell_settings.screen_width << " x "
<< active_session_shell_settings.screen_height;
presentation_state_.presentation->SetDisplaySizeInMm(
active_session_shell_settings.screen_width,
active_session_shell_settings.screen_height);
}
}
void BasemgrImpl::StartBaseShell() {
if (base_shell_running_) {
FXL_DLOG(INFO) << "StartBaseShell() called when already running";
return;
}
base_shell_app_ = std::make_unique<AppClient<fuchsia::modular::Lifecycle>>(
launcher_, CloneStruct(settings_.base_shell));
base_shell_app_->services().ConnectToService(base_shell_.NewRequest());
fuchsia::ui::app::ViewProviderPtr base_shell_view_provider;
base_shell_app_->services().ConnectToService(
base_shell_view_provider.NewRequest());
zx::eventpair root_view_token, root_view_holder_token;
if (zx::eventpair::create(0u, &root_view_token, &root_view_holder_token) !=
ZX_OK)
FXL_NOTREACHED() << "Failed to create view tokens";
// We still need to pass a token for root view to base shell since
// dev_base_shell (which mimics flutter behavior) blocks until it receives
// the root view token.
base_shell_view_provider->CreateView(std::move(root_view_token), nullptr,
nullptr);
InitializePresentation(std::move(root_view_holder_token));
// Populate parameters and initialize the base shell.
fuchsia::modular::BaseShellParams params;
params.presentation = std::move(presentation_state_.presentation);
base_shell_->Initialize(base_shell_context_binding_.NewBinding(),
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 (settings_.test) {
// 0. Print test banner.
FXL_LOG(INFO)
<< std::endl
<< std::endl
<< "======================== Starting Test [" << settings_.test_name
<< "]" << std::endl
<< "============================================================"
<< std::endl;
}
// Wait for persistent data to come up.
if (!settings_.no_minfs) {
WaitForMinfs();
}
// Start OAuth Token Manager App.
token_manager_factory_app_.release();
fuchsia::modular::AppConfig token_manager_config;
token_manager_config.url = settings_.account_provider.url;
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());
user_provider_impl_.reset(new UserProviderImpl(
launcher_, settings_.sessionmgr, session_shell_config_,
settings_.story_shell, token_manager_factory_.get(),
authentication_context_provider_binding_.NewBinding().Bind(), this));
ShowSetupOrLogin();
ReportEvent(ModularEvent::BOOTED_TO_BASEMGR);
}
void BasemgrImpl::GetUserProvider(
fidl::InterfaceRequest<fuchsia::modular::UserProvider> request) {
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 (settings_.test) {
FXL_LOG(INFO)
<< std::endl
<< "============================================================"
<< std::endl
<< "======================== [" << settings_.test_name << "] Done";
}
// TODO(mesch): Some of these could be done in parallel too.
// fuchsia::modular::UserProvider must go first, but the order after user
// provider is for now rather arbitrary. We terminate base shell last so
// that in tests testing::Teardown() is invoked at the latest possible time.
// Right now it just demonstrates that AppTerminate() works as we like it
// to.
user_provider_impl_.Teardown(kUserProviderTimeout, [this] {
FXL_DLOG(INFO) << "- fuchsia::modular::UserProvider down";
StopTokenManagerFactoryApp()->Then([this] {
FXL_DLOG(INFO) << "- fuchsia::auth::TokenManagerFactory down";
StopBaseShell()->Then([this] {
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.
FXL_CHECK(base_shell_);
base_shell_->GetAuthenticationUIContext(std::move(request));
}
void BasemgrImpl::DidLogin() {
// Continues if `enable_presenter` is set to true during testing, as
// ownership of the Presenter should still be moved to the session shell.
if (settings_.test && !settings_.enable_presenter) {
// 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.
return;
}
// TODO(MI4-1117): See above. The base shell shouldn't be shut down.
if (!settings_.test) {
FXL_DLOG(INFO) << "Stopping base shell due to login";
StopBaseShell();
}
InitializePresentation(std::move(session_shell_view_holder_token_));
}
void BasemgrImpl::DidLogout() {
if (settings_.test) {
// 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 base shell due to logout";
StartBaseShell();
}
zx::eventpair BasemgrImpl::GetSessionShellViewToken(
zx::eventpair /*default_view_token*/) {
if (session_shell_view_holder_token_.is_valid()) {
session_shell_view_holder_token_.reset();
}
zx::eventpair session_shell_view_token;
if (zx::eventpair::create(0u, &session_shell_view_token,
&session_shell_view_holder_token_) != ZX_OK)
FXL_NOTREACHED() << "Failed to create view tokens";
return session_shell_view_token;
}
fidl::InterfaceHandle<fuchsia::sys::ServiceProvider>
BasemgrImpl::GetSessionShellServiceProvider(
fidl::InterfaceHandle<fuchsia::sys::ServiceProvider> /*default_services*/) {
fidl::InterfaceHandle<fuchsia::sys::ServiceProvider> handle;
service_namespace_.AddBinding(handle.NewRequest());
return handle;
}
void BasemgrImpl::OnEvent(fuchsia::ui::input::KeyboardEvent event) {
switch (event.code_point) {
case ' ': {
SwapSessionShell();
break;
}
case 's': {
SetNextShadowTechnique();
break;
}
case 'l':
ToggleClipping();
break;
default:
FXL_DLOG(INFO) << "Unknown keyboard event: codepoint=" << event.code_point
<< ", modifiers=" << event.modifiers;
break;
}
}
void BasemgrImpl::AddGlobalKeyboardShortcuts(
fuchsia::ui::policy::PresentationPtr& presentation) {
presentation->CaptureKeyboardEventHACK(
{
.code_point = ' ', // spacebar
.modifiers = fuchsia::ui::input::kModifierLeftControl,
},
keyboard_capture_listener_bindings_.AddBinding(this));
presentation->CaptureKeyboardEventHACK(
{
.code_point = 's',
.modifiers = fuchsia::ui::input::kModifierLeftControl,
},
keyboard_capture_listener_bindings_.AddBinding(this));
presentation->CaptureKeyboardEventHACK(
{
.code_point = 'l',
.modifiers = fuchsia::ui::input::kModifierRightAlt,
},
keyboard_capture_listener_bindings_.AddBinding(this));
}
void BasemgrImpl::SwapSessionShell() {
if (session_shell_settings_.empty()) {
FXL_DLOG(INFO) << "No session shells has been defined";
return;
}
auto shell_count = session_shell_settings_.size();
if (shell_count <= 1) {
FXL_DLOG(INFO)
<< "Only one session shell has been defined so switch is disabled";
return;
}
active_session_shell_settings_index_ =
(active_session_shell_settings_index_ + 1) % shell_count;
UpdateSessionShellConfig();
user_provider_impl_->SwapSessionShell(CloneStruct(session_shell_config_))
->Then([] { FXL_DLOG(INFO) << "Swapped session shell"; });
}
void BasemgrImpl::SetNextShadowTechnique() {
using ShadowTechnique = fuchsia::ui::gfx::ShadowTechnique;
auto next_shadow_technique =
[](ShadowTechnique shadow_technique) -> ShadowTechnique {
switch (shadow_technique) {
case ShadowTechnique::UNSHADOWED:
return ShadowTechnique::SCREEN_SPACE;
case ShadowTechnique::SCREEN_SPACE:
return ShadowTechnique::SHADOW_MAP;
default:
FXL_LOG(ERROR) << "Unknown shadow technique: "
<< fidl::ToUnderlying(shadow_technique);
// Fallthrough
case ShadowTechnique::SHADOW_MAP:
case ShadowTechnique::MOMENT_SHADOW_MAP:
return ShadowTechnique::UNSHADOWED;
}
};
SetShadowTechnique(
next_shadow_technique(presentation_state_.shadow_technique));
}
void BasemgrImpl::SetShadowTechnique(
fuchsia::ui::gfx::ShadowTechnique shadow_technique) {
if (!presentation_state_.presentation)
return;
presentation_state_.shadow_technique = shadow_technique;
FXL_LOG(INFO) << "Setting shadow technique to "
<< fidl::ToUnderlying(presentation_state_.shadow_technique);
fuchsia::ui::gfx::RendererParam param;
param.set_shadow_technique(presentation_state_.shadow_technique);
auto renderer_params =
fidl::VectorPtr<fuchsia::ui::gfx::RendererParam>::New(0);
renderer_params.push_back(std::move(param));
presentation_state_.presentation->SetRendererParams(
std::move(renderer_params));
}
void BasemgrImpl::ToggleClipping() {
if (!presentation_state_.presentation)
return;
FXL_DLOG(INFO) << "Toggling clipping";
presentation_state_.clipping_enabled = !presentation_state_.clipping_enabled;
presentation_state_.presentation->EnableClipping(
presentation_state_.clipping_enabled);
}
void BasemgrImpl::UpdateSessionShellConfig() {
// The session shell settings overrides the session_shell flag passed via
// command line, except in integration tests. TODO(MF-113): Consolidate
// the session shell settings.
fuchsia::modular::AppConfig session_shell_config;
if (settings_.test || session_shell_settings_.empty()) {
session_shell_config = CloneStruct(settings_.session_shell);
} else {
const auto& settings =
session_shell_settings_[active_session_shell_settings_index_];
session_shell_config.url = settings.name;
}
session_shell_config_ = std::move(session_shell_config);
}
void BasemgrImpl::ShowSetupOrLogin() {
auto show_setup_or_login = [this] {
// If the session shell settings specifies it, auto-login as the first
// authenticated user. Otherwise, start the base shell to launch setup.
if (active_session_shell_settings_index_ < session_shell_settings_.size() &&
session_shell_settings_[active_session_shell_settings_index_]
.auto_login) {
user_provider_impl_->PreviousUsers(
[this](fidl::VectorPtr<fuchsia::modular::auth::Account> accounts) {
if (accounts->empty()) {
StartBaseShell();
} else {
fuchsia::modular::UserLoginParams params;
params.account_id = accounts->at(0).id;
user_provider_impl_->Login(std::move(params));
}
});
} else {
StartBaseShell();
}
};
// TODO(MF-134): Improve the factory reset logic by deleting more than just
// the user data.
// 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) {
// Unset the factory reset flag.
device_settings_manager_->SetInteger(
kFactoryResetKey, 0, [](bool result) {
if (!result) {
FXL_LOG(WARNING) << "Factory reset flag was not updated.";
}
});
user_provider_impl_->PreviousUsers(
[this](
fidl::VectorPtr<fuchsia::modular::auth::Account> accounts) {
std::vector<FuturePtr<>> did_remove_users;
did_remove_users.reserve(accounts->size());
for (const auto& account : *accounts) {
auto did_remove_user = Future<>::Create(
"BasemgrImpl.ShowSetupOrLogin.did_remove_user");
user_provider_impl_->RemoveUser(
account.id,
[did_remove_user](fidl::StringPtr error_code) {
if (error_code) {
FXL_LOG(WARNING) << "Account was not removed during "
"factory reset. Error code: "
<< error_code;
}
did_remove_user->Complete();
});
did_remove_users.emplace_back(did_remove_user);
}
Wait("BasemgrImpl.ShowSetupOrLogin.Wait", did_remove_users)
->Then([this] { StartBaseShell(); });
});
} else {
show_setup_or_login();
}
});
}
void BasemgrImpl::RestartSession() { user_provider_impl_->RestartSession(); }
} // namespace modular