[session_shell] sessionmgr uses SessionShell
Use the SessionShell service of the session shell to forward
views of stories started by RequestStart() calling AttachView().
If a story was started by RequestStart(), and it's stopped,
then the session shell is also called with DetachView().
Use the RequestStart() method in one test case in session_shell_test.
It is not yet used anywhere else. After this is submitted, clients
can be migrated from using Start() to RequestStart(). Once Start()
is no longer used, it can be removed.
MF-121 #comment sessionmgr uses SessionShell
TESTED=Added test case to session_shell integration test.
Change-Id: I03704d87b64708c61ee2959e7b1f8ee6991274c3
diff --git a/bin/sessionmgr/sessionmgr_impl.cc b/bin/sessionmgr/sessionmgr_impl.cc
index 12e2d08..305eed8 100644
--- a/bin/sessionmgr/sessionmgr_impl.cc
+++ b/bin/sessionmgr/sessionmgr_impl.cc
@@ -680,7 +680,7 @@
}
void SessionmgrImpl::RunSessionShell(
- fuchsia::modular::AppConfig session_shell) {
+ 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>(
@@ -715,7 +715,7 @@
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),
+ user_environment_->GetLauncher(), std::move(session_shell_config),
/* data_origin = */ "", std::move(service_list));
session_shell_app_->SetAppErrorHandler([this] {
@@ -729,6 +729,10 @@
session_shell_app_->services().ConnectToService(view_provider.NewRequest());
view_provider->CreateView(view_owner.NewRequest(), nullptr);
session_shell_view_host_->ConnectView(std::move(view_owner));
+
+ fuchsia::modular::SessionShellPtr session_shell;
+ session_shell_app_->services().ConnectToService(session_shell.NewRequest());
+ story_provider_impl_->SetSessionShell(std::move(session_shell));
}
void SessionmgrImpl::TerminateSessionShell(const std::function<void()>& done) {
diff --git a/bin/sessionmgr/sessionmgr_impl.h b/bin/sessionmgr/sessionmgr_impl.h
index c2a67f8..2b9d5b8 100644
--- a/bin/sessionmgr/sessionmgr_impl.h
+++ b/bin/sessionmgr/sessionmgr_impl.h
@@ -209,6 +209,7 @@
fidl::BindingSet<fuchsia::modular::internal::Sessionmgr> bindings_;
component::ServiceProviderImpl session_shell_services_;
+
fidl::BindingSet<fuchsia::modular::SessionShellContext>
session_shell_context_bindings_;
diff --git a/bin/sessionmgr/story_runner/story_controller_impl.cc b/bin/sessionmgr/story_runner/story_controller_impl.cc
index c3fcd5f..3b16646 100644
--- a/bin/sessionmgr/story_runner/story_controller_impl.cc
+++ b/bin/sessionmgr/story_runner/story_controller_impl.cc
@@ -504,6 +504,14 @@
}
story_controller_impl_->SetState(fuchsia::modular::StoryState::STOPPING);
+ story_controller_impl_->DetachView([this, weak_this = GetWeakPtr()] {
+ if (weak_this) {
+ StopStory();
+ }
+ });
+ }
+
+ void StopStory() {
std::vector<FuturePtr<>> did_teardowns;
did_teardowns.reserve(story_controller_impl_->running_mod_infos_.size());
@@ -957,12 +965,13 @@
void Run() override {
FlowToken flow{this};
- // If the story is running, we do nothing and close the view owner request.
+ // If the story is running, we do nothing.
if (story_controller_impl_->IsRunning()) {
FXL_LOG(INFO)
<< "StoryControllerImpl::StartCall() while already running: ignored.";
return;
}
+
story_controller_impl_->StartStoryShell(std::move(request_));
// Start all modules that were not themselves explicitly started by another
@@ -1421,6 +1430,11 @@
operation_queue_.Add(new StartCall(this, story_storage_, std::move(request)));
}
+void StoryControllerImpl::RequestStart() {
+ operation_queue_.Add(new StartCall(this, story_storage_,
+ nullptr /* ViewOwner request */));
+}
+
void StoryControllerImpl::Stop(StopCallback done) {
operation_queue_.Add(new StopCall(this, done));
}
@@ -1544,6 +1558,14 @@
void StoryControllerImpl::StartStoryShell(
fidl::InterfaceRequest<fuchsia::ui::viewsv1token::ViewOwner> request) {
+ if (!request) {
+ // The start call originated in RequestStart() rather than Start().
+ fuchsia::ui::viewsv1token::ViewOwnerPtr view_owner;
+ request = view_owner.NewRequest();
+ story_provider_impl_->AttachView(story_id_, std::move(view_owner));
+ needs_detach_view_ = true;
+ }
+
story_shell_app_ =
story_provider_impl_->StartStoryShell(story_id_, std::move(request));
story_shell_app_->services().ConnectToService(story_shell_.NewRequest());
@@ -1554,6 +1576,15 @@
fit::bind_member(this, &StoryControllerImpl::OnSurfaceFocused);
}
+void StoryControllerImpl::DetachView(std::function<void()> done) {
+ if (needs_detach_view_) {
+ story_provider_impl_->DetachView(story_id_, std::move(done));
+ needs_detach_view_ = false;
+ } else {
+ done();
+ }
+}
+
void StoryControllerImpl::SetState(
const fuchsia::modular::StoryState new_state) {
if (new_state == state_) {
diff --git a/bin/sessionmgr/story_runner/story_controller_impl.h b/bin/sessionmgr/story_runner/story_controller_impl.h
index 1dd366f..6d9ca7c 100644
--- a/bin/sessionmgr/story_runner/story_controller_impl.h
+++ b/bin/sessionmgr/story_runner/story_controller_impl.h
@@ -174,6 +174,7 @@
void GetInfo(GetInfoCallback callback) override;
void Start(fidl::InterfaceRequest<fuchsia::ui::viewsv1token::ViewOwner>
request) override;
+ void RequestStart() override;
void TakeAndLoadSnapshot(
fidl::InterfaceRequest<fuchsia::ui::viewsv1token::ViewOwner> request,
TakeAndLoadSnapshotCallback done) override;
@@ -193,9 +194,10 @@
void GetLink(fuchsia::modular::LinkPath link_path,
fidl::InterfaceRequest<fuchsia::modular::Link> request) override;
- // Phases of Start() broken out into separate methods.
+ // Communicates with SessionShell.
void StartStoryShell(
fidl::InterfaceRequest<fuchsia::ui::viewsv1token::ViewOwner> request);
+ void DetachView(std::function<void()> done);
// Called whenever |story_storage_| sees an updated ModuleData from another
// device.
@@ -226,6 +228,11 @@
// persist it to.
const fidl::StringPtr story_id_;
+ // True once AttachView() was called. Temporarily needed during transition
+ // from Start() to RequestStart(), will be removed once Start() is removed.
+ // Cf. MF-121.
+ bool needs_detach_view_{};
+
// This is the canonical source for state. This state is per device and only
// kept in memory.
fuchsia::modular::StoryState state_{fuchsia::modular::StoryState::STOPPED};
diff --git a/bin/sessionmgr/story_runner/story_provider_impl.cc b/bin/sessionmgr/story_runner/story_provider_impl.cc
index bf0723b..92fc38a 100644
--- a/bin/sessionmgr/story_runner/story_provider_impl.cc
+++ b/bin/sessionmgr/story_runner/story_provider_impl.cc
@@ -333,6 +333,22 @@
operation_queue_.Add(new StopAllStoriesCall(this, callback));
}
+void StoryProviderImpl::SetSessionShell(
+ fuchsia::modular::SessionShellPtr session_shell) {
+ // Not on operation queue, because it's called only after all stories have
+ // been stopped or none are running yet, i.e. when no Operations that would
+ // call this interface are scheduled. If there is an operation pending here,
+ // then it would pertain to a story running in the new session shell started
+ // by puppet master or an agent, so we must assign this now.
+ //
+ // TODO(mesch): It may well be that we need to revisit this when we support
+ // starting stories, or swapping session shells, through puppet master, i.e.
+ // from outside the session shell.
+ //
+ // TODO(mesch): Add a WARNING log if the operation is not empty.
+ session_shell_ = std::move(session_shell);
+}
+
void StoryProviderImpl::Teardown(const std::function<void()>& callback) {
// Closing all binding to this instance ensures that no new messages come
// in, though previous messages need to be processed. The stopping of
@@ -448,6 +464,22 @@
focus_provider_->Request(story_id);
}
+void StoryProviderImpl::AttachView(
+ fidl::StringPtr story_id, fuchsia::ui::viewsv1token::ViewOwnerPtr view_owner) {
+ FXL_CHECK(session_shell_);
+ fuchsia::modular::ViewIdentifier view_id;
+ view_id.story_id = std::move(story_id);
+ session_shell_->AttachView(std::move(view_id), std::move(view_owner));
+}
+
+void StoryProviderImpl::DetachView(fidl::StringPtr story_id,
+ std::function<void()> done) {
+ FXL_CHECK(session_shell_);
+ fuchsia::modular::ViewIdentifier view_id;
+ view_id.story_id = std::move(story_id);
+ session_shell_->DetachView(std::move(view_id), std::move(done));
+}
+
void StoryProviderImpl::NotifyStoryStateChange(
fidl::StringPtr story_id, const fuchsia::modular::StoryState story_state,
const fuchsia::modular::StoryVisibilityState story_visibility_state) {
diff --git a/bin/sessionmgr/story_runner/story_provider_impl.h b/bin/sessionmgr/story_runner/story_provider_impl.h
index 8aac267..f6cdb10 100644
--- a/bin/sessionmgr/story_runner/story_provider_impl.h
+++ b/bin/sessionmgr/story_runner/story_provider_impl.h
@@ -64,8 +64,13 @@
void Connect(fidl::InterfaceRequest<fuchsia::modular::StoryProvider> request);
+ // Used when the session shell is swapped.
void StopAllStories(const std::function<void()>& callback);
+ // The session shell to send story views to. It is not a constructor argument
+ // because it is updated when the session shell is swapped.
+ void SetSessionShell(fuchsia::modular::SessionShellPtr session_shell);
+
// Stops serving the fuchsia::modular::StoryProvider interface and stops all
// stories.
void Teardown(const std::function<void()>& callback);
@@ -120,6 +125,16 @@
// fuchsia::modular::FocusProvider
void RequestStoryFocus(fidl::StringPtr story_id);
+ // Called by StoryControllerImpl. Sends, using AttachView(), the view of the
+ // story identified by |story_id| to the current session shell.
+ void AttachView(
+ fidl::StringPtr story_id, fuchsia::ui::viewsv1token::ViewOwnerPtr view_owner);
+
+ // Called by StoryControllerImpl. Notifies, using DetachView(), the current
+ // session shell that the view of the story identified by |story_id| is about
+ // to close.
+ void DetachView(fidl::StringPtr story_id, std::function<void()> done);
+
// Called by StoryControllerImpl.
void NotifyStoryStateChange(
fidl::StringPtr story_id, fuchsia::modular::StoryState story_state,
@@ -222,6 +237,10 @@
SessionStorage* session_storage_; // Not owned.
+ // The service from the session shell run by the sessionmgr. Owned here
+ // because only used from here.
+ fuchsia::modular::SessionShellPtr session_shell_;
+
// Unique ID generated for this user/device combination.
const std::string device_id_;
diff --git a/lib/testing/story_controller_mock.h b/lib/testing/story_controller_mock.h
index 3246eef..b8e4ac8 100644
--- a/lib/testing/story_controller_mock.h
+++ b/lib/testing/story_controller_mock.h
@@ -40,6 +40,11 @@
}
// |fuchsia::modular::StoryController|
+ void RequestStart() override {
+ FXL_NOTIMPLEMENTED();
+ }
+
+ // |fuchsia::modular::StoryController|
void Stop(StopCallback done) override { FXL_NOTIMPLEMENTED(); }
// |fuchsia::modular::StoryController|
diff --git a/public/fidl/fuchsia.modular/story/story_controller.fidl b/public/fidl/fuchsia.modular/story/story_controller.fidl
index 9492818..0be4264 100644
--- a/public/fidl/fuchsia.modular/story/story_controller.fidl
+++ b/public/fidl/fuchsia.modular/story/story_controller.fidl
@@ -15,6 +15,8 @@
// Gets information associated with the story.
1: GetInfo() -> (StoryInfo info, StoryState state);
+ // DEPRECATED in favor of RequestStart().
+ //
// Runs the story controlled by this |StoryController| instance if not yet
// running or requested to start, else does nothing. |view_owner| is an
// interface request for the view of the story shell of this story. If the
@@ -23,6 +25,11 @@
// until the Stop() requests complete.
3: Start(request<fuchsia.ui.viewsv1token.ViewOwner> view_owner);
+ // Requests to runsthe story controlled by this |StoryController| instance.
+ // When the story starts, if not yet running, the view of the newly started
+ // story shell will be passed in a call to SessionShell.AttachView().
+ 14: RequestStart();
+
// Requests to stop the story controlled by this |StoryController|. If Start()
// requests are pending when this request is issued, the request is queued
// until the Start() requests complete. Before stopping the story, a snapshot
diff --git a/tests/session_shell/README.md b/tests/session_shell/README.md
index a75495e..e3c30d3 100644
--- a/tests/session_shell/README.md
+++ b/tests/session_shell/README.md
@@ -6,10 +6,15 @@
* See existing stories as long as they are not deleted.
-* Add modules to existign stories.
+* Add modules to existing stories.
* Be notified when story state changes.
+* Be notified through SessionShell.AttachView() of a new story view when story
+ start is requested by RequestStart() rather than Start().
+
+* Be notified through SessionShell.DetachView() of a story going away.
+
The test code is invoked as a session shell from basemgr and executes a
predefined sequence of steps, rather than to expose a UI to be driven by user
interaction, as a session shell normally would. I.e. the test is implemented as a
diff --git a/tests/session_shell/session_shell_test_session_shell.cc b/tests/session_shell/session_shell_test_session_shell.cc
index 757e267..701c6d0 100644
--- a/tests/session_shell/session_shell_test_session_shell.cc
+++ b/tests/session_shell/session_shell_test_session_shell.cc
@@ -13,6 +13,7 @@
#include <lib/async/default.h>
#include <lib/component/cpp/startup_context.h>
#include <lib/fidl/cpp/binding.h>
+#include <lib/fidl/cpp/binding_set.h>
#include <lib/fsl/vmo/sized_vmo.h>
#include <lib/fsl/vmo/strings.h>
#include <lib/fxl/command_line.h>
@@ -28,9 +29,13 @@
#include "peridot/tests/session_shell/defs.h"
using modular::testing::Await;
+using modular::testing::Fail;
using modular::testing::Signal;
using modular::testing::TestPoint;
+using fuchsia::modular::SessionShell;
+using fuchsia::modular::ViewIdentifier;
+
namespace {
// A simple story provider watcher implementation. Just logs observed state
@@ -135,9 +140,54 @@
// Cf. README.md for what this test does in general and how. The test cases are
// described in detail in comments below.
+class SessionShellImpl : fuchsia::modular::SessionShell {
+ public:
+ SessionShellImpl() = default;
+ ~SessionShellImpl() = default;
+
+ using ViewId = fuchsia::modular::ViewIdentifier;
+
+ fidl::InterfaceRequestHandler<fuchsia::modular::SessionShell> GetHandler() {
+ return bindings_.GetHandler(this);
+ }
+
+ void set_on_attach_view(std::function<void(ViewId view_id)> callback) {
+ on_attach_view_ = callback;
+ }
+
+ void set_on_detach_view(std::function<void(ViewId view_id)> callback) {
+ on_detach_view_ = callback;
+ }
+
+ private:
+ // |SessionShell|
+ void AttachView(fuchsia::modular::ViewIdentifier view_id,
+ fidl::InterfaceHandle<fuchsia::ui::viewsv1token::ViewOwner>
+ view_owner) override {
+ on_attach_view_(std::move(view_id));
+ }
+
+ // |SessionShell|
+ void DetachView(fuchsia::modular::ViewIdentifier view_id,
+ std::function<void()> done) override {
+ on_detach_view_(std::move(view_id));
+ done();
+ }
+
+ fidl::BindingSet<fuchsia::modular::SessionShell> bindings_;
+ std::function<void(ViewId view_id)> on_attach_view_{[](ViewId) {}};
+ std::function<void(ViewId view_id)> on_detach_view_{[](ViewId) {}};
+
+ FXL_DISALLOW_COPY_AND_ASSIGN(SessionShellImpl);
+};
+
+
+// Cf. README.md for what this test does and how.
class TestApp : public modular::testing::ComponentBase<void>
{
public:
+ using ViewId = fuchsia::modular::ViewIdentifier;
+
explicit TestApp(component::StartupContext* const startup_context)
: ComponentBase(startup_context) {
TestInit(__FILE__);
@@ -149,6 +199,20 @@
session_shell_context_->GetStoryProvider(story_provider_.NewRequest());
story_provider_state_watcher_.Watch(&story_provider_);
+ startup_context->outgoing().AddPublicService(
+ session_shell_impl_.GetHandler());
+
+ // Until we use RequestStart() for the first time, there must be no calls on
+ // the SessionShell service.
+ session_shell_impl_.set_on_attach_view(
+ [](ViewId view_id) {
+ Fail("AttachView() called without RequestStart().");
+ });
+ session_shell_impl_.set_on_detach_view(
+ [](ViewId view_id) {
+ Fail("DetachView() called without RequestStart().");
+ });
+
TestStoryProvider_GetStoryInfo_Null();
}
@@ -502,11 +566,106 @@
story3_info_after_delete_.Pass();
}
+ TestStory4();
+ }
+
+ // Test Case Story4:
+ //
+ // Create a story and start it with RequestStart() rather than Start().
+ //
+ // Verify the view is received through SessionShell.AttachView().
+ //
+ // Verify that, when the story is stopped, a request for
+ // SessionShell.DetachView() is received.
+
+ TestPoint story4_create_{"Story4 Create"};
+
+ void TestStory4() {
+ puppet_master_->ControlStory("story4", story_puppet_master_.NewRequest());
+
+ fuchsia::modular::AddMod add_mod;
+ add_mod.mod_name.push_back("mod1");
+ add_mod.intent.handler = kCommonNullModule;
+
+ fuchsia::modular::StoryCommand command;
+ command.set_add_mod(std::move(add_mod));
+
+ fidl::VectorPtr<fuchsia::modular::StoryCommand> commands;
+ commands.push_back(std::move(command));
+
+ story_puppet_master_->Enqueue(std::move(commands));
+ story_puppet_master_->Execute(
+ [this](fuchsia::modular::ExecuteResult result) {
+ story4_create_.Pass();
+ TestStory4_Run();
+ });
+ }
+
+ TestPoint story4_state_before_run_{"Story4 State before Run"};
+ TestPoint story4_state_after_run_{"Story4 State after Run"};
+
+ TestPoint story4_attach_view_{"Story4 attach View"};
+ TestPoint story4_detach_view_{"Story4 detach View"};
+
+ void TestStory4_Run() {
+ story_provider_->GetController("story4", story_controller_.NewRequest());
+
+ story_controller_->GetInfo([this](fuchsia::modular::StoryInfo info,
+ fuchsia::modular::StoryState state) {
+ story_info_ = std::move(info);
+ if (state == fuchsia::modular::StoryState::STOPPED) {
+ story4_state_before_run_.Pass();
+ }
+ });
+
+ // Start and show the new story using RequestStart().
+ story_controller_->RequestStart();
+
+ session_shell_impl_.set_on_attach_view(
+ [this](ViewId) {
+ story4_attach_view_.Pass();
+ });
+
+ story_controller_->GetInfo([this](fuchsia::modular::StoryInfo info,
+ fuchsia::modular::StoryState state) {
+ if (state == fuchsia::modular::StoryState::RUNNING) {
+ story4_state_after_run_.Pass();
+ TestStory4_DeleteStory();
+ }
+ });
+ }
+
+ TestPoint story4_delete_{"Story4 Delete"};
+
+ void TestStory4_DeleteStory() {
+ puppet_master_->DeleteStory(story_info_.id,
+ [this] { story4_delete_.Pass(); });
+
+ session_shell_impl_.set_on_detach_view(
+ [this](ViewId) {
+ story4_detach_view_.Pass();
+ });
+
+ story_provider_->GetStoryInfo(
+ story_info_.id, [this](fuchsia::modular::StoryInfoPtr info) {
+ TestStory4_InfoAfterDeleteIsNull(std::move(info));
+ });
+
+ }
+
+ TestPoint story4_info_after_delete_{"Story4 Info after Delete is null"};
+
+ void TestStory4_InfoAfterDeleteIsNull(fuchsia::modular::StoryInfoPtr info) {
+ if (!info) {
+ story4_info_after_delete_.Pass();
+ }
+
Signal(modular::testing::kTestShutdown);
}
void TeardownStoryController() { story_controller_.Unbind(); }
+ SessionShellImpl session_shell_impl_;
StoryProviderStateWatcherImpl story_provider_state_watcher_;
fuchsia::modular::SessionShellContextPtr session_shell_context_;